Android OpenSL ES 音频播放完整实现指南
一、完整项目结构
app/
├── src/
│ ├── main/
│ │ ├── java/com/example/audioplayer/
│ │ │ ├── AudioPlayer.java
│ │ │ └── MainActivity.java
│ │ ├── cpp/
│ │ │ ├── opensl_player.h
│ │ │ ├── opensl_player.cpp
│ │ │ └── native-lib.cpp
│ │ └── CMakeLists.txt
二、完整Java层实现
2.1 AudioPlayer.java
package com.example.audioplayer;public class AudioPlayer {public interface PlaybackListener {void onPlaybackStarted();void onPlaybackStopped();void onPlaybackError(String message);}static {System.loadLibrary("audioplayer");}private long nativeHandle;private PlaybackListener listener;public AudioPlayer() {nativeHandle = nativeCreate();}public void setPlaybackListener(PlaybackListener listener) {this.listener = listener;}public void start(String filePath) {if (nativeHandle != 0) {nativeStart(nativeHandle, filePath);}}public void stop() {if (nativeHandle != 0) {nativeStop(nativeHandle);}}public void release() {if (nativeHandle != 0) {nativeRelease(nativeHandle);nativeHandle = 0;}}// Native方法private native long nativeCreate();private native void nativeRelease(long handle);private native void nativeStart(long handle, String filePath);private native void nativeStop(long handle);// 供Native层调用的回调方法private void onPlaybackStarted() {if (listener != null) {listener.onPlaybackStarted();}}private void onPlaybackStopped() {if (listener != null) {listener.onPlaybackStopped();}}private void onError(String message) {if (listener != null) {listener.onPlaybackError(message);}}
}
三、完整Native层实现
3.1 opensl_player.h
#ifndef OPENSL_PLAYER_H
#define OPENSL_PLAYER_H#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <atomic>
#include <mutex>
#include <vector>class OpenSLPlayer {
public:enum PlayerState {IDLE,PLAYING,PAUSED};OpenSLPlayer();~OpenSLPlayer();bool init(int sampleRate, int channelCount, int bufferSize);void start();void stop();void pause();void resume();void release();bool enqueueData(const void* data, size_t size);PlayerState getState() const { return state.load(); }// 回调接口void setPlaybackCallback(void (*startCallback)(void*),void (*stopCallback)(void*),void (*errorCallback)(const char*, void*),void* context);private:// OpenSL ES对象SLObjectItf engineObj;SLEngineItf engine;SLObjectItf outputMixObj;SLObjectItf playerObj;SLPlayItf playerPlay;SLAndroidSimpleBufferQueueItf playerBufferQueue;// 状态管理std::atomic<PlayerState> state{IDLE};bool initialized = false;std::mutex stateMutex;// 回调相关void (*startCallback)(void*) = nullptr;void (*stopCallback)(void*) = nullptr;void (*errorCallback)(const char*, void*) = nullptr;void* callbackContext = nullptr;// 初始化方法bool createEngine();bool createOutputMix();bool createPlayer(int sampleRate, int channelCount);void destroyPlayer();// 静态回调函数static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
};#endif // OPENSL_PLAYER_H
3.2 opensl_player.cpp
#include "opensl_player.h"
#include <cassert>
#include <cstring>
#include <unistd.h>OpenSLPlayer::OpenSLPlayer() : engineObj(nullptr), engine(nullptr),outputMixObj(nullptr), playerObj(nullptr),playerPlay(nullptr), playerBufferQueue(nullptr) {}OpenSLPlayer::~OpenSLPlayer() {release();
}bool OpenSLPlayer::init(int sampleRate, int channelCount, int bufferSize) {std::lock_guard<std::mutex> lock(stateMutex);if (initialized) {return true;}if (!createEngine()) {return false;}if (!createOutputMix()) {release();return false;}if (!createPlayer(sampleRate, channelCount)) {release();return false;}initialized = true;return true;
}bool OpenSLPlayer::createEngine() {const SLInterfaceID engine_ids[] = {SL_IID_ENGINE};const SLboolean engine_req[] = {SL_BOOLEAN_TRUE};SLresult result = slCreateEngine(&engineObj, 0, nullptr, 0, engine_ids, engine_req);if (result != SL_RESULT_SUCCESS) {return false;}result = (*engineObj)->Realize(engineObj, SL_BOOLEAN_FALSE);if (result != SL_RESULT_SUCCESS) {return false;}result = (*engineObj)->GetInterface(engineObj, SL_IID_ENGINE, &engine);return result == SL_RESULT_SUCCESS;
}bool OpenSLPlayer::createOutputMix() {const SLInterfaceID ids[] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean req[] = {SL_BOOLEAN_FALSE};SLresult result = (*engine)->CreateOutputMix(engine, &outputMixObj, 1, ids, req);if (result != SL_RESULT_SUCCESS) {return false;}result = (*outputMixObj)->Realize(outputMixObj, SL_BOOLEAN_FALSE);return result == SL_RESULT_SUCCESS;
}bool OpenSLPlayer::createPlayer(int sampleRate, int channelCount) {// 配置音频源SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2 // 双缓冲};SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,static_cast<SLuint32>(channelCount),static_cast<SLuint32>(sampleRate * 1000), // 转换为毫赫兹SL_PCMSAMPLEFORMAT_FIXED_16,SL_PCMSAMPLEFORMAT_FIXED_16,channelCount == 2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER,SL_BYTEORDER_LITTLEENDIAN};SLDataSource audioSrc = {&loc_bufq, &format_pcm};// 配置音频输出SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX,outputMixObj};SLDataSink audioSnk = {&loc_outmix, nullptr};// 创建播放器const SLInterfaceID player_ids[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};const SLboolean player_req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};SLresult result = (*engine)->CreateAudioPlayer(engine, &playerObj, &audioSrc, &audioSnk,sizeof(player_ids)/sizeof(player_ids[0]), player_ids, player_req);if (result != SL_RESULT_SUCCESS) {return false;}result = (*playerObj)->Realize(playerObj, SL_BOOLEAN_FALSE);if (result != SL_RESULT_SUCCESS) {return false;}result = (*playerObj)->GetInterface(playerObj, SL_IID_PLAY, &playerPlay);if (result != SL_RESULT_SUCCESS) {return false;}result = (*playerObj)->GetInterface(playerObj, SL_IID_BUFFERQUEUE, &playerBufferQueue);if (result != SL_RESULT_SUCCESS) {return false;}// 注册回调result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, bufferQueueCallback, this);return result == SL_RESULT_SUCCESS;
}void OpenSLPlayer::bufferQueueCallback(SLAndroidSimpleBufferQueueItf bq, void* context) {OpenSLPlayer* player = static_cast<OpenSLPlayer*>(context);// 这里可以添加缓冲区处理逻辑
}void OpenSLPlayer::start() {if (!initialized) return;std::lock_guard<std::mutex> lock(stateMutex);if (state == IDLE || state == PAUSED) {SLresult result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);if (result == SL_RESULT_SUCCESS) {state = PLAYING;if (startCallback) {startCallback(callbackContext);}} else if (errorCallback) {errorCallback("Failed to start playback", callbackContext);}}
}void OpenSLPlayer::stop() {if (!initialized) return;std::lock_guard<std::mutex> lock(stateMutex);if (state != IDLE) {SLresult result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED);if (result == SL_RESULT_SUCCESS) {state = IDLE;if (stopCallback) {stopCallback(callbackContext);}} else if (errorCallback) {errorCallback("Failed to stop playback", callbackContext);}}
}bool OpenSLPlayer::enqueueData(const void* data, size_t size) {if (!initialized || state != PLAYING) {return false;}SLresult result = (*playerBufferQueue)->Enqueue(playerBufferQueue, data, size);if (result != SL_RESULT_SUCCESS) {if (errorCallback) {errorCallback("Failed to enqueue audio data", callbackContext);}return false;}return true;
}void OpenSLPlayer::release() {std::lock_guard<std::mutex> lock(stateMutex);if (playerObj != nullptr) {(*playerObj)->Destroy(playerObj);playerObj = nullptr;playerPlay = nullptr;playerBufferQueue = nullptr;}if (outputMixObj != nullptr) {(*outputMixObj)->Destroy(outputMixObj);outputMixObj = nullptr;}if (engineObj != nullptr) {(*engineObj)->Destroy(engineObj);engineObj = nullptr;engine = nullptr;}initialized = false;state = IDLE;
}
3.3 native-lib.cpp (JNI层)
#include <jni.h>
#include <string>
#include "opensl_player.h"struct JavaCallbackContext {JavaVM* jvm;jobject javaPlayer;jmethodID startMethod;jmethodID stopMethod;jmethodID errorMethod;
};void playbackStartCallback(void* context) {JavaCallbackContext* ctx = static_cast<JavaCallbackContext*>(context);JNIEnv* env;ctx->jvm->AttachCurrentThread(&env, nullptr);env->CallVoidMethod(ctx->javaPlayer, ctx->startMethod);ctx->jvm->DetachCurrentThread();
}void playbackStopCallback(void* context) {JavaCallbackContext* ctx = static_cast<JavaCallbackContext*>(context);JNIEnv* env;ctx->jvm->AttachCurrentThread(&env, nullptr);env->CallVoidMethod(ctx->javaPlayer, ctx->stopMethod);ctx->jvm->DetachCurrentThread();
}void playbackErrorCallback(const char* message, void* context) {JavaCallbackContext* ctx = static_cast<JavaCallbackContext*>(context);JNIEnv* env;ctx->jvm->AttachCurrentThread(&env, nullptr);jstring jMessage = env->NewStringUTF(message);env->CallVoidMethod(ctx->javaPlayer, ctx->errorMethod, jMessage);env->DeleteLocalRef(jMessage);ctx->jvm->DetachCurrentThread();
}extern "C" JNIEXPORT jlong JNICALL
Java_com_example_audioplayer_AudioPlayer_nativeCreate(JNIEnv* env, jobject instance) {OpenSLPlayer* player = new OpenSLPlayer();// 设置Java回调JavaCallbackContext* context = new JavaCallbackContext();env->GetJavaVM(&context->jvm);context->javaPlayer = env->NewGlobalRef(instance);jclass clazz = env->GetObjectClass(instance);context->startMethod = env->GetMethodID(clazz, "onPlaybackStarted", "()V");context->stopMethod = env->GetMethodID(clazz, "onPlaybackStopped", "()V");context->errorMethod = env->GetMethodID(clazz, "onError", "(Ljava/lang/String;)V");player->setPlaybackCallback(playbackStartCallback,playbackStopCallback,playbackErrorCallback,context);return reinterpret_cast<jlong>(player);
}extern "C" JNIEXPORT void JNICALL
Java_com_example_audioplayer_AudioPlayer_nativeRelease(JNIEnv* env, jobject instance, jlong handle) {OpenSLPlayer* player = reinterpret_cast<OpenSLPlayer*>(handle);if (player) {JavaCallbackContext* context = static_cast<JavaCallbackContext*>(player->getCallbackContext());if (context) {env->DeleteGlobalRef(context->javaPlayer);delete context;}delete player;}
}extern "C" JNIEXPORT void JNICALL
Java_com_example_audioplayer_AudioPlayer_nativeStart(JNIEnv* env, jobject instance, jlong handle, jstring filePath) {OpenSLPlayer* player = reinterpret_cast<OpenSLPlayer*>(handle);if (player) {const char* path = env->GetStringUTFChars(filePath, nullptr);// 初始化播放器 (44100Hz, 立体声, 4096字节缓冲区)if (!player->init(44100, 2, 4096)) {env->ReleaseStringUTFChars(filePath, path);return;}// 这里应该添加从文件读取音频数据的逻辑// 简单示例: 直接开始播放player->start();env->ReleaseStringUTFChars(filePath, path);}
}extern "C" JNIEXPORT void JNICALL
Java_com_example_audioplayer_AudioPlayer_nativeStop(JNIEnv* env, jobject instance, jlong handle) {OpenSLPlayer* player = reinterpret_cast<OpenSLPlayer*>(handle);if (player) {player->stop();}
}
四、CMakeLists.txt配置
cmake_minimum_required(VERSION 3.4.1)# 设置编译选项
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra")# 查找OpenSL ES库
find_library(OPENSLES_LIB OpenSLES)# 添加native库
add_library(audioplayerSHAREDsrc/main/cpp/native-lib.cppsrc/main/cpp/opensl_player.cppsrc/main/cpp/opensl_player.h
)# 链接库
target_link_libraries(audioplayer${OPENSLES_LIB}androidlog
)# 包含目录
include_directories(src/main/cpp)
五、使用示例
5.1 MainActivity.java
package com.example.audioplayer;import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private AudioPlayer audioPlayer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);audioPlayer = new AudioPlayer();audioPlayer.setPlaybackListener(new AudioPlayer.PlaybackListener() {@Overridepublic void onPlaybackStarted() {runOnUiThread(() -> Toast.makeText(MainActivity.this, "播放开始", Toast.LENGTH_SHORT).show());}@Overridepublic void onPlaybackStopped() {runOnUiThread(() -> Toast.makeText(MainActivity.this, "播放停止", Toast.LENGTH_SHORT).show());}@Overridepublic void onPlaybackError(String message) {runOnUiThread(() -> Toast.makeText(MainActivity.this, "错误: " + message, Toast.LENGTH_LONG).show());}});Button playButton = findViewById(R.id.play_button);playButton.setOnClickListener(v -> {String audioPath = "你的音频文件路径";audioPlayer.start(audioPath);});Button stopButton = findViewById(R.id.stop_button);stopButton.setOnClickListener(v -> audioPlayer.stop());}@Overrideprotected void onDestroy() {super.onDestroy();audioPlayer.release();}
}
六、实现说明
-
初始化流程:
- 创建OpenSL ES引擎
- 创建输出混音器
- 创建音频播放器
- 设置缓冲区队列回调
-
播放控制:
start()
: 开始播放stop()
: 停止播放pause()
/resume()
: 暂停/恢复播放
-
数据管理:
enqueueData()
: 向缓冲区队列添加音频数据- 缓冲区队列回调处理
-
线程安全:
- 使用
std::mutex
保护关键状态 - 使用
std::atomic
保证状态变量的原子性
- 使用
-
内存管理:
- 在
release()
中正确释放所有OpenSL ES资源 - 在JNI层管理Java对象的全局引用
- 在
这个完整实现提供了从Java层到Native层的完整音频播放解决方案,开发者可以直接集成到项目中,或者根据需要进行修改扩展。