AudioTrackTest-opensles
OpenSL使用流程分析
OpenSL ES的API都是基于对象和接口的方式来提供的。
(1)先create Engine的ObjectItf,然后Realize Engine的ObjectItf,然后Engine的ObjectItf通过GetInterface创建EngineItf
(2)EngineItf通过CreateAudioPlayer接口来创建audio player的ObjectItf,通过CreateOutputMix接口来创建outputmix的ObjectItf
(3)audio player的ObjectItf通过GetInterface来创建bufferqueue和play Itf
整体流程:
(1)ffmpeg解析mp3,得到PCM,打包成AudioPacket,放到packet_pool中。
(2)opensl从packet_pool中读取AudioPacket,通过Enqueue函数,压进buffer中
源码分析
1. setAudioDataSource
进行ffmpeg音频解码初始化操作和openSLES初始化操作
JNIEXPORT jboolean JNICALL Java_com_example_audiotracktest_opensles_SoundTrackController_setAudioDataSource
(JNIEnv *env, jobject obj, jstring acPathParam) {
const char* acPath = env->GetStringUTFChars(acPathParam, NULL);
soundService = SoundService::GetInstance();
// 1. 初始化ffmpeg相关的东西
soundService->initSongDecoder(acPath);
// 2. 初始化OpenSLES相关的东西
SLresult result = soundService->initSoundTrack();
env->ReleaseStringUTFChars(acPathParam, acPath);
return true;
}
1. soundService->initSongDecoder
void SoundService::initSongDecoder(const char *acPath) {
LOGI("enter SoundService::initSongDecoder");
mDecoderController = new AccompanyDecoderController();
// 对ffmpeg进行初始化操作
mDecoderController->init(acPath);
// 设置buffer count的数量
this->mBufferNums = 2;
this->mCurrentFrame = 0;
// 设置一个包的buffer大小,channel * sample rate * bytes per sample
mPacketBufferSize = 2 * 44100 * 2;
mBufferSize = mPacketBufferSize * mBufferNums;
}
2. soundService->initSoundTrack
SLresult SoundService::initSoundTrack() {
LOGI("enter SoundService::initSoundTrack");
SLresult result;
OpenSLESContext* openSLESContext = OpenSLESContext::GetInstance();
// 1. 创建engine对象
mEngine = openSLESContext->getEngine();
// 2. 创建outputmix对象
result = CreateOutputMix();
if (SL_RESULT_SUCCESS != result) {
return result;
}
// 3. 实例化outputmix对象
result = RealizeObject(mOutputMixObject);
if (SL_RESULT_SUCCESS != result) {
return result;
}
// 4. 创建两个buffer,mBuffer和mTarget
InitPlayerBuffer();
// 5. 创建AudioPlayer Itf
result = CreateBufferQueueAudioPlayer();
if (SL_RESULT_SUCCESS != result) {
return result;
}
// 6. 实例化AudioPlayer对象
result = RealizeObject(mAudioPlayerObject);
if (SL_RESULT_SUCCESS != result) {
return result;
}
// 7. 获取AudioPlayer对象的接口
result = GetAudioPlayerBufferQueueInterface();
if (SL_RESULT_SUCCESS != result) {
return result;
}
// 8. 注册player的回调
result = RegisterPlayerCallback();
if (SL_RESULT_SUCCESS != result) {
return result;
}
// 9. 获取audioplayer的接口
result = GetAudioPlayerInterface();
if (SL_RESULT_SUCCESS != result) {
return result;
}
LOGI("leave init");
return SL_RESULT_SUCCESS;
}
OpenSLES的对象初始化的过程:
(1)创建SLObjectItf(create)
(2)实例化SLObjectItf(Realize)
(3)通过实例化后的对象的函数GetInterface来获取接口/其他对象(GetInterface)
1. 初始化engine
1. 创建SLObjectItf mEngineObject对象
slCreateEngine(&mEngineObject, ARRAY_LEN(engineOptions),engineOptions,0,0,0);
2. 实例化SLObjectItf mEngineObject对象
(*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE)
3. 通过mEngineObject获取SLEngineItf mEngine对象
(*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngine);
2. 初始化outputmix
1. 通过mEngine对象来创建SLObjectItf mOutputMixObject
(*mEngine)->CreateOutputMix(mEngine, &mOutputMixObject, 0, 0, 0);
2. 实例化SLObjectItf mOutputMixObject对象
3. 初始化audioplayer
1. 通过mEngine对象来创建SLObjectItf mAudioPlayerObject
SLresult CreateBufferQueueAudioPlayer() {
SLDataLocator_AndroidSimpleBufferQueue dataSourceLocator = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locator type
// 注意,这里设置的数字应该和后面play函数中调用的Enqueue函数的次数一致
// 不然的话,就会出现SL_RESULT_BUFFER_INSUFFICIENT错误。
1 // buffer count
};
SLDataFormat_PCM dataSourceFormat = {
SL_DATAFORMAT_PCM, // format type
2, // channel count
SL_SAMPLINGRATE_44_1, // samples per second in millihertz
SL_PCMSAMPLEFORMAT_FIXED_16, // bits per sample
SL_PCMSAMPLEFORMAT_FIXED_16, // container size
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, // channel mask
SL_BYTEORDER_LITTLEENDIAN // endianness
};
SLDataSource dataSource = {
&dataSourceLocator,
&dataSourceFormat
};
SLDataLocator_OutputMix dataSinkLocator = {
SL_DATALOCATOR_OUTPUTMIX,
mOutputMixObject
};
SLDataSink dataSink = {
&dataSinkLocator,
0
};
SLInterfaceID interfaceIds[] = { SL_IID_BUFFERQUEUE };
SLboolean requiredInterfaces[] { SL_BOOLEAN_TRUE };
return (*mEngine)->CreateAudioPlayer(mEngine, &mAudioPlayerObject,
&dataSource, &dataSink, ARRAY_LEN(interfaceIds),
interfaceIds, requiredInterfaces);
};
(1)配置SLDataSource
<1> 配置SLDataLocator_AndroidSimpleBufferQueue buffer类型和数量
<2> 配置SLDataFormat_PCM PCM的类型,通道数,采样率,定点,扬声器,小端模式
(2)配置SLDataSink:就是音频输出
(3)需要请求的接口:SLInterfaceID SL_IID_BUFFERQUEUE为缓冲队列,SL_IID_VOLUME为音量;然后对应的requiredInterfaces设为SL_BOOLEAN_TRUE
2. 实例化SLObjectItf mAudioPlayerObject对象
3. 通过mAduioPlayerObject对象来获取SLPlayItf mAudioPlayerPlay对象
4. 通过mAduioPlayerObject对象来获取SLAndroidSimpleBufferQueueItf mAudioPlayerBufferQueue对象
(*mAudioPlayerObject)->GetInterface(mAudioPlayerObject, SL_IID_BUFFERQUEUE,&mAudioPlayerBufferQueue);
综上看:mAudioPlayerPlay对象和mAudioPlayerBufferQueue对象隶属于mAudioPlayerObject对象
4. 注册player的回调
1. 通过mAudioPlayerBufferQueue对象来注册PlayerCallback回调函数,来向buffer中填充数据
(*mAudioPlayerBufferQueue)->RegisterCallback(mAudioPlayerBufferQueue, PlayerCallback, this);
2. play开始播放
JNIEXPORT void JNICALL Java_com_example_audiotracktest_opensles_SoundTrackController_play
(JNIEnv * env, jobject obj) {
if (NULL != soundService) {
// 1. 开始播放
soundService->play();
}
}
1. play开始播放
SLresult SoundService::play() {
LOGI("enter SoundService::play()...");
// 1. 设置AudioPlayer的状态为playing
// (*mAudioPlayerPlay)->SetPlayState(mAudioPlayerPlay, SL_PLAYSTATE_PLAYING);
SLresult result = SetAudioPlayerStatePlaying();
if (SL_RESULT_SUCCESS != result) {
return result;
}
mPlayingState = PLAYING_STATE_PLAYING;
int ret = 0;
if (NULL != mDecoderController) {
/*for (int j = 0; j < 33; j++) {
int pSize = -1;
pSize = mDecoderController->readSamples(mTarget + ret, mPacketBufferSize/2, NULL);
if (0 < pSize) {
ret += pSize;
} else {
break;
}
LOGI("ret = %d", ret);
} */
// 2. 从ffmpeg中读取数据
ret = mDecoderController->readSamples(mTarget + ret, mPacketBufferSize/2, NULL);
}
if (0 < ret) {
convertByteArrayFromShortArray(mTarget, ret, mBuffer);
// 3. 然后将数据通过AudioPlayerBufferQueue填充到buffer中
(*mAudioPlayerBufferQueue)->Enqueue(mAudioPlayerBufferQueue, mBuffer, ret * 2);
}
LOGI("out SoundService::play()...");
// 4. 然后play函数进入到死循环中,不能退出,退出应用就会闪退了。
while(mPlayingState != PLAYING_STATE_STOPPED) {};
}
2. producePacket回调获取buffer数据
void SoundService::producePacket() {
LOGI("SoundService::producePacket() audio player call back method...");
byte *audioBuffer = mBuffer + (mCurrentFrame % mBufferNums) * mPacketBufferSize;
int result = 0;
if (NULL != mDecoderController) {
/*for (int j = 0; j < 33; j++) {
int pSize = -1;
pSize = mDecoderController->readSamples(mTarget + result, mPacketBufferSize/2, NULL);
if (0 < pSize) {
result += pSize;
} else {
break;
}
} */
// 读取数据
result = mDecoderController->readSamples(mTarget + result, mPacketBufferSize/2, NULL);
LOGI("enter SoundService::producePacket() PLAYING_STATE_PLAYING packetBufferSize=%d, result=%d", mPacketBufferSize, result);
}
if (0 < result) {
convertByteArrayFromShortArray(mTarget, result, audioBuffer);
// 入栈
(*mAudioPlayerBufferQueue)->Enqueue(mAudioPlayerBufferQueue, audioBuffer, result * 2);
}
mCurrentFrame = (mCurrentFrame + 1) % mBufferNums;
}
3. stop停止播放
JNIEXPORT void JNICALL Java_com_example_audiotracktest_opensles_SoundTrackController_stop
(JNIEnv * env, jobject obj) {
if (NULL != soundService) {
// 1. 停止播放
soundService->stop();
soundService = NULL;
}
}
1. stop停止播放
SLresult SoundService::stop() {
LOGI("enter SoundService::stop()");
mPlayingState = PLAYING_STATE_STOPPED;
LOGI("Set the audio player state paused");
// 1. 设置audioplayer的状态为stoped
// (*mAudioPlayerPlay)->SetPlayState(mAudioPlayerPlay, SL_PLAYSTATE_PAUSED);
SLresult result = SetAudioPlayerStateStoped();
if (SL_RESULT_SUCCESS != result) {
LOGI("Set the audio player state paused return false");
return result;
}
// 2. 销毁object
DestroyContext();
LOGI("out SoundService::stop()");
}
void SoundService::DestroyContext() {
LOGI("enter SoundService::DestroyContext");
DestroyObject(mAudioPlayerObject);
FreePlayerBuffer();
DestroyObject(mOutputMixObject);
if (NULL != mDecoderController) {
mDecoderController->destroy();
delete mDecoderController;
mDecoderController = NULL;
}
LOGI("leave SoundService::DestroyContext");
}
问题
1. SL_RESULT_BUFFER_INSUFFICIENT错误
SLDataLocator_AndroidSimpleBufferQueue配置的buffer数和play的时候Enqueue的buffer数目对不上
2. play函数中出现段错误
play函数不应该退出,不然就会出现段错误。在最后面加个while死循环。
参考
(1)Android音频开发(7):使用 OpenSL ES API(下):
https://blog.51cto.com/ticktick/1771239
(2)Android Open SL ES — 官方Demo解析native-audio
https://www.jianshu.com/p/5308ab38a3d0
(3)源码位置:https://github.com/mashenlyl/AudioTrackTest