Bootstrap

av_write_frame与av_interleaved_write_frame

ffmpeg的 Mux 主要分为三步操作:

avformat_write_header: 写文件头。

av_write_frame/av_interleaved_write_frame: 写packet。

av_write_trailer: 写文件尾。

 av_write_frame()用于输出一帧视音频数据,它的声明位于libavformat\avformat.h,直接将包写进Mux,没有缓存和重新排序,一切都需要用户自己设置。

av_interleaved_write_frame将对 packet 进行缓存和 pts 检查,这是区别于 av_write_frame 的地方。

av_write_frame

从源代码可以看出,av_write_frame()主要完成了以下几步工作:
(1)调用check_packet()做一些简单的检测。

    从代码中可以看出,check_packet()的功能比较简单:首先检查一下输入的AVPacket是否为空,如果为空,则是直接返回;然后检查一下AVPacket的stream_index(标记了该AVPacket所属的AVStream)设置是否正常,如果为负数或者大于AVStream的个数,则返回错误信息;
(2)调用compute_pkt_fields2()设置AVPacket的一些属性值。

    从代码中可以看出,compute_pkt_fields2()主要有两方面的功能:一方面用于计算AVPacket的duration, dts等信息;另一方面用于检查pts、dts这些参数的合理性(例如PTS是否一定大于DTS)。
(3)调用write_packet()写入数据。

    write_packet()函数最关键的地方就是调用了AVOutputFormat中写入数据的方法。如果AVPacket中的flag标记中包含AV_PKT_FLAG_UNCODED_FRAME,就会调用AVOutputFormat的write_uncoded_frame()函数;如果不包含那个标记,就会调用write_packet()函数。write_packet()实际上是一个函数指针,指向特定的AVOutputFormat中的实现函数。例如,我们看一下FLV对应的AVOutputFormat,位于libavformat\flvenc.c。

av_interleaved_write_frame

intav_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt)

{

   int ret, flush = 0, i;

         //对包预处理

   ret = prepare_input_packet(s, pkt);

   if (ret < 0)

       goto fail;

         //bitstreamfilters (过滤器,也是区别于av_write_frame 的地方)

   if (pkt) {

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

       if (s->oformat->check_bitstream) {

           if (!st->internal->bitstream_checked) {

                if ((ret =s->oformat->check_bitstream(s, pkt)) < 0)

                    goto fail;

               else if (ret == 1)

                   st->internal->bitstream_checked = 1;

           }

       }

       for (i = 0; i < st->internal->nb_bsfcs; i++) {

           AVBSFContext *ctx = st->internal->bsfcs[i];

           if (i > 0) {

               AVBSFContext* prev_ctx =st->internal->bsfcs[i - 1];

                if(prev_ctx->par_out->extradata_size != ctx->par_in->extradata_size){

                    if ((ret =avcodec_parameters_copy(ctx->par_in, prev_ctx->par_out)) < 0)

                        goto fail;

                }

           }

           // TODO: when any bitstream filter requires flushing at EOF, we'll needto

           // flush each stream's BSF chain on write_trailer.

           if ((ret = av_bsf_send_packet(ctx, pkt)) < 0) {

                av_log(ctx, AV_LOG_ERROR,

                       "Failed to sendpacket to filter %s for stream %d",

                       ctx->filter->name,pkt->stream_index);

                goto fail;

           }

           // TODO: when any automatically-added bitstream filter is generatingmultiple

           // output packets for a single input one, we'll need to call this in aloop

           // and write each output packet.

           if ((ret = av_bsf_receive_packet(ctx, pkt)) < 0) {

                if (ret == AVERROR(EAGAIN) ||ret == AVERROR_EOF)

                    return 0;

                av_log(ctx, AV_LOG_ERROR,

                       "Failed to sendpacket to filter %s for stream %d",

                      ctx->filter->name,pkt->stream_index);

                goto fail;

           }

           if (i == st->internal->nb_bsfcs - 1) {

                if(ctx->par_out->extradata_size != st->codecpar->extradata_size) {

                    if ((ret = avcodec_parameters_copy(st->codecpar,ctx->par_out)) < 0)

                        goto fail;

                }

           }

       }

       if (s->debug & FF_FDEBUG_TS)

           av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame size:%d dts:%spts:%s\n",

                pkt->size,av_ts2str(pkt->dts), av_ts2str(pkt->pts));

#if FF_API_COMPUTE_PKT_FIELDS2 &&FF_API_LAVF_AVCTX

       if ((ret = compute_muxer_pkt_fields(s, st, pkt)) < 0 &&!(s->oformat->flags & AVFMT_NOTIMESTAMPS))

           goto fail;

#endif

       if (pkt->dts == AV_NOPTS_VALUE && !(s->oformat->flags& AVFMT_NOTIMESTAMPS)) {

           ret = AVERROR(EINVAL);

           goto fail;

       }

    }else {

       av_log(s, AV_LOG_TRACE, "av_interleaved_write_frame FLUSH\n");

       flush = 1;

    }

   //写包

   for (;; ) {

       AVPacket opkt;

       //此处也是与av_write_frame的区别

       int ret = interleave_packet(s, &opkt, pkt, flush);

       if (pkt) {

           memset(pkt, 0, sizeof(*pkt));

           av_init_packet(pkt);

            pkt = NULL;

       }

       if (ret <= 0) //FIXME cleanup needed for ret<0 ?

           return ret;

                   //写入

       ret = write_packet(s, &opkt);

       if (ret >= 0)

           s->streams[opkt.stream_index]->nb_frames++;

       av_packet_unref(&opkt);

       if (ret < 0)

           return ret;

       if(s->pb && s->pb->error)

           return s->pb->error;

    }

fail:

   av_packet_unref(pkt);

   return ret;

}

prepare_input_packet 函数与av_write_frame 的一致,主要是 bitstream filters 和 interleave_packet 的区别:

static intinterleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *in, int flush)

{

   if (s->oformat->interleave_packet) {

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

       if (in)

           av_packet_unref(in);

       return ret;

    }else

       return ff_interleave_packet_per_dts(s, out, in, flush);

}

 

;