【问题标题】:Audio Recording and Streaming in AndroidAndroid 中的音频录制和流式传输
【发布时间】:2014-11-04 23:17:51
【问题描述】:

我正在开发一个安卓应用程序。我想完成以下功能。 我将使用手机的内置麦克风进行录音,同时我希望通过手机的扬声器或耳机播放录制的音频。 可行吗?如果是,请帮助我。

【问题讨论】:

  • 录制时,是在文件中录制还是在临时Buffer中录制?

标签: android audio-streaming audio-recording


【解决方案1】:

这是一个简单的录制和播放应用程序。
使用安卓AudioRecordAudioTrack

设计

录制的音频被写入缓冲区并从同一个缓冲区播放,该机制在按钮控制的循环中运行(使用 Android 线程)。


代码

private String TAG = "AUDIO_RECORD_PLAYBACK";
private boolean isRunning = true;
private Thread m_thread;               /* Thread for running the Loop */

private AudioRecord recorder = null;
private AudioTrack track = null;

int bufferSize = 320;                  /* Buffer for recording data */
byte buffer[] = new byte[bufferSize];

/* Method to Enable/Disable Buttons */
private void enableButton(int id,boolean isEnable){
    ((Button)findViewById(id)).setEnabled(isEnable);
}

GUI 有两个按钮 STARTSTOP
启用按钮:

enableButton(R.id.StartButton,true);
enableButton(R.id.StopButton,false);

/* Assign Button Click Handlers */
((Button)findViewById(R.id.StartButton)).setOnClickListener(btnClick);
((Button)findViewById(R.id.StopButton)).setOnClickListener(btnClick);

为 OnClickListener 映射开始和停止按钮

private View.OnClickListener btnClick = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        switch(v.getId()){
        case R.id.StartButton:
        {
            Log.d(TAG, "======== Start Button Pressed ==========");
            isRunning = true;
            do_loopback(isRunning);
            enableButton(R.id.StartButton,false);
            enableButton(R.id.StopButton,true);
            break;
        }
        case R.id.StopButton:
        {
            Log.d(TAG, "======== Stop Button Pressed ==========");
            isRunning = false;
            do_loopback(isRunning);
            enableButton(R.id.StopButton,false);
            enableButton(R.id.StartButton,true);
            break;
        }
    }
}

启动线程:

private void do_loopback(final boolean flag) 
{
    m_thread = new Thread(new Runnable() {
        public void run() {
            run_loop(flag);
        }
    });
    m_thread.start();
}

AudioRecord和AudioTrack的初始化方法:

public AudioTrack findAudioTrack (AudioTrack track)
{
    Log.d(TAG, "===== Initializing AudioTrack API ====");
    int m_bufferSize = AudioTrack.getMinBufferSize(8000, 
            AudioFormat.CHANNEL_OUT_MONO, 
            AudioFormat.ENCODING_PCM_16BIT);

    if (m_bufferSize != AudioTrack.ERROR_BAD_VALUE) 
    {
        track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, 
                AudioFormat.CHANNEL_OUT_MONO, 
                AudioFormat.ENCODING_PCM_16BIT, m_bufferSize, 
                AudioTrack.MODE_STREAM);

        if (track.getState() == AudioTrack.STATE_UNINITIALIZED) {
            Log.e(TAG, "===== AudioTrack Uninitialized =====");
            return null;
        }
    }
    return track;
}

public AudioRecord findAudioRecord (AudioRecord recorder)
{
    Log.d(TAG, "===== Initializing AudioRecord API =====");     
    int m_bufferSize = AudioRecord.getMinBufferSize(8000,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT);

    if (m_bufferSize != AudioRecord.ERROR_BAD_VALUE) 
    {
        recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000, 
                AudioFormat.CHANNEL_IN_MONO,
                AudioFormat.ENCODING_PCM_16BIT, m_bufferSize);

        if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
            Log.e(TAG, "====== AudioRecord UnInitilaised ====== ");
            return null;
        }
    }
    return recorder;
}

findAudioRecordfindAudioTrack 的值可能因设备而异。
请参考this问题。


运行循环的代码:

public void run_loop (boolean isRunning)
{

    /** == If Stop Button is pressed == **/
    if (isRunning == false) {
        Log.d(TAG, "=====  Stop Button is pressed ===== ");
        
        if (AudioRecord.STATE_INITIALIZED == recorder.getState()){
            recorder.stop();
            recorder.release();
        }
        if (AudioTrack.STATE_INITIALIZED == track.getState()){
            track.stop();
            track.release();
        }
        return;
    }
    

    /** ======= Initialize AudioRecord and AudioTrack ======== **/
    recorder = findAudioRecord(recorder);
    if (recorder == null) {
        Log.e(TAG, "======== findAudioRecord : Returned Error! =========== ");
        return;
    }

    track = findAudioTrack(track);
    if (track == null) {
        Log.e(TAG, "======== findAudioTrack : Returned Error! ========== ");
        return;
    }

    if ((AudioRecord.STATE_INITIALIZED == recorder.getState()) &&
            (AudioTrack.STATE_INITIALIZED == track.getState()))
    {
        recorder.startRecording();
        Log.d(TAG, "========= Recorder Started... =========");
        track.play();
        Log.d(TAG, "========= Track Started... =========");
    } 
    else 
    {
        Log.d(TAG, "==== Initilazation failed for AudioRecord or AudioTrack =====");
        return;
    }

    /** ------------------------------------------------------ **/

    /* Recording and Playing in chunks of 320 bytes */
    bufferSize = 320;
    
    while (isRunning == true) 
    {
        /* Read & Write to the Device */
        recorder.read(buffer, 0, bufferSize);
        track.write(buffer, 0, bufferSize);

    }
    Log.i(TAG, "Loopback exit");
    return;
}

请在AndroidManifest.xml中包含以下内容

<uses-permission android:name="android.permission.RECORD_AUDIO" >  </uses-permission>

上述过程也可以通过使用相同 API 从文件中写入/读取来实现。
为什么使用audioRecord 而不是mediaRecorder - See here

代码已经过测试(在 Google Nexus 5 上)并且运行良好。

注意:请为recorder.readtrack.write添加一些错误检查代码,以防失败。同样适用于findAudioRecordfindAudioTrack

【讨论】:

    【解决方案2】:

    首先在 onCreate 方法中创建对象,MediaRecorder 类对象以及要保存录制数据的文件路径。

      String outputFile = Environment.getExternalStorageDirectory().
      getAbsolutePath() + "/myrecording.3gp";   // Define outputFile outside onCreate method
    
      MediaRecorder myAudioRecorder = new MediaRecorder(); // Define this outside onCreate method
    
      myAudioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
      myAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
      myAudioRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
      myAudioRecorder.setOutputFile(outputFile);
    

    这三个函数你可以在任何按钮上调用它,以播放Rec、停止Rec和开始Rec;

      public void start(View view){
      try {
         myAudioRecorder.prepare();
         myAudioRecorder.start();
       } catch (IllegalStateException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
       } catch (IOException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
       }
       start.setEnabled(false);
       stop.setEnabled(true);
       Toast.makeText(getApplicationContext(), "Recording started", Toast.LENGTH_LONG).show();
      }
    
      public void stop(View view){
    
        myAudioRecorder.stop();
        myAudioRecorder.release();
        myAudioRecorder  = null;
        stop.setEnabled(false);
        play.setEnabled(true);
        Toast.makeText(getApplicationContext(), "Audio recorded successfully",
        Toast.LENGTH_LONG).show();
       }
    
    
    
      public void play(View view) throws IllegalArgumentException,   
    
        SecurityException, IllegalStateException, IOException{
        MediaPlayer m = new MediaPlayer();
        m.setDataSource(outputFile);
        m.prepare();
        m.start();
        Toast.makeText(getApplicationContext(), "Playing audio", Toast.LENGTH_LONG).show();
    
        }
    

    【讨论】:

      【解决方案3】:

      当我阅读开发者文档here 时,Android 支持 RTSP 协议(​​用于实时流式传输)以及 HTTP/HTTPS 实时流式传输协议草案。

      还有一个例子here。您必须具备有关流媒体服务器的基础知识,例如 Red5 或 Wowza。

      【讨论】:

        猜你喜欢
        • 2016-01-05
        • 2021-05-23
        • 1970-01-01
        • 1970-01-01
        • 2012-11-06
        • 1970-01-01
        • 2016-05-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多