FFmpeg视频播放(音视频解码)

本文详细介绍了FFmpeg在Android上的视频播放过程,包括音频和视频的解码、渲染以及播放。通过创建线程进行解码和渲染,使用SafeQueue进行同步。视频解码后将frame数据传递给SurfaceView,音频则采用OpenSL ES进行播放。文章还提到了音视频同步问题,并预告了后续将探讨解决同步问题的方法。
摘要由CSDN通过智能技术生成

上一篇中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的生产者与消费者的问题处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值