av_write_frame()

来源:互联网 发布:数据库文件提取数据 编辑:程序博客网 时间:2024/05/19 11:49

首先编码的大致流程如下:
(1)avformat_alloc_output_context2()
(2)avfomat_write_header()
(3)avcodec_send_frame()/avcodec_receive_packet()
(4)av_write_frame()
(5)av_write_trailer()
av_write_frame()用于输出一帧视音频数据,它的声明位于libavformat\avformat.h,如下所示。

/**  * Write a packet to an output media file.  *  * This function passes the packet directly to the muxer, without any buffering  * or reordering. The caller is responsible for correctly interleaving the  * packets if the format requires it. Callers that want libavformat to handle  * the interleaving should call av_interleaved_write_frame() instead of this  * function.  *  * @param s media file handle  * @param pkt The packet containing the data to be written. Note that unlike  *            av_interleaved_write_frame(), this function does not take  *            ownership of the packet passed to it (though some muxers may make  *            an internal reference to the input packet).  * *            This parameter can be NULL (at any time, not just at the end), in  *            order to immediately flush data buffered within the muxer, for  *            muxers that buffer up data internally before writing it to the  *            output.  *        *            Packet's @ref AVPacket.stream_index "stream_index" field must be  *            set to the index of the corresponding stream in @ref  *            AVFormatContext.streams "s->streams". It is very strongly  *            recommended that timing information (@ref AVPacket.pts "pts", @ref  *            AVPacket.dts "dts", @ref AVPacket.duration "duration") is set to  *            correct values.  * @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush  *  * @see av_interleaved_write_frame()  */  int av_write_frame(AVFormatContext *s, AVPacket *pkt);  

参数:
s:用于输出的AVFormatContext。
pkt:等待输出的AVPacket。
函数正常执行后返回值等于0。

av_write_frame()的调用关系如下图所示。
这里写图片描述

av_write_frame()的定义位于libavformat\mux.c,如下所示。

int av_write_frame(AVFormatContext *s, AVPacket *pkt)  {      int ret;       ret = check_packet(s, pkt);      if (ret < 0)          return ret;      //PacketNULLFlush Encoder      if (!pkt) {          if (s->oformat->flags & AVFMT_ALLOW_FLUSH) {              ret = s->oformat->write_packet(s, NULL);              if (s->flush_packets && s->pb && s->pb->error >= 0 && s->flags & AVFMT_FLAG_FLUSH_PACKETS)                  avio_flush(s->pb);              if (ret >= 0 && s->pb && s->pb->error < 0)                  ret = s->pb->error;              return ret;          }          return 1;      }      ret = compute_pkt_fields2(s, s->streams[pkt->stream_index], pkt);      if (ret < 0 && !(s->oformat->flags & AVFMT_NOTIMESTAMPS))          return ret;      //写入      ret = write_packet(s, pkt);      if (ret >= 0 && s->pb && s->pb->error < 0)          ret = s->pb->error;      if (ret >= 0)          s->streams[pkt->stream_index]->nb_frames++;      return ret;  }  

从源代码可以看出,av_write_frame()主要完成了以下几步工作:
(1)调用check_packet()做一些简单的检测
(2)调用compute_pkt_fields2()设置AVPacket的一些属性值
(3)调用write_packet()写入数据

下面分别看一下这几个函数功能。
一、check_packet()定义位于libavformat\mux.c,如下所示。

static int check_packet(AVFormatContext *s, AVPacket *pkt)  {      if (!pkt)          return 0;      if (pkt->stream_index < 0 || pkt->stream_index >= s->nb_streams) {          av_log(s, AV_LOG_ERROR, "Invalid packet stream index: %d\n",                 pkt->stream_index);          return AVERROR(EINVAL);      }      if (s->streams[pkt->stream_index]->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT) {          av_log(s, AV_LOG_ERROR, "Received a packet for an attachment stream.\n");          return AVERROR(EINVAL);      }      return 0;  }  

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

二、compute_pkt_fields2()的代码很长,下面简要介绍他的功能。
compute_pkt_fields2()主要有两方面的功能:
(1)计算AVPacket的duration, dts等信息;
(2)用于检查pts、dts这些参数的合理性(例如PTS是否一定大于DTS)。

三、write_packet()函数最关键的地方就是调用了AVOutputFormat中写入数据的方法。和avformat_write_header类似。如果AVPacket中的flag标记中包含AV_PKT_FLAG_UNCODED_FRAME,就会调用AVOutputFormat的write_uncoded_frame()函数;如果不包含那个标记,就会调用write_packet()函数。write_packet()实际上是一个函数指针,指向特定的AVOutputFormat中的实现函数。

在这里我们举例子看一下FLV封装格式对应的AVOutputFormat,它的定义位于libavformat\flvenc.c,如下所示。

AVOutputFormat ff_flv_muxer = {      .name           = "flv",      .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),      .mime_type      = "video/x-flv",      .extensions     = "flv",      .priv_data_size = sizeof(FLVContext),      .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,      .video_codec    = AV_CODEC_ID_FLV1,      .write_header   = flv_write_header,      .write_packet   = flv_write_packet,      .write_trailer  = flv_write_trailer,      .codec_tag      = (const AVCodecTag* const []) {                            flv_video_codec_ids, flv_audio_codec_ids, 0                        },      .flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |                        AVFMT_TS_NONSTRICT,   };

write_packet()最终会调用到flv_write_packet函数。