从 MIC 录制并流式传输到 TCP 服务器; MediaRecorder:启动失败:-38

Posted

技术标签:

【中文标题】从 MIC 录制并流式传输到 TCP 服务器; MediaRecorder:启动失败:-38【英文标题】:Record from MIC and stream to TCP server; MediaRecorder: start failed: -38 【发布时间】:2018-04-18 11:18:12 【问题描述】:

我正在尝试通过 TCP 将音频从 android 设备的麦克风流式传输到服务器。问题是我在控制台上收到错误。已建立 TCP 连接,但未发送音频数据。

我意识到这可能是因为编解码器选择不当,因为有些需要能够在流中搜索,这是不可能的。我实际上可以使用任何有效的编解码器,但我读到 MediaRecorder.OutputFormat.RAW_AMR 和 MediaRecorder.AudioEncoder.AMR_NB 是流媒体的最佳组合。如果有更好的选择,请提出另一种选择。

这是我在日志中看到的:

11-06 11:09:27.276 22983-22983/se.jensolsson.test.test D/ViewRootImpl@5ed8717[MainActivity]: ViewPostImeInputStage processPointer 0
11-06 11:09:27.355 22983-22983/se.jensolsson.test.test D/ViewRootImpl@5ed8717[MainActivity]: ViewPostImeInputStage processPointer 1
11-06 11:09:27.387 22983-25466/se.jensolsson.test.test I/MediaRecorderJNI: setup
11-06 11:09:27.394 22983-25466/se.jensolsson.test.test I/MediaRecorderJNI: setAudiosource(1)
11-06 11:09:27.397 22983-25466/se.jensolsson.test.test I/MediaRecorderJNI: setAudioEncoder(1)
11-06 11:09:27.400 22983-25466/se.jensolsson.test.test I/MediaRecorderJNI: setOutputFile
11-06 11:09:27.400 22983-25466/se.jensolsson.test.test I/MediaRecorderJNI: prepare
11-06 11:09:27.407 22983-25466/se.jensolsson.test.test I/MediaRecorderJNI: start
11-06 11:09:27.408 22983-25466/se.jensolsson.test.test E/MediaRecorder: start failed: -38
11-06 11:09:27.408 22983-25466/se.jensolsson.test.test W/System.err: java.lang.IllegalStateException
11-06 11:09:27.411 22983-25466/se.jensolsson.test.test W/System.err:     at android.media.MediaRecorder._start(Native Method)
11-06 11:09:27.411 22983-25466/se.jensolsson.test.test W/System.err:     at android.media.MediaRecorder.start(MediaRecorder.java:1170)
11-06 11:09:27.411 22983-25466/se.jensolsson.test.test W/System.err:     at se.jensolsson.test.test.MainActivity$1$1.run(MainActivity.java:78)
11-06 11:09:27.411 22983-25466/se.jensolsson.test.test W/System.err:     at java.lang.Thread.run(Thread.java:762)

这里是AndroidManifest.xml的相关部分

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

这里是源代码:

public class MainActivity extends AppCompatActivity 

    private MediaRecorder mediaRecorder;
    private boolean permissionToRecordAccepted;
    private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;

    private ParcelFileDescriptor pfd;

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode)
            case REQUEST_RECORD_AUDIO_PERMISSION:
                permissionToRecordAccepted  = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                if (!permissionToRecordAccepted ) finish();
                break;
        

    

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityCompat.requestPermissions(this, new String[]  Manifest.permission.RECORD_AUDIO , REQUEST_RECORD_AUDIO_PERMISSION);

        Button buttonStartRecording = (Button)findViewById(R.id.button_start_recording);
        buttonStartRecording.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                new Thread(new Runnable() 

                    @Override
                    public void run() 
                        try 
                            Socket s = new Socket("10.0.83.8", 8888);
                            ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(s);

                            MediaRecorder recorder = new MediaRecorder();
                            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                            recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
                            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                            recorder.setOutputFile(pfd.getFileDescriptor());

                            try 
                                recorder.prepare();
                             catch (IllegalStateException e) 
                                e.printStackTrace();
                             catch (IOException e) 
                                e.printStackTrace();
                            

                            recorder.start();

                         catch (Exception e) 
                            e.printStackTrace();
                        
                    
                ).start();

            
        );   
    

我运行的设备是搭载 Android 7.0 的三星 Galaxy A5。我在 gradle 文件中使用 minSdkVersion 22 和 targetSdkVersion 26。

编辑:预装的应用录音机工作正常。所以我不明白这怎么可能是麦克风忙。

编辑 2:如果我更改为以下内容并保存到文件而不是流,它似乎可以工作。所以我仍然打赌声音格式和流媒体存在问题,因为网络流不支持搜索。如果是这种情况,我应该使用什么格式?

//recorder.setOutputFile(pfd.getFileDescriptor());
File outputFile = File.createTempFile("test", "mp4", getApplicationContext().getCacheDir());
recorder.setOutputFile(outputFile.getPath());

编辑 3 没有一个答案是正确的。我现在发现主要问题是我无法将声音数据保存到 ParcelFileDescriptor.fromSocket 创建的流中

如果我这样做,它会起作用

ParcelFileDescriptor[] mParcelFileDescriptors = ParcelFileDescriptor.createPipe();
final ParcelFileDescriptor mParcelRead = new ParcelFileDescriptor(mParcelFileDescriptors[0]);
ParcelFileDescriptor mParcelWrite = new ParcelFileDescriptor(mParcelFileDescriptors[1]);

然后将流内容发送到服务器。 我不知道这是否存在一些时间问题,或者它是否会导致某些声音文件格式损坏,因为我猜头中的字节可能会随时根据格式发生变化。

【问题讨论】:

【参考方案1】:

根据这个答案,这是因为麦克风正在被另一个应用程序使用,包括语音助手https://***.com/a/47290248/3938238

【讨论】:

所以我需要关闭语音助手才能录音?我尝试使用内置录音机进行录音,效果很好。有什么办法可以让这项工作更顺利吗? 好像不是语音助手。在设置中设置为关闭位置。【参考方案2】:

可能是因为有其他应用正在使用手机的麦克风,请参阅:

https://***.com/a/23975085/5479863

【讨论】:

也许,但哪个应用程序?这是一部全新的手机,没有安装任何特定的东西。我现在也尝试过 Galaxy S5 Neo。同样的问题

以上是关于从 MIC 录制并流式传输到 TCP 服务器; MediaRecorder:启动失败:-38的主要内容,如果未能解决你的问题,请参考以下文章

将音频记录从浏览器流式传输到服务器?

流式传输实时音频

从 nodejs 录制音频和流式传输到客户端

Android:同时录制和流式传输

如何将视频从浏览器提交/流式传输到服务器?

从 Internet 流式传输单个音频文件