Android 音频 OpenSL ES PCM数据播放

PCM 数据播放在开发中也经常使用,例如自己编写播放器,解码之后的音频PCM数据,就可以通过OpenSL 播放,比用Java层的AudioTrack更快,延迟更低。

下面我们编写OpenSL PCM播放,播放的主要逻辑是从文件读取PCM数据然后播放,代码编写环境Eclipse。

一、 Eclipse 创建Android工程

二、布局XML 创建文件 /res/layout/activity_audio_track.xml

<LinearLayout xmlns:android=""xmlns:tools=""android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".URIActivity" ><Buttonandroid:id="@+id/btn_play"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_alignParentTop="true"android:layout_marginLeft="18dp"android:text="播放" /></LinearLayout>布局文件就一个播放按钮

三、Activity类 创建/src/com/example/testopensl/AudioTrackActivity.java

package com.example.testopensl;import com.example.audio.R;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;public class AudioTrackActivity extends Activity implements OnClickListener {private static String URI_PCM = "/mnt/sdcard/pm.pcm";static {System.loadLibrary("TestAudio");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_audio_track);findViewById(R.id.btn_play).setOnClickListener(this);createEngine();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_play:createAudioPlayer(URI_PCM);break;}}@Overrideprotected void onDestroy() {super.onDestroy();shutdown();}/** Native methods, implemented in jni folder */public static native void createEngine();public static native boolean createAudioPlayer(String uri);public static native void setPlayingAudioPlayer(boolean isPlaying);public static native void setVolumeAudioPlayer(int millibel);public static native void setMutAudioPlayer(boolean mute);public static native void shutdown();}

四、编写日志头文件,用于日志输出, 创建/jni/log.h 文件

#ifndef LOG_H_#define LOG_H_#include <android/log.h>#ifndef DGB#define DGB 0#endif#ifndef LOG_TAG#define LOG_TAG __FILE__#endif#ifndef ALOGD#if DGB#define ALOGD(…) \__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#else#define ALOGD(…) ((void)0)#endif#endif#endif /* LOG_H_ */五、用javah命令 生成jni 头文件. 文件目录/jni/com_example_testopensl_AudioTrackActivity.h/* DO NOT EDIT THIS FILE – it is machine generated */#include <jni.h>/* Header for class com_example_testopensl_AudioTrackActivity */#ifndef _Included_com_example_testopensl_AudioTrackActivity#define _Included_com_example_testopensl_AudioTrackActivity#ifdef __cplusplusextern "C" {#endif/* * Class:com_example_testopensl_AudioTrackActivity * Method: createEngine * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_createEngine (JNIEnv *, jclass);/* * Class:com_example_testopensl_AudioTrackActivity * Method: createAudioPlayer * Signature: (Ljava/lang/String;)Z */JNIEXPORT jboolean JNICALL Java_com_example_testopensl_AudioTrackActivity_createAudioPlayer (JNIEnv *, jclass, jstring);/* * Class:com_example_testopensl_AudioTrackActivity * Method: setPlayingAudioPlayer * Signature: (Z)V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_setPlayingAudioPlayer (JNIEnv *, jclass, jboolean);/* * Class:com_example_testopensl_AudioTrackActivity * Method: setVolumeAudioPlayer * Signature: (I)V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_setVolumeAudioPlayer (JNIEnv *, jclass, jint);/* * Class:com_example_testopensl_AudioTrackActivity * Method: setMutAudioPlayer * Signature: (Z)V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_setMutAudioPlayer (JNIEnv *, jclass, jboolean);/* * Class:com_example_testopensl_AudioTrackActivity * Method: shutdown * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_shutdown (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif六、JNI的实现 /jni/com_example_testopensl_AudioTrackActivity.cpp/** log */#define LOG_TAG "AudioTrackActivity"#define DGB 1#include "log.h"#include <SLES/OpenSLES.h>#include <SLES/OpenSLES_Android.h>#include <stdio.h>#include <assert.h>#include <pthread.h>#include "com_example_testopensl_AudioTrackActivity.h"class PlaybackThread;// engine interfacesstatic SLObjectItf engineObject = NULL;static SLEngineItf engineEngine;// output mix interfacesstatic SLObjectItf outputMixObject = NULL;// buffer queue player interfacesstatic SLObjectItf bqPlayerObject = NULL;static SLPlayItf bqPlayerPlay;static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;static SLEffectSendItf bqPlayerEffectSend;static SLVolumeItf bqPlayerVolume;static PlaybackThread* mThread;class PlaybackThread {private:FILE *mFile;void* mBuffer;size_t mSize;bool read;public:PlaybackThread(const char* uri) :mFile(NULL), mBuffer(NULL), mSize(0), read(true) {mFile = fopen((char*) uri, "r");if (mFile == NULL) {ALOGD("open file error %s", uri);return;}mBuffer = malloc(8192);}void start() {if (mFile == NULL) {return;}enqueueBuffer();}// release file buffervoid release() {if (mFile != NULL) {fclose(mFile);mFile == NULL;}if (mBuffer != NULL) {free(mBuffer);mBuffer == NULL;}}~PlaybackThread() {release();ALOGD("~PlaybackThread");}void enqueueBuffer() {if (bqPlayerBufferQueue == NULL) {return;}// for streaming playback, replace this test by logic to find and fill the next bufferwhile (true) {if (read) {mSize = fread(mBuffer, 1, 8192, mFile);}if (mSize > 0) {SLresult result;// enqueue another bufferresult = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, mBuffer, mSize);if (result == SL_RESULT_BUFFER_INSUFFICIENT) {read = false;return;}read = true;} else {return;}} }// this callback handler is called every time a buffer finishes playingstatic void playerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {assert(NULL != context);PlaybackThread* thread = (PlaybackThread *) context;if (thread != NULL) {thread->enqueueBuffer();}}};/* * Class:com_example_testopensl_AudioTrackActivity * Method: createEngine * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_createEngine(JNIEnv *, jclass) {SLresult result;// create engineresult = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);assert(SL_RESULT_SUCCESS == result);(void) result;// realize the engineresult = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void) result;// get the engine interface, which is needed in order to create other objectsresult = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);assert(SL_RESULT_SUCCESS == result);(void) result;// create output mix,result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);assert(SL_RESULT_SUCCESS == result);(void) result;// realize the output mixresult = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void) result;}/* * Class:com_example_testopensl_AudioTrackActivity * Method: createAudioPlayer * Signature: (Ljava/lang/String;)Z */JNIEXPORT jboolean JNICALL Java_com_example_testopensl_AudioTrackActivity_createAudioPlayer(JNIEnv *env, jclass clazz, jstring uri) {const char* utf8Uri = env->GetStringUTFChars(uri, NULL);SLresult result;// configure audio sourceSLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 3 };SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN };SLDataSource audioSrc = { &loc_bufq, &format_pcm };// configure audio sinkSLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject };SLDataSink audioSnk = { &loc_outmix, NULL };// create audio playerconst SLInterfaceID ids[3] = { SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME };const SLboolean req[3] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 3, ids, req);assert(SL_RESULT_SUCCESS == result);(void) result;// realize the playerresult = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void) result;// get the play interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);assert(SL_RESULT_SUCCESS == result);(void) result;// get the buffer queue interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);assert(SL_RESULT_SUCCESS == result);(void) result;mThread = new PlaybackThread(utf8Uri);// register callback on the buffer queueresult = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, PlaybackThread::playerCallback, mThread);assert(SL_RESULT_SUCCESS == result);(void) result;// get the effect send interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, &bqPlayerEffectSend);assert(SL_RESULT_SUCCESS == result);(void) result;// get the volume interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);assert(SL_RESULT_SUCCESS == result);(void) result;// set the player's state to playingresult = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);assert(SL_RESULT_SUCCESS == result);(void) result;//pthread_t id;mThread->start();env->ReleaseStringUTFChars(uri, utf8Uri);ALOGD("createAudioPlayer finish");return 0;}/* * Class:com_example_testopensl_AudioTrackActivity * Method: setPlayingAudioPlayer * Signature: (Z)V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_setPlayingAudioPlayer(JNIEnv *, jclass, jboolean) {}/* * Class:com_example_testopensl_AudioTrackActivity * Method: setVolumeAudioPlayer * Signature: (I)V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_setVolumeAudioPlayer(JNIEnv *, jclass, jint) {}/* * Class:com_example_testopensl_AudioTrackActivity * Method: setMutAudioPlayer * Signature: (Z)V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_setMutAudioPlayer(JNIEnv *, jclass, jboolean) {}/* * Class:com_example_testopensl_AudioTrackActivity * Method: shutdown * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_testopensl_AudioTrackActivity_shutdown(JNIEnv *, jclass) {//destory player objectif (bqPlayerObject != NULL) {(*bqPlayerObject)->Destroy(bqPlayerObject);bqPlayerPlay = NULL;bqPlayerBufferQueue = NULL;bqPlayerEffectSend = NULL;bqPlayerVolume = NULL;}// destroy output mix object, and invalidate all associated interfacesif (outputMixObject != NULL) {(*outputMixObject)->Destroy(outputMixObject);outputMixObject = NULL;}// destroy engine object, and invalidate all associated interfacesif (engineObject != NULL) {(*engineObject)->Destroy(engineObject);engineObject = NULL;engineEngine = NULL;}if (mThread != NULL) {delete mThread;mThread = NULL;}}方法说明:害怕攀登高峰的人,永远在山下徘徊。

Android 音频 OpenSL ES PCM数据播放

相关文章:

你感兴趣的文章:

标签云: