上一篇中FFmpeg解封装中在TinaFFmpeg中的prepare方法里把解码器上下文AVCodecContext交给VideoChannel,AudioChannel后,解码工作就交给它们来处理了,这节我们来看它们是如何处理的。
解码入口
prepare完成后会调用Java中TinaPlayer的onPrepare的方法,然后回调 PlayActivity的start方法,然后进入native层的start方法:
LOGE("native prepare流程准备完毕");
// 准备完了 通知java 你随时可以开始播放
callHelper->onPrepare(THREAD_CHILD);
//prepare完成后会调用Java中TinaPlayer的onPrepare的方法,然后回调 PlayActivity的start方法,然后进入native层的start方法
//TinaPlayer
public void onPrepare(){
if (null != listener){
listener.onPrepare();
}
}
//PlayActivity
tinaPlayer.setOnPrepareListener(new TinaPlayer.OnPrepareListener() {
@Override
public void onPrepare() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText("开始播放").show();
}
});
//调用native_start
tinaPlayer.start();
}
});
//TinaPlayer
public void start(){
native_start();
}
native层:调用ffmpeg中的start方法,然后分别调用videoChannel、audioChannel的play()方法
extern "C"
JNIEXPORT void JNICALL
Java_tina_com_player_TinaPlayer_native_1start(JNIEnv *env, jobject instance) {
ffmpeg->start();
}
//TinaFFmpeg
void TinaFFmpeg::start() {
//重新开线程
isPlaying = 1;
if (audioChannel) {
//设置为工作状态
audioChannel->play();
}
if (videoChannel) {
//设置为工作状态
videoChannel->setAudioChannel(audioChannel);
videoChannel->play();
}
pthread_create(&pid_play, 0, play, this);
}
兵马未动,粮草先行。把解码需要的数据源packet放入到SafeQueue的同步队列中去:
void *play(void *args) {
TinaFFmpeg *fFmpeg = static_cast<TinaFFmpeg *>(args);
fFmpeg->_start();
return 0;
}
void TinaFFmpeg::_start() {
int ret;
//1.读取媒体数据包(音频、视频数据)
while (isPlaying) {
//读取文件的时候没有网络请求,一下子读完了,可能导致oom
//特别是读本地文件的时候 一下子就读完了
if (audioChannel && audioChannel->packets.size() > 100) {
//10ms
av_usleep(1000 * 10);
continue;
}
if (videoChannel && videoChannel->packets.size() > 100) {
av_usleep(1000 * 10);
continue;
}
AVPacket *avPacket = av_packet_alloc();
ret = av_read_frame(formatContext, avPacket);
//等于0成功,其它失败
if (ret == 0) {
if (audioChannel && avPacket->stream_index == audioChannel->id) {
//todo 音频
//在audioChannel中执行 解码工作
audioChannel->packets.push(avPacket);
} else if (videoChannel && avPacket->stream_index == videoChannel->id) {
//在videoChannel中执行 解码工作
videoChannel->packets.push(avPacket);
}
} else if (ret == AVERROR_EOF) {
//读取完成,但是可能还没有播放完成
if (audioChannel->packets.empty() && audioChannel->frames.empty()
&& videoChannel->packets.empty() && videoChannel->frames.empty()){
av_packet_free(&avPacket);//这里不释放会有内存泄漏,崩溃
break;
}
} else {
av_packet_free(&avPacket);
break;
}
}
isPlaying = 0;
audioChannel->stop();
videoChannel->stop();
}
其实push队列跟audioChannel、videoChannel的play()的过程是同步的,是基于SafeQueue对象packets的生产者与消费者的问题处