这个是得到jvm需要的方法
extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {jint result = -1;//得到jvmjavaVM = vm;JNIEnv *env;if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {return result;}return JNI_VERSION_1_4;}
这里主要是加载文件,加载失败会把状态回调到Java层
void WlFFmpeg::decodeFFmpegThread() {//加锁pthread_mutex_lock(&init_mutex);//必要步骤av_register_all();//必要步骤avformat_network_init();//得到AVFormatContextpFormatCtx = avformat_alloc_context();//设置超时回调接口pFormatCtx->interrupt_callback.callback = avformat_callback;pFormatCtx->interrupt_callback.opaque = this;//打开文件if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {//打开失败if (LOG_DEBUG) {LOGE("can not open url :%s", url);}//把打开失败的状态回调给Java层callJava->onCallError(CHILD_THREAD, 1001, "can not open url");//退出状态激活exit = true;//解锁pthread_mutex_unlock(&init_mutex);return;}//打开文件成功,接下来寻找流if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {//寻找流失败if (LOG_DEBUG) {LOGE("can not find streams from %s", url);}//把寻找流失败通知Java层callJava->onCallError(CHILD_THREAD, 1002, "can not find streams from url");//推出状态激活exit = true;//解锁pthread_mutex_unlock(&init_mutex);return;}//寻找确定的音频流for (int i = 0; i < pFormatCtx->nb_streams; i++) {//找到音频流if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)//得到音频流{if (audio == NULL) {//初始化WlAudioaudio = new WlAudio(playstatus, pFormatCtx->streams[i]->codecpar->sample_rate,callJava);//把流的信息写入WlAudioaudio->streamIndex = i;audio->codecpar = pFormatCtx->streams[i]->codecpar;audio->duration = pFormatCtx->duration / AV_TIME_BASE;audio->time_base = pFormatCtx->streams[i]->time_base;}}}//得到解码器AVCodec *dec = avcodec_find_decoder(audio->codecpar->codec_id);if (!dec) {//没有解码器if (LOG_DEBUG) {LOGE("can not find decoder");}//把失败信息通知Java层callJava->onCallError(CHILD_THREAD, 1003, "can not find decoder");//推出状态激活exit = true;//解锁pthread_mutex_unlock(&init_mutex);return;}//把AVCodecContext赋值给WLAUDIOaudio->avCodecContext = avcodec_alloc_context3(dec);if (!audio->avCodecContext) {//失败if (LOG_DEBUG) {LOGE("can not alloc new decodecctx");}//通知Java层callJava->onCallError(CHILD_THREAD, 1004, "can not alloc new decodecctx");//推出状态激活exit = true;//解锁pthread_mutex_unlock(&init_mutex);return;}//必要步骤if (avcodec_parameters_to_context(audio->avCodecContext, audio->codecpar) < 0) {//失败if (LOG_DEBUG) {LOGE("can not fill decodecctx");}callJava->onCallError(CHILD_THREAD, 1005, "ccan not fill decodecctx");exit = true;pthread_mutex_unlock(&init_mutex);return;}//必要步骤if (avcodec_open2(audio->avCodecContext, dec, 0) != 0) {//失败if (LOG_DEBUG) {LOGE("cant not open audio strames");}callJava->onCallError(CHILD_THREAD, 1006, "cant not open audio strames");exit = true;pthread_mutex_unlock(&init_mutex);return;}//打开文件成功,通知Java层if (callJava != NULL) {if (playstatus != NULL && !playstatus->exit) {callJava->onCallParpared(CHILD_THREAD);} else {exit = true;}}//解锁pthread_mutex_unlock(&init_mutex);
}
加载成功就调用start方法,这个方法主要是把数据从文件中取出放入队列,再在子线程中开始加载opensl,进行音频播放
void WlFFmpeg::start() {//因为在加载中初始化了audio,所以这里有个检查if (audio == NULL) {return;}//播放,设置openslaudio->play();//读取数据,把数据从文件写入队列while (playstatus != NULL && !playstatus->exit) {AVPacket *avPacket = av_packet_alloc();//这里读取文件if (av_read_frame(pFormatCtx, avPacket) == 0) {if (avPacket->stream_index == audio->streamIndex) {//写入队列audio->queue->putAvpacket(avPacket);} else {av_packet_free(&avPacket);av_free(avPacket);}} else {av_packet_free(&avPacket);av_free(avPacket);while (playstatus != NULL && !playstatus->exit) {if (audio->queue->getQueueSize() > 0) {continue;} else {playstatus->exit = true;break;}}break;}}exit = true;if (LOG_DEBUG) {LOGD("解码完成");}
}
这是初始化opensles,进行播放的准备
void WlAudio::initOpenSLES() {SLresult result;result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);//第二步,创建混音器const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);(void)result;result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);(void)result;result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSnk = {&outputMix, 0};// 第三步,配置PCM格式信息SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};SLDataFormat_PCM pcm={SL_DATAFORMAT_PCM,//播放pcm格式的数据2,//2个声道(立体声)static_cast<SLuint32>(getCurrentSampleRateForOpensles(sample_rate)),//44100hz的频率SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)SL_BYTEORDER_LITTLEENDIAN//结束标志};SLDataSource slDataSource = {&android_queue, &pcm};const SLInterfaceID ids[1] = {SL_IID_BUFFERQUEUE};const SLboolean req[1] = {SL_BOOLEAN_TRUE};(*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 1, ids, req);//初始化播放器(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);// 得到接口后调用 获取Player接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);// 注册回调缓冲区 获取缓冲队列接口(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);//缓冲接口回调(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
// 获取播放状态接口(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);pcmBufferCallBack(pcmBufferQueue, this);}
这是opensles的声明
// 引擎接口SLObjectItf engineObject = NULL;SLEngineItf engineEngine = NULL;//混音器SLObjectItf outputMixObject = NULL;SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;//pcmSLObjectItf pcmPlayerObject = NULL;SLPlayItf pcmPlayerPlay = NULL;//缓冲器队列接口SLAndroidSimpleBufferQueueItf pcmBufferQueue = NULL;
opensl回调的接口,主要就是回调这个接口进行音频播放
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{WlAudio *wlAudio = (WlAudio *) context;if(wlAudio != NULL){int buffersize = wlAudio->resampleAudio();if(buffersize > 0){wlAudio->clock += buffersize / ((double)(wlAudio->sample_rate * 2 * 2));if(wlAudio->clock - wlAudio->last_tiem >= 0.1){wlAudio->last_tiem = wlAudio->clock;//回调应用层wlAudio->callJava->onCallTimeInfo(CHILD_THREAD, wlAudio->clock, wlAudio->duration);}(* wlAudio-> pcmBufferQueue)->Enqueue( wlAudio->pcmBufferQueue, (char *) wlAudio-> buffer, buffersize);}}
}
回调借口调用这个,获得播放数据
int WlAudio::resampleAudio() {data_size = 0;while(playstatus != NULL && !playstatus->exit){if(queue->getQueueSize() == 0)//加载中{if(!playstatus->load){playstatus->load = true;callJava->onCallLoad(CHILD_THREAD, true);}continue;} else{if(playstatus->load){playstatus->load = false;callJava->onCallLoad(CHILD_THREAD, false);}}avPacket = av_packet_alloc();if(queue->getAvpacket(avPacket) != 0){av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;continue;}ret = avcodec_send_packet(avCodecContext, avPacket);if(ret != 0){av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;continue;}avFrame = av_frame_alloc();ret = avcodec_receive_frame(avCodecContext, avFrame);if(ret == 0){if(avFrame->channels && avFrame->channel_layout == 0){avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);}else if(avFrame->channels == 0 && avFrame->channel_layout > 0){avFrame->channels = av_get_channel_layout_nb_channels(avFrame->channel_layout);}SwrContext *swr_ctx;swr_ctx = swr_alloc_set_opts(NULL,AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16,avFrame->sample_rate,avFrame->channel_layout,(AVSampleFormat) avFrame->format,avFrame->sample_rate,NULL, NULL);if(!swr_ctx || swr_init(swr_ctx) <0){av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;av_frame_free(&avFrame);av_free(avFrame);avFrame = NULL;swr_free(&swr_ctx);continue;}int nb = swr_convert(swr_ctx,&buffer,avFrame->nb_samples,(const uint8_t **) avFrame->data,avFrame->nb_samples);int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);data_size = nb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);now_time = avFrame->pts * av_q2d(time_base);if(now_time < clock){now_time = clock;}clock = now_time;av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;av_frame_free(&avFrame);av_free(avFrame);avFrame = NULL;swr_free(&swr_ctx);break;} else{av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;av_frame_free(&avFrame);av_free(avFrame);avFrame = NULL;continue;}}return data_size;
}
这是一个方法类
int WlAudio::getCurrentSampleRateForOpensles(int sample_rate) {int rate = 0;switch (sample_rate){case 8000:rate = SL_SAMPLINGRATE_8;break;case 11025:rate = SL_SAMPLINGRATE_11_025;break;case 12000:rate = SL_SAMPLINGRATE_12;break;case 16000:rate = SL_SAMPLINGRATE_16;break;case 22050:rate = SL_SAMPLINGRATE_22_05;break;case 24000:rate = SL_SAMPLINGRATE_24;break;case 32000:rate = SL_SAMPLINGRATE_32;break;case 44100:rate = SL_SAMPLINGRATE_44_1;break;case 48000:rate = SL_SAMPLINGRATE_48;break;case 64000:rate = SL_SAMPLINGRATE_64;break;case 88200:rate = SL_SAMPLINGRATE_88_2;break;case 96000:rate = SL_SAMPLINGRATE_96;break;case 192000:rate = SL_SAMPLINGRATE_192;break;default:rate = SL_SAMPLINGRATE_44_1;}return rate;
}
释放内存
void WlAudio::release() {
//opensl是放在audio里的,这里释放一下,opensles提供专门的API进行释放if(queue != NULL){delete(queue);queue = NULL;}if(pcmPlayerObject != NULL){(*pcmPlayerObject)->Destroy(pcmPlayerObject);pcmPlayerObject = NULL;pcmPlayerPlay = NULL;pcmBufferQueue = NULL;}if(outputMixObject != NULL){(*outputMixObject)->Destroy(outputMixObject);outputMixObject = NULL;outputMixEnvironmentalReverb = NULL;}if(engineObject != NULL){(*engineObject)->Destroy(engineObject);engineObject = NULL;engineEngine = NULL;}if(buffer != NULL){free(buffer);buffer = NULL;}if(avCodecContext != NULL){avcodec_close(avCodecContext);avcodec_free_context(&avCodecContext);avCodecContext = NULL;}if(playstatus != NULL){playstatus = NULL;}if(callJava != NULL){callJava = NULL;}}
改变播放状态
void WlAudio::stop() {if(pcmPlayerPlay != NULL){(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);}
}void WlAudio::pause() {if(pcmPlayerPlay != NULL){//直接操纵opensl,设置 暂停(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);}
}void WlAudio::resume() {if(pcmPlayerPlay != NULL){//直接操纵opensl,设置播放(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);}
}
这是一个通知Java层,错误信息的方法
void WlCallJava::onCallError(int type, int code, char *msg) {if (type == MAIN_THREAD) {jstring jmsg = jniEnv->NewStringUTF(msg);jniEnv->CallVoidMethod(jobj, jmid_error, code, jmsg);jniEnv->DeleteLocalRef(jmsg);} else if (type == CHILD_THREAD) {JNIEnv *jniEnv;if (javaVM->AttachCurrentThread(&jniEnv, 0) != JNI_OK) {if (LOG_DEBUG) {LOGE("call onCallTimeInfo worng");}return;}jstring jmsg = jniEnv->NewStringUTF(msg);jniEnv->CallVoidMethod(jobj, jmid_error, code, jmsg);jniEnv->DeleteLocalRef(jmsg);javaVM->DetachCurrentThread();}
}
队列里的方法
int WlQueue::putAvpacket(AVPacket *packet) {
//把数据放入队列pthread_mutex_lock(&mutexPacket);queuePacket.push(packet);pthread_cond_signal(&condPacket);pthread_mutex_unlock(&mutexPacket);return 0;
}int WlQueue::getAvpacket(AVPacket *packet) {//把数据取出队列pthread_mutex_lock(&mutexPacket);while (playstatus != NULL && !playstatus->exit) {if (queuePacket.size() > 0) {AVPacket *avPacket = queuePacket.front();if (av_packet_ref(packet, avPacket) == 0) {queuePacket.pop();}av_packet_free(&avPacket);av_free(avPacket);avPacket = NULL;break;} else {pthread_cond_wait(&condPacket, &mutexPacket);}}pthread_mutex_unlock(&mutexPacket);return 0;
}int WlQueue::getQueueSize() {int size = 0;pthread_mutex_lock(&mutexPacket);size = queuePacket.size();pthread_mutex_unlock(&mutexPacket);return size;
}void WlQueue::clearAvpacket() {pthread_cond_signal(&condPacket);pthread_mutex_unlock(&mutexPacket);//清空队列while (!queuePacket.empty()) {AVPacket *packet = queuePacket.front();queuePacket.pop();av_packet_free(&packet);av_free(packet);packet = NULL;}pthread_mutex_unlock(&mutexPacket);}