Android多媒体开发(5)————利用Android(安卓)AudioTrack播放mp3文件
/********************************************************************************************
* author:conowen@大钟
* E-mail:conowen@hotmail.com
*site:http://menwoo.com/
*深圳市大望谷科技有限公司
*http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
1、Android AudioTrack简介
在Android中播放声音可以用MediaPlayer和AudioTrack两种方案的,但是两种方案是有很大区别的,MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。而AudioTrack只能播放PCM数据流。
事实上,两种本质上是没啥区别的,MediaPlayer在播放音频时,在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,最后由AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过Mediaplayer的解码部分而已。Mediaplayer的解码核心部分是基于OpenCORE 来实现的,支持通用的音视频和图像格式,codec使用的是OpenMAX接口来进行扩展。因此使用audiotrack播放mp3文件的话,要自己加入一个音频解码器,如libmad。否则只能播放PCM数据,如大多数WAV格式的音频文件。
参考上一篇博文。
http://blog.csdn.net/conowen/article/details/7727145
2、使用Mediaplayer的不足
MediaPlayer提供了5个setDataSource方法,如其中一个,虽然可以设置文件流起始地址与文件流长度。
public void setDataSource(FileDescriptor fd, long offset, long length)Since: API Level 1Sets the data source (FileDescriptor) to use. The FileDescriptor must be seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility to close the file descriptor. It is safe to do so as soon as this call returns. Parameters
Throws
|
但是对于实时地播放加密过的音频文件却是束手无策。虽然对于一些加密过的音频文件,可以采用Audiotrack与Libmad结合的方式解决。
3、简单Demo程序
下面提供一个Audiotrack播放mp3的demo,mp3没有经过加密的,解码部分是由libmad完成。(若是要播放加密音频文件,可以操作的libmad解码文件流即可。)
直接贴代码,代码的大意已经在注释说明了。
@LibmadActivity.java
/* * author:conowen * date:2012.7.29 */package com.conowen.libmad;import android.app.Activity;import android.media.AudioFormat;import android.media.AudioManager;import android.media.AudioTrack;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.Toast;public class LibmadActivity extends Activity {private Thread mThread;private short[] audioBuffer;private AudioTrack mAudioTrack;private Button btnPlay, btnPauseButton;private int samplerate;private int mAudioMinBufSize;private int ret;private NativeMP3Decoder MP3Decoder;private boolean mThreadFlag;private String filePath = "/sdcard/test.mp3";/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);btnPlay = (Button) findViewById(R.id.buttonPlay);btnPauseButton = (Button) findViewById(R.id.buttonPause);MP3Decoder = new NativeMP3Decoder();ret = MP3Decoder.initAudioPlayer(filePath, 0);if (ret == -1) {Log.i("conowen", "Couldn't open file '" + filePath + "'");} else {mThreadFlag = true;initAudioPlayer();audioBuffer = new short[1024 * 1024];mThread = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubwhile (mThreadFlag) {if (mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PAUSED) {// ****从libmad处获取data******/MP3Decoder.getAudioBuf(audioBuffer,mAudioMinBufSize);mAudioTrack.write(audioBuffer, 0, mAudioMinBufSize);} else {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}});mThread.start();}btnPlay.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (ret == -1) {Log.i("conowen", "Couldn't open file '" + filePath + "'");Toast.makeText(getApplicationContext(),"Couldn't open file '" + filePath + "'",Toast.LENGTH_SHORT).show();} else {if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {//mThreadFlag = true;// 音频线程开始mAudioTrack.play();// mThread.start();} else if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {//mThreadFlag = true;// 音频线程开始mAudioTrack.play();} else {Toast.makeText(getApplicationContext(),"Already in play", Toast.LENGTH_SHORT).show(); }}}});btnPauseButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif (ret == -1) {Log.i("conowen", "Couldn't open file '" + filePath + "'");Toast.makeText(getApplicationContext(),"Couldn't open file '" + filePath + "'",Toast.LENGTH_SHORT).show();} else {if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {mAudioTrack.pause();} else {Toast.makeText(getApplicationContext(), "Already stop",Toast.LENGTH_SHORT).show();}}}});}private void initAudioPlayer() {// TODO Auto-generated method stubsamplerate = MP3Decoder.getAudioSamplerate();System.out.println("samplerate = " + samplerate);samplerate = samplerate / 2;// 声音文件一秒钟buffer的大小mAudioMinBufSize = AudioTrack.getMinBufferSize(samplerate,AudioFormat.CHANNEL_CONFIGURATION_STEREO,AudioFormat.ENCODING_PCM_16BIT);mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, // 指定在流的类型// STREAM_ALARM:警告声// STREAM_MUSCI:音乐声,例如music等// STREAM_RING:铃声// STREAM_SYSTEM:系统声音// STREAM_VOCIE_CALL:电话声音samplerate,// 设置音频数据的采样率AudioFormat.CHANNEL_CONFIGURATION_STEREO,// 设置输出声道为双声道立体声AudioFormat.ENCODING_PCM_16BIT,// 设置音频数据块是8位还是16位mAudioMinBufSize, AudioTrack.MODE_STREAM);// 设置模式类型,在这里设置为流类型// AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。// STREAM方式表示由用户通过write方式把数据一次一次得写到audiotrack中。// 这种方式的缺点就是JAVA层和Native层不断地交换数据,效率损失较大。// 而STATIC方式表示是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,// 后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。// 这种方法对于铃声等体积较小的文件比较合适。}static {System.loadLibrary("mad");}@Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();mAudioTrack.stop();mAudioTrack.release();// 关闭并释放资源mThreadFlag = false;// 音频线程暂停MP3Decoder.closeAduioFile();}}
@NativeMP3Decoder.java
package com.conowen.libmad;public class NativeMP3Decoder {private int ret;public NativeMP3Decoder() {}public native int initAudioPlayer(String file,int StartAddr);public native int getAudioBuf(short[] audioBuffer, int numSamples);public native void closeAduioFile();public native int getAudioSamplerate();}
@main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="AudioTrack" /> <Button android:id = "@+id/buttonPlay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="PLAY" /> <Button android:id = "@+id/buttonPause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="PAUSE" /> </LinearLayout>
更多相关文章
- android > Android(安卓)音频均衡器,可通过拖动调节音频EQ
- Android(安卓)Media Framework 总纲
- Android音频可视化
- Android简单实现音乐播放器
- [原]如何在Android用FFmpeg+SDL2.0解码显示图像
- Android本地视频播放器开发--视频解码
- Android(安卓)3.0 r1 API中文文档(107) —— AsyncPlayer
- Android(安卓)10ms问题:关于Android音频路径延迟的解释
- android 开发对gif解码(适配android 4.2、4.3、4.4版本)