MediaPlayer和AudioTrack播放Audio的區別與聯絡

weixin_34262482發表於2014-01-15

播放聲音可以用MediaPlayer和AudioTrack,兩者都提供了java API供應用開發者使用。雖然都可以播放聲音,但兩者還是有很大的區別的。
其中最大的區別是MediaPlayer可以播放多種格式的聲音檔案,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer會在framework層建立對應的音訊解碼器。
而AudioTrack只能播放已經解碼的PCM流,如果是檔案的話只支援wav格式的音訊檔案,因為wav格式的音訊檔案大部分都是PCM流。AudioTrack不建立解碼器,所以只能播放不需要解碼的wav檔案。

當然兩者之間還是有緊密的聯絡的,MediaPlayer在framework層還是會建立AudioTrack,把解碼後的PCM數流傳遞給AudioTrack,AudioTrack再傳遞給AudioFlinger進行混音,然後才傳遞給硬體播放。
所以是MediaPlayer包含了AudioTRack。
通過檢視API可以知道,MediaPlayer提供了5個setDataSource方法,分為三類,一類是傳遞播放檔案的字串路徑作為引數,例如直接取sd卡里mp3檔案的路徑,一類是傳遞播放檔案的FileDescriptor檔案描述符作為播放的id,例例如從db中查詢的音訊檔案的id,就可以直接賦給MediaPlayer進行播放。還有一類是Uri型別的資原始檔,用於播放content uri檔案。

下面是一個用MediaPlayer播放音樂的示例,音樂檔案是從資料庫中取出的。

    if (mMediaPlayer == null) {
        mMediaPlayer = new MediaPlayer(); // 建立MediaPlayer物件
    }
    
    mMediaPlayer.reset();
    String dataSource = getDataByPosition(mCursor, mPlayPosition);
    mMediaPlayer.setDataSource(dataSource); // 設定需要播放的資料來源
    mMediaPlayer.prepare(); // 準備播放,如果是流媒體需要呼叫prepareAsync進行非同步準備
    if (Common.PLAY_MODE_SINGLE_LOOP == mPlayMode) {
        mMediaPlayer.setLooping(true); // 單曲迴圈
    } else {
        mMediaPlayer.setLooping(false); // 不迴圈播放
    }
    mMediaPlayer.start(); // 開始播放,如果是播放流媒體,需要等到流媒體準備完成才能播放(在prepare的callback函式中呼叫start進行播放)

    // 根據位置來獲取歌曲位置
    public String getDataByPosition(Cursor c, int position) {
        c.moveToPosition(position);
        int dataColumn = c.getColumnIndex(MediaStore.Audio.Media.DATA);
        String data = c.getString(dataColumn);
        return data;
    }

AudioTrack播放聲音時不能直接把wav檔案傳遞給AudioTrack進行播放,必須傳遞buffer,通過write函式把需要播放的緩衝區buffer傳遞給AudioTrack,然後才能播放。

AudioTrack使用的例子參考下面:
此示例轉自:http://samyou.iteye.com/blog/1125872

public class AndroidTest extends Activity implements View.OnClickListener,SurfaceHolder.Callback
{
    private SurfaceHolder surfaceHolder = null;
    private SurfaceView surfaceView = null;
    private AudioTrack audioTrack = null;
    private Thread writePCMThread = null;
    private File audioFile = null;
    private FileInputStream fileInputStream = null;
    private byte buffer[] = new byte[16*10000];

    // The Handler that gets information back from the other threads
    private final Handler msgHandler = new Handler()
    {
        public void handleMessage(Message msg)
        {
            switch (msg.what)
            {
            default:
                break;
            }
        }
    };


    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        surfaceView = (SurfaceView) findViewById(R.id.surface);
        SurfaceHolder surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        findViewById(R.id.button1).setOnClickListener(this);
        findViewById(R.id.button2).setOnClickListener(this);
    }

    public void finish() {
        super.finish();
        System.out.println("finish");
        try {
            writePCMThread.interrupt();
        } catch (Exception e) {
        }
        try {
            fileInputStream.close();
        } catch (Exception e) {
        }
        try {
            audioTrack.stop();
            audioTrack.release();
        } catch (Exception e) {
        }
    }



    protected void onResume()
    {
        super.onResume();
        System.out.println("back on!!!!!!!!!!!");
        initAudioTrack();
        audioFile = new File(Environment.getExternalStorageDirectory(),"test.wav");
        System.out.println(audioFile.length());
        try {
            fileInputStream = new FileInputStream(audioFile);
            fileInputStream.skip(0x2c);
        } catch (Exception e) {
        }

        writePCMThread = new Thread(new Runnable(){
            public void run() {
                try
                {
                    while(fileInputStream.read(buffer)>=0)
                    {
                        System.out.println("write pcm data");
                        audioTrack.write(buffer, 0, buffer.length);
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

    private void initAudioTrack()
    {
        int minBufferSize = AudioTrack.getMinBufferSize(0xac44, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
        System.out.println("minBufferSize = "+minBufferSize);
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 0xac44,
        AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize*2,AudioTrack.MODE_STREAM);
        audioTrack.setStereoVolume(1.0f, 1.0f);// 設定當前音量大小
        System.out.println("initAudioTrack over");
        audioTrack.play();
    }

    public void onClick(View v)
    {
        switch (v.getId()) {
        case R.id.button1:
            writePCMThread.start();
            break;
        case R.id.button2:
            break;
        default:
            break;
    }


    }

    public void surfaceCreated(SurfaceHolder holder) {
        System.out.println("surfaceCreated()");
        this.surfaceHolder = holder;
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
        this.surfaceHolder = holder;
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    }

}

 

注:PCM和WAV

以下摘自:http://www.erji.net/read.php?tid=227570

簡單來說,pcm是一種資料編碼格式,CD唱盤上燒錄的就直接用pcm格式編碼的資料檔案; wav是一種聲音檔案格式,wav裡面包含的聲音資料可以是採用pcm格式編碼的聲音資料,也可以是採用其它格式編碼的聲音資料,但目前一般採用pcm編碼的聲音資料 兩者區別就是這些

pcm是一個通訊上的概念,脈衝編碼調製。wav是媒體概念,體現的是封裝。wav檔案可以封裝pcm編碼資訊,也可以封裝其他編碼格式,例如mp3等。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章