Android 音频 OpenSL ES PCM数据播放
2014-11-30 14:36
686 查看
PCM 数据播放在开发中也经常使用,例如自己编写播放器,解码之后的音频PCM数据,就可以通过OpenSL 播放,比用Java层的AudioTrack更快,延迟更低。
下面我们编写OpenSL PCM播放,播放的主要逻辑是从文件读取PCM数据然后播放,代码编写环境Eclipse。
一、 Eclipse 创建Android工程
二、布局XML 创建文件 /res/layout/activity_audio_track.xml
三、Activity类 创建/src/com/example/testopensl/AudioTrackActivity.java
四、编写日志头文件,用于日志输出, 创建/jni/log.h 文件
Java_com_example_testopensl_AudioTrackActivity_createEngine 创建引擎
Java_com_example_testopensl_AudioTrackActivity_createAudioPlayer 创建播放器并开始播放
Java_com_example_testopensl_AudioTrackActivity_shutdown 释放资源
Java_com_example_testopensl_AudioTrackActivity_setPlayingAudioPlayer 未实现
Java_com_example_testopensl_AudioTrackActivity_setVolumeAudioPlayer 未实现
Java_com_example_testopensl_AudioTrackActivity_setMutAudioPlayer 未实现
未实现方法可以参考 URI播放实现
七、创建/jni/Android.mk 编译文件
八、AndroidManifest 配置
运行之后界面图如下:
代码中使用的PCM数据信息:
采样率:44100
声道:2
编码:16BIT
关键代码是在 com_example_testopensl_AudioTrackActivity.cpp 中创建播放器AudioSrc 参数
上面指定了播放PCM数据的信息
代码编写完,功能比较简单,点击播放按钮从文件读取PCM数据。
下面我们编写OpenSL PCM播放,播放的主要逻辑是从文件读取PCM数据然后播放,代码编写环境Eclipse。
一、 Eclipse 创建Android工程
二、布局XML 创建文件 /res/layout/activity_audio_track.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".URIActivity" > <Button android: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"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_audio_track); findViewById(R.id.btn_play).setOnClickListener(this); createEngine(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_play: createAudioPlayer(URI_PCM); break; } } @Override protected 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 __cplusplus extern "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 interfaces static SLObjectItf engineObject = NULL; static SLEngineItf engineEngine; // output mix interfaces static SLObjectItf outputMixObject = NULL; // buffer queue player interfaces static 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 buffer void 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 buffer while (true) { if (read) { mSize = fread(mBuffer, 1, 8192, mFile); } if (mSize > 0) { SLresult result; // enqueue another buffer result = (*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 playing static 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 engine result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); assert(SL_RESULT_SUCCESS == result); (void) result; // realize the engine result = (*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 objects result = (*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 mix result = (*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 source SLDataLocator_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 sink SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject }; SLDataSink audioSnk = { &loc_outmix, NULL }; // create audio player const 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 player result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); assert(SL_RESULT_SUCCESS == result); (void) result; // get the play interface result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); assert(SL_RESULT_SUCCESS == result); (void) result; // get the buffer queue interface result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue); assert(SL_RESULT_SUCCESS == result); (void) result; mThread = new PlaybackThread(utf8Uri); // register callback on the buffer queue result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, PlaybackThread::playerCallback, mThread); assert(SL_RESULT_SUCCESS == result); (void) result; // get the effect send interface result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, &bqPlayerEffectSend); assert(SL_RESULT_SUCCESS == result); (void) result; // get the volume interface result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); assert(SL_RESULT_SUCCESS == result); (void) result; // set the player's state to playing result = (*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 object if (bqPlayerObject != NULL) { (*bqPlayerObject)->Destroy(bqPlayerObject); bqPlayerPlay = NULL; bqPlayerBufferQueue = NULL; bqPlayerEffectSend = NULL; bqPlayerVolume = NULL; } // destroy output mix object, and invalidate all associated interfaces if (outputMixObject != NULL) { (*outputMixObject)->Destroy(outputMixObject); outputMixObject = NULL; } // destroy engine object, and invalidate all associated interfaces if (engineObject != NULL) { (*engineObject)->Destroy(engineObject); engineObject = NULL; engineEngine = NULL; } if (mThread != NULL) { delete mThread; mThread = NULL; } }方法说明:
Java_com_example_testopensl_AudioTrackActivity_createEngine 创建引擎
Java_com_example_testopensl_AudioTrackActivity_createAudioPlayer 创建播放器并开始播放
Java_com_example_testopensl_AudioTrackActivity_shutdown 释放资源
Java_com_example_testopensl_AudioTrackActivity_setPlayingAudioPlayer 未实现
Java_com_example_testopensl_AudioTrackActivity_setVolumeAudioPlayer 未实现
Java_com_example_testopensl_AudioTrackActivity_setMutAudioPlayer 未实现
未实现方法可以参考 URI播放实现
七、创建/jni/Android.mk 编译文件
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := TestAudio LOCAL_SRC_FILES := com_example_testopensl_AudioTrackActivity.cpp LOCAL_LDLIBS += -llog -lOpenSLES -landroid include $(BUILD_SHARED_LIBRARY)LOCAL_LDLIBS 需要加llog、lOpenSLES、landroid 编译链接库
八、AndroidManifest 配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.audio" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.testopensl.AudioTrackActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>编写完Eclipse 结构图如下:
运行之后界面图如下:
代码中使用的PCM数据信息:
采样率:44100
声道:2
编码:16BIT
关键代码是在 com_example_testopensl_AudioTrackActivity.cpp 中创建播放器AudioSrc 参数
// configure audio source SLDataLocator_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 };
上面指定了播放PCM数据的信息
代码编写完,功能比较简单,点击播放按钮从文件读取PCM数据。
相关文章推荐
- Android编程中利用AudioTrack播放PCM数据在音频的最后出现重复回声现象的解决方案
- 使用AudioTrack播放PCM音频数据(android)
- Android开发:使用AudioTrack播放PCM音频数据【附源码】
- Android使用AudioRecord录制pcm音频原始数据以及使用AudioTrack播放
- 使用AudioTrack播放PCM音频数据(android)
- 使用AudioTrack播放PCM音频数据(android)
- android使用opensl es进行简单的音频播放
- 【Android 多媒体应用】使用MediaCodec解码使用AudioTrack播放音频数据
- Android音频处理之通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能
- 使用AudioTrack播放PCM音频数据
- android中pcm数据的播放(AudioTrack)
- DirectSound播放PCM(可播放实时采集的音频数据)
- [AudioTrack]使用AudioTrack播放PCM音频数据
- Android通过OpenSL ES播放音频套路详解
- 使用AudioRecoder录制,AudioTrack播放PCM音频数据
- Android 音视频开发(三):使用 AudioTrack 播放PCM音频
- 使用DirectSound播放PCM音频数据
- 播放PCM音频数据的双缓冲用法(转)
- Android音频处理——通过AudioRecord去保存PCM文件进行录制,播放,停止,删除功能
- 使用AudioTrack播放PCM音频数据