ffmpeg之av_read_frame

av_read_frame
主要用到了如下函数。 read_from_packet_buffer,read_frame_internal(ff_read_packet(probe_codec),parse_packet(av_parser_parse2)。


与av_read_packet的区别是读出的是包,它可能是半帧或多帧,不保证帧的完整性。av_read_frame对av_read_packet进行了封装,使读出的数据总是完整的帧。
read_from_packet_buffer
read_from_packet_buffer函数比较简单,从AVPacketList里取出数据即可。若缓存中没有数据,av_read_frame函数则会调用read_frame_internal函数来获取数据。
read_frame_internal
read_frame_internal主要有以下两步,
(1)调用了ff_read_packet()从相应的AVInputFormat读取数据,如解析出TS的PES,一个PES包中可能有多个音频帧。

(2)如果媒体频流需要使用AVCodecParser,则调用parse_packet()解析相应的AVPacket,如调用h264_parser.c解析出一帧H264。

static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{
    int ret = 0, i, got_packet = 0;
    AVDictionary *metadata = NULL;
    av_init_packet(pkt);

    while (!got_packet && !s->internal->parse_queue) {

        //parse_packet成功后,s->internal->parse_queue就不为空。

        AVStream *st;

        AVPacket cur_pkt;

        。。。

    }

   //退出循环后,调用read_from_packet_buffer,从parse_queue中取出数据。

   if (!got_packet && s->internal->parse_queue)
        ret = read_from_packet_buffer(&s->internal->parse_queue, &s->internal->parse_queue_end, pkt);

}

ff_read_packet
ff_read_packet函数也是会判断缓存是否有数据,若有则从缓存中取出,若没有,则调用demuxer的read_packet来读取数据。ff_read_packet()中最关键的地方就是调用了AVInputFormat的read_packet()方法。AVInputFormat的read_packet()是一个函数指针,指向当前的AVInputFormat的读取数据的函数,比如flv的flv_read_packet,flv_read_packet()的代码比较长,但是逻辑比较简单。它的主要功能就是根据FLV文件格式的规范,逐层解析Tag以及TagData,获取Tag以及TagData中的信息。
ff_read_packet的缓存和av_read_frame函数里的缓存是不一样的,ff_read_packet的缓存为AVFormatInternal::raw_packet_buffer,在av_read_frame函数里缓存则为
AVFormatInternal::packet_buffer,简单的理解为在codec被识别之前用raw_packet_buffer缓存,codec识别后用packet_buffer。


int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    int ret, i, err;
    AVStream *st;
     if (!genpts) {  
        ret = s->packet_buffer ?  
            read_from_packet_buffer(&s->packet_buffer, &s->packet_buffer_end, pkt) :  
            read_frame_internal(s, pkt);  
        if (ret < 0)  
            return ret;  
        goto return_packet;  
    } 

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

        //首先判断s->raw_packet_buffer中是否有数据,一般开始的时候是没有的
        if (pktl) {
            *pkt = pktl->pkt;
            st   = s->streams[pkt->stream_index];
            if (s->raw_packet_buffer_remaining_size <= 0)
                if ((err = probe_codec(s, st, NULL)) < 0)
                    return err;  //
            if (st->request_probe <= 0) { //-1是probe完成,0是不要probe
                s->raw_packet_buffer                 = pktl->next;
                s->raw_packet_buffer_remaining_size += pkt->size;
                av_free(pktl);
                return 0;
            }
        }

        pkt->data = NULL;
        pkt->size = 0;
        av_init_packet(pkt);
        ret = s->iformat->read_packet(s, pkt);

        //如果没有数据,调用s->iformat->read_packet(s,pkt),对于ts流来讲:s->iformat->read_packet调用的是mpegts_read_packet(读出一个PES包),0表示成功,小于0表示错误,pkt就在这里获取到。 
        if (ret < 0) {
            continue;

        }

//读取成功  ,判断是不是要丢掉 

        if ((s->flags & AVFMT_FLAG_DISCARD_CORRUPT) &&
            (pkt->flags & AV_PKT_FLAG_CORRUPT)) {
            av_log(s, AV_LOG_WARNING,
                   "Dropped corrupted packet (stream = %d)\n",
                   pkt->stream_index);
            av_free_packet(pkt);
            continue;
        }

        if (pkt->stream_index >= (unsigned)s->nb_streams) {
            av_log(s, AV_LOG_ERROR, "Invalid stream index %d\n", pkt->stream_index);
            continue;
        }

        st = s->streams[pkt->stream_index];

        if (update_wrap_reference(s, st, pkt->stream_index, pkt) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
            // correct first time stamps to negative values
            if (!is_relative(st->first_dts))
                st->first_dts = wrap_timestamp(st, st->first_dts);
            if (!is_relative(st->start_time))
                st->start_time = wrap_timestamp(st, st->start_time);
            if (!is_relative(st->cur_dts))
                st->cur_dts = wrap_timestamp(st, st->cur_dts);
        }

        pkt->dts = wrap_timestamp(st, pkt->dts);
        pkt->pts = wrap_timestamp(st, pkt->pts);

        force_codec_ids(s, st);

        /* TODO: audio: time filter; video: frame reordering (pts != dts) */

        //音频要做时间过滤; 视频帧的重排序(当视频帧的dts和pts不等的时候)

        if (s->use_wallclock_as_timestamps)
            pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);

        if (!pktl && st->request_probe <= 0)
            return ret;
        //最后调用add_to_pktbuf将packet加入s->raw_packet_buffer中。
        add_to_pktbuf(&s->raw_packet_buffer, pkt, &s->raw_packet_buffer_end);//将packet加入到s->raw_packet_buffer
        s->raw_packet_buffer_remaining_size -= pkt->size;
      //raw buffer也是有大小限制的,就是raw_packet_buffer_remaining_size 这么大。  
        if ((err = probe_codec(s, st, pkt)) < 0)
            return err;
    }
}

probe_codec
probe_codec是用来探测codec的,probe_codec先是把pkt的数据打包进AVProbeData,然后调用set_codec_from_probe_data来进行探测的,set_codec_from_probe_data的基本思想是根据av_probe_input_format3函数返回的一个AVInputFormat格式来和fmt_id_type匹配得出的codec_id和type的。
parse_packet
parse_packet()给需要AVCodecParser的媒体流提供解析AVPacket的功能,最终调用了相应AVCodecParser的av_parser_parse2()函数,代码为s->parser->parser_parse,接着会调用具体的解析函数,如h264_parse(parse_nal_units),最终解析出来AVPacket。
h264解析
ff_h264_decode_seq_parameter_set  解析SPS。
ff_h264_decode_picture_parameter_set   解析PPS。
ff_h264_decode_sei                   解析SEI。
以上代码在libavcdec/h264_parser.c。

AVClass与AVOption
AVClass最主要的作用就是给结构体(例如AVFormatContext等)增加AVOption功能的支持。换句话说AVClass就是AVOption和目标结构体之间的“桥梁”。AVClass要求必须声明为目标结构体的第一个变量。 AVClass中有一个option数组用于存储目标结构体的所有的AVOption。举个例子,AVFormatContext结构体,AVClass和AVOption之间的关系如下图所示。

    图中AVFormatContext结构体的第一个变量为AVClass类型的指针av_class,它在AVFormatContext结构体初始化的时候,被赋值指向了全局静态变量av_format_context_class结构体(定义位于libavformat\options.c)。而AVClass类型的av_format_context_class结构体中的option变量指向了全局静态数组avformat_options(定义位于libavformat\options_table.h)。现在回到AVOption。其实除了可以对FFmpeg常用结构体AVFormatContext,AVCodecContext等进行赋值之外,还可以对它们的私有数据priv_data进行赋值。这个字段里通常存储了各种编码器特有的结构体。而这些结构体的定义在FFmpeg的SDK中是找不到的。例如使用libx264进行编码的时候,通过AVCodecContext的priv_data字段可以对X264Context结构体中的变量进行赋值,设置preset,profile等。使用libx265进行编码的时候,通过AVCodecContext的priv_data字段可以对libx265Context结构体中的变量进行赋值,设置preset,tune等。

AVProbeData
typedef struct AVProbeData {
    const char *filename;  //文件名
    unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. // buf存储用于推测AVInputFormat的媒体数据,最后还有个

mime_type保存媒体的类型。其中buf可以为空,但是其后面无论如何都需要 填充AVPROBE_PADDING_SIZE个0(AVPROBE_PADDING_SIZE取值为32,即32个0)。 */
    int buf_size;       /**< Size of buf except extra allocated bytes */
    const char *mime_type; /**< mime_type, when known. */
} AVProbeData;

avformat_open_input(init_input(av_probe_input_format3), s->iformat->read_header());
init_ input
if ((ret = init_input(s, filename)) < 0)  
        goto fail;  
    //执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格式进行分析,也就是说pb在底层,iformat在上层。
主要分两种情况
1 知道s->pb,从s->pb得到s->iformat。
2 不知道s->pb,打开文件(avio_open),探测文件格式(av_probe_input_buffer)。
av_probe_input_format3
av_probe_input_format3()根据输入数据查找合适的AVInputFormat。输入的数据位于AVProbeData中。该函数最主要的部分是一个循环。该循环调用av_iformat_next()遍历FFmpeg中所有的AVInputFormat(av_register_all时组建),并根据以下规则确定AVInputFormat和输入媒体数据的匹配分数(score,反应匹配程度):
(1) 如果AVInputFormat中包含read_probe(),就调用read_probe()函数获取匹配分数(这一方法如果结果匹配的话,一般会获 得AVPROBE_SCORE_MAX的分值,即100分)。如果不包含该函数,就使用av_match_ext()函数比较输入媒体的扩展名和 AVInputFormat的扩展名是否匹配,如果匹配的话,设定匹配分数为 AVPROBE_SCORE_EXTENSION(AVPROBE_SCORE_EXTENSION取值为50,即50分)。
(2)使用av_match_name()比较输入媒体的mime_type和AVInputFormat的mime_type,如果匹配的话,设定匹配分数为AVPROBE_SCORE_MIME(AVPROBE_SCORE_MIME取值为75,即75分)。
(3)如果该AVInputFormat的匹配分数大于此前的最大匹配分数,则记录当前的匹配分数为最大匹配分数,并且记录当前的AVInputFormat为最佳匹配的AVInputFormat。

ffmpeg.c
parse_option()
    解析一个输入选项。具体的解析步骤不再赘述。parse_options()会循环调用parse_option()直到所有选项解析完毕。FFmpeg的每一个选项信息存储在一个OptionDef结构体中。

av_interleaved_write_frame与av_write_frame的区别?

av_interleaved_write_frame

/*

* Write apacket to an output media file ensuring correct interleaving.

 * This function will buffer the packetsinternally as needed to make sure the

 * packets in the output file are properlyinterleaved in the order of

 * increasing dts. Callers doing their owninterleaving should call

 * av_write_frame() instead of this function.

*/

 

int av_write_frame(AVFormatContext *s, AVPacket *pkt);  

/*

*Write a packet to an output media file.

* Thisfunction passes the packet directly to the muxer, without any buffering

* orreordering. The caller is responsible for correctly interleaving the

*packets if the format requires it. Callers that want libavformat to handle

* theinterleaving should call av_interleaved_write_frame() instead of this

*function. */

av_interleaved_write_frame调用了interleave_packet,而av_write_frame没有调用,看看interleave_packet的代码:

    static intinterleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, intflush) 

    { 

        if(s->oformat->interleave_packet) { 

            int ret =s->oformat->interleave_packet(s, out, in, flush); 

            if (in) 

                av_free_packet(in); 

            return ret; 

        } else 

            returnff_interleave_packet_per_dts(s, out, in, flush); 

    } 

代码非常简单,如果AVOutputFormat有interleave_packet函数指针,则调用,如果没有则调用ff_interleave_packet_per_dts,看看ff_interleave_packet_per_dts的代码:

intff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *out,

                                 AVPacket *pkt,int flush)

{

    AVPacketList *pktl;

    int stream_count = 0;

    int noninterleaved_count = 0;

    int i, ret;

 

    if (pkt) {//把pkt加入到AVPacketList中去

        if ((ret = ff_interleave_add_packet(s,pkt, interleave_compare_dts)) < 0)

            return ret;

    }

 

    for (i = 0; i < s->nb_streams; i++) {

        if(s->streams[i]->last_in_packet_buffer) {

            ++stream_count;

        } else if(s->streams[i]->codec->codec_type != AVMEDIA_TYPE_ATTACHMENT&&

                  s->streams[i]->codec->codec_id != AV_CODEC_ID_VP8 &&

                  s->streams[i]->codec->codec_id != AV_CODEC_ID_VP9) {

            ++noninterleaved_count;

        }

    }

 

    if(s->internal->nb_interleaved_streams == stream_count)

        flush = 1;

 

    if (s->max_interleave_delta > 0&&

        s->internal->packet_buffer&&

        !flush &&

       s->internal->nb_interleaved_streams ==stream_count+noninterleaved_count

    ) {

        AVPacket *top_pkt =&s->internal->packet_buffer->pkt;

        int64_t delta_dts = INT64_MIN;

        int64_t top_dts =av_rescale_q(top_pkt->dts,

                                      s->streams[top_pkt->stream_index]->time_base,

                                      AV_TIME_BASE_Q);

 

        for (i = 0; i < s->nb_streams;i++) {

            int64_t last_dts;

            const AVPacketList *last =s->streams[i]->last_in_packet_buffer;

 

            if (!last)

                continue;

 

            last_dts =av_rescale_q(last->pkt.dts,

                                   s->streams[i]->time_base,

                                    AV_TIME_BASE_Q);

            delta_dts = FFMAX(delta_dts,last_dts - top_dts);

            // last_dts -top_dtsPTS差值。

        }

 

        /*dts间隔大于max_interleave_delta */

        if (delta_dts >s->max_interleave_delta) {

            av_log(s, AV_LOG_DEBUG,

                   "Delay between thefirst packet and last packet in the "

                   "muxing queue is%"PRId64" > %"PRId64": forcing output\n",

                   delta_dts,s->max_interleave_delta);

            flush = 1;

        }

    }

 

    if (stream_count && flush) {

        AVStream *st;

        pktl =s->internal->packet_buffer;

        *out = pktl->pkt;

        st  = s->streams[out->stream_index];

 

        s->internal->packet_buffer =pktl->next;

        if (!s->internal->packet_buffer)

           s->internal->packet_buffer_end = NULL;

 

        if (st->last_in_packet_buffer ==pktl)

            st->last_in_packet_buffer =NULL;

        av_freep(&pktl);

 

        return 1;

    } else {

        av_init_packet(out);

        return 0;

    }

}

    从代码中看到,函数ff_interleave_packet_per_dts调用ff_interleave_add_packet把pkt加入缓存,然后再从缓存中取出第一个pkt返回。

    函数ff_interleave_add_packet把pkt经过排序(如果缓存中有pkt的话)加入到缓存中,这个排序是根据ff_interleave_packet_per_dts函数传入的compare(其实就是函数interleave_compare_dts)指针做的。

    interleave_compare_dts函数其实就是在比较传入的2个pkt的dts值,其实就是比那个的dts更早,如果有音频还有audio_preload(音频预加载时间)的话,还需要把音频包的预加载时间算进去。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值