FFmpeg的player之decoder_decode_frame()

3 篇文章 0 订阅
2 篇文章 0 订阅

目录

一、大致逻辑如下:

 二、发送失败重新发送packet


这个函数顾名思义:解码

一、大致逻辑如下:

1、从video_queue里获取packet,:packet_queue_get();

2、发送packet给decoder :decoder_send_packer();

3、从decoder去获取,:decoder_receive_frame();

这个看代码时候可能顺序不一样,这个是因为一些工程的原因:

比如:

//流连续的情况下,不断调用avcodec_receive_frame获取解码后的frame;
循环调用avcodec_receive_frame,有取到帧,就返回。
(即使还没送入新的Packet,这是为了兼容一个Packet可以解出多个Frame的情况)
    // 3*. 从解码器接收frame
    // 3.1 一个视频packet含一个视频frame
    //     解码器缓存一定数量的packet后,才有解码后的frame输出
    //     frame输出顺序是按pts的顺序,如IBBPBBP
    //     frame->pkt_pos变量是此frame对应的packet在视频文件中的偏移地址,值同pkt.pos

还比如:

// 2*. 将packet发送给解码器
//    发送packet的顺序是按dts递增的顺序,如IPBBPBB
//    pkt.pos变量可以标识当前packet在视频文件中的地址偏移
//[4]解码器中会缓存一定数量的帧,一个新的解码动作启动后,向解码器送入好几个packet解码器才会输出
第一个packet,这比较容易理解,因为解码时帧之 间有信赖关系,例如IPB三个帧被送入解码器后,
B帧解码需要依赖I帧和P帧,所在在B帧输出前,I帧和P帧必须存在于解码器中而不能删除。理解了这一点,
 后面视频frame队列中对视频帧的显示和删除机制才容易理解。
                //[5]. 解码器中缓存的帧可以通过冲洗(flush)解码器取出。冲洗(flush)解码器的方法
就是调用avcodec_send_packet(..., NULL),然后多次调用avcodec_receive_frame()将缓存帧取尽。
缓存帧取完后,avcodec_receive_frame()返回AVERROR_EOF。ffplay中,是通过向解码器发送flush_pkt
(实际为NULL),每次seek操作都会向解码器发送flush_pkt。
     

下面是一个比较形象的流程图,

 

 二、发送失败重新发送packet

 肯定会遇到发送失败packet的情况,怎么处理呢?

答案:决不能丢掉,保存下来等下次再发,

用到一个函数:av_packet_move_ref(&d->pkt, &pkt);

这个是复制操作,将&pkt指针给&d->pkt,内存里面保存AVbuffer的内容没有改变,

//av_packet_move_ref这个函数就是完全的只复制,source的值完全的搬到destination,并且把source重置掉。其实就是搬了个位置,buf的引用数不改变。

为什么做这个操作,

看ffplayer的发送失败保存的代码:

if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {
                    av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
                    d->packet_pending = 1;//用于在send失败时重新发送
                    //这个是复制操作,将&pkt指针给&d->pkt,内存里面保存AVbuffer的内容没有改变,这个AVbuffer会再下次继续发送,和上面if (d->packet_pending)对应的。
                    av_packet_move_ref(&d->pkt, &pkt);
                }

 看再发送的时候,如果发送失败,会重新保存这次的packet到一个全局变量&d->pkt内,然后在下次循环中不再取数据,直接send_packet,

//从packet_queue取packet
        do {
            if (d->queue->nb_packets == 0)// 如果packet_queue为空则等待
                SDL_CondSignal(d->empty_queue_cond);
            
            //如果有待重发的pkt,则先取待重发的pkt,否则从队列中取一个pkt
            if (d->packet_pending) {// 有未处理的packet则先处理
                // 把上次发送失败的,且存放在&d->pkt的packet复制给&pkt, 不再从video_queue中get数据,
                av_packet_move_ref(&pkt, &d->pkt);
                d->packet_pending = 0;
            } else {
                // 1*. 取出一个packet。使用pkt对应的serial赋值给d->pkt_serial
                if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)// 取pkt
                    return -1;
            }
        } while (d->queue->serial != d->pkt_serial);

 

实现了

[ffmpeg] 解码API - TaigaComplex - 博客园

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值