ffmpeg函数分析:av_read_frame()

对于视频的编解码来说,要对数据进行解码,那么首先要获取视频帧的压缩数据。
av_read_frame()的作用就是获取视频的数据
注:av_read_frame()获取视频的一帧,不存在半帧说法。但可以获取音频的若干帧。
说明①:av_read_frame()函数是ffmpeg新型的用法,就用法之所以被抛弃,就是因为以前获取的数据可能不是完整的,而av_read_frame()保证了视频数据一帧的完整性。
说明②:查看API的改变可以看到,从2012-03-20开始,Deprecate av_read_packet(), use av_read_frame()

返回流的下一帧。
*此函数返回存储在文件中的内容,但不验证解码器是否有有效帧。
它将把文件中存储的内容拆分为帧,并为每个调用返回一个帧。
它不会省略有效帧之间的无效数据,以便给解码器最大可能的解码信息。
如果pkt->buf为NULL,那么直到下一个av_read_frame()或直到avformat_close_input(),包都是有效的。
否则数据包将无限期有效。在这两种情况下,当不再需要包时,必须使用av_free_packet释放包。
对于视频,数据包只包含一帧。
对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数帧数。
如果音频帧有一个可变的大小(例如MPEG音频),那么它包含一帧。
在AVStream中,pkt->pts、pkt->dts和pkt->持续时间总是被设置为恰当的值。
time_base单元(猜测格式是否不能提供它们)。
如果视频格式为B-frames,pkt->pts可以是AV_NOPTS_VALUE,所以如果不解压缩有效负载,最好依赖pkt->dts。

int av_read_frame(AVFormatContext *s, AVPacket *pkt);
参数说明:
AVFormatContext *s   // 文件格式上下文,输入的AVFormatContext
AVPacket *pkt   // 这个值不能传NULL,必须是一个空间,输出的AVPacket
            // 返回值:return 0 is OK, <0 on error or end of file

av_read_frame()函数源码位于libavformat\utils.c

int av_read_frame(AVFormatContext *s, AVPacket *pkt)
{
    const int genpts = s->flags & AVFMT_FLAG_GENPTS;
    int eof = 0;
    int ret;
    AVStream *st;

    if (!genpts) {
        ret = s->internal->packet_buffer
              ? ff_packet_list_get(&s->internal->packet_buffer,
                                        &s->internal->packet_buffer_end, pkt)
              : read_frame_internal(s, pkt);
        if (ret < 0)
            return ret;
        goto return_packet;
    }

    for (;;) {
        AVPacketList *pktl = s->internal->packet_buffer;

        if (pktl) {
            AVPacket *next_pkt = &pktl->pkt;

            if (next_pkt->dts != AV_NOPTS_VALUE) {
                int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;
                // last dts seen for this stream. if any of packets following
                // current one had no dts, we will set this to AV_NOPTS_VALUE.
                int64_t last_dts = next_pkt->dts;
                av_assert2(wrap_bits <= 64);
                while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {
                    if (pktl->pkt.stream_index == next_pkt->stream_index &&
                        av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2ULL << (wrap_bits - 1)) < 0) {
                        if (av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2ULL << (wrap_bits - 1))) {
                            // not B-frame
                            next_pkt->pts = pktl->pkt.dts;
                        }
                        if (last_dts != AV_NOPTS_VALUE) {
                            // Once last dts was set to AV_NOPTS_VALUE, we don't change it.
                            last_dts = pktl->pkt.dts;
                        }
                    }
                    pktl = pktl->next;
                }
                if (eof && next_pkt->pts == AV_NOPTS_VALUE && last_dts != AV_NOPTS_VALUE) {
                    // Fixing the last reference frame had none pts issue (For MXF etc).
                    // We only do this when
                    // 1. eof.
                    // 2. we are not able to resolve a pts value for current packet.
                    // 3. the packets for this stream at the end of the files had valid dts.
                    next_pkt->pts = last_dts + next_pkt->duration;
                }
                pktl = s->internal->packet_buffer;
            }

            /* read packet from packet buffer, if there is data */
            st = s->streams[next_pkt->stream_index];
            if (!(next_pkt->pts == AV_NOPTS_VALUE && st->discard < AVDISCARD_ALL &&
                  next_pkt->dts != AV_NOPTS_VALUE && !eof)) {
                ret = ff_packet_list_get(&s->internal->packet_buffer,
                                               &s->internal->packet_buffer_end, pkt);
                goto return_packet;
            }
        }

        ret = read_frame_internal(s, pkt);
        if (ret < 0) {
            if (pktl && ret != AVERROR(EAGAIN)) {
                eof = 1;
                continue;
            } else
                return ret;
        }

        ret = ff_packet_list_put(&s->internal->packet_buffer,
                                 &s->internal->packet_buffer_end,
                                 pkt, FF_PACKETLIST_FLAG_REF_PACKET);
        av_packet_unref(pkt);
        if (ret < 0)
            return ret;
    }

return_packet:

    st = s->streams[pkt->stream_index];
    if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && pkt->flags & AV_PKT_FLAG_KEY) {
        ff_reduce_index(s, st->index);
        av_add_index_entry(st, pkt->pos, pkt->dts, 0, 0, AVINDEX_KEYFRAME);
    }

    if (is_relative(pkt->dts))
        pkt->dts -= RELATIVE_TS_BASE;
    if (is_relative(pkt->pts))
        pkt->pts -= RELATIVE_TS_BASE;

    return ret;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值