FFmpeg

目录

FFmpeg

graph TD A[av_register_all] --> B[avformat_open_input]-->C[avformat_find_stream_info] C-->D[avcodec_find_decoder] F[竖向流程图]-->H[ass]

FFmpeg struct

AVFrameContext

保存需要读入的文件的格式信息,比如流的个数以及流数据等,AVFormat中实现了目前多媒体领域中的绝大多数媒体封装格式,包括封装和解封装,如MP4,FLV,KV,TS等文件封装格式,RTMP,RTSP,MMS,HLS等网络协议封装格式。FFmpeg是否支持某种媒体封装格式,取决于编译时是否包含了该格式的封装库。根据实际需求,可进行媒体封装格式的扩展,增加自己定制的封装格式,即在AVFormat中增加自己的封装处理模块。

  • duration:

avformat_alloc_context

初始化上下文

avformat_alloc_output_context2

可以初始化一个用于输出的AVFormatContext结构体。它的声明位于libavformat\avformat.h


int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,
                                   const char *format_name, const char *filename);
  • ctx:函数调用成功之后创建的AVFormatContext结构体。
  • oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由FFmpeg猜测输出格式。
    PS:使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。
  • format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。
  • filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。
    函数执行成功的话,其返回值大于等于0。

avformat_open_input

int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options)

打开一个文件,第一个参数是一个AVFormatContext指针变量的地址,它会根据打开的文件信息填充AVFormatContext,需要注意的是,此处的pFormatContext必须为NULL或由avformat_alloc_context分配得到,这也是上一节中将其初始化为NULL的原因,否则此函数调用会出问题。第二个参数是打开的文件名,通过argv[1]指定,也就是命令行的第一个参数。后两个参数分别用于指定特定的输入格式(AVInputFormat)以及指定文件打开额外参数的AVDictionary结构,这里均留作NULL。

avformat_close_input

用于关闭一个AVFormatContext,一般情况下是和avformat_open_input()成对使用的。

avformat_find_stream_info

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

avformat_open_input函数只是读文件头,并不会填充流信息,因此我们需要接下来调用avformat_find_stream_info获取文件中的流信息,此函数会读取packet,并确定文件中所有的流信息,设置pFormatCtx->streams指向文件中的流,但此函数并不会改变文件指针,读取的packet会给后面的解码进行处理。

avformat_free_context

销毁释放AVFormatContext结构。

AVCodecContext

保存了相应流的详细编码信息,比如视频的宽、高,编码类型等。

avcodec_free_context

释放AVCodecContext

avcode_alloc_context3

创建AVCodecContext 上下文

avcodec_copy_context

int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);

旧的API函数:avcodec_copy_context,主要的功能就是编码参数上下文的拷贝。
在最后释放的时候,居然出错了:

avcodec_close(ctx);
avcodec_close(ctx_video);//释放它的时候出错,出现部分double free

跟踪avcodec_close发现,是在释放AVCodecContext结构体中的nb_coded_side_data变量引起的错误
所以,在copy上下文之后,加了下面两行代码:

ctx_video->coded_side_data = NULL;
ctx_video->nb_coded_side_data = 0;

不太清楚这个变量的功能。这样操作后,编码正常,且在调用avcodec_close的时候,也没出现double free的错误,暂且这样处理吧。

AVInputFormat

存储输入音视频使用的封装格式。

AVOutputFormat

存储输出音视频使用的封装格式。

AVIOContext

AVIOContext是FFMPEG管理输入输出数据的结构体。本文将会详细分析一下该结构体里每个变量的含义和作用。

  • unsigned char *buffer:缓存开始位置

  • int buffer_size:缓存大小(默认32768)

  • unsigned char *buf_ptr:当前指针读取到的位置

  • unsigned char *buf_end:缓存结束的位置

  • void *opaque:URLContext结构体

在解码的情况下,buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候,先把数据从硬盘读入buffer,然后在送给解码器用于解码。
其中opaque指向了URLContext。注意,这个结构体并不在FFMPEG提供的头文件中,而是在FFMPEG的源代码中。从FFMPEG源代码中翻出的定义如下所示:

URLContext

存储视音频使用的协议的类型以及状态

URLProtocol

存储输入视音频使用的封装格式

AVStream


/**
 * Stream structure.
 * New fields can be added to the end with minor version bumps.
 * Removal, reordering and changes to existing fields require a major
 * version bump.
 * sizeof(AVStream) must not be used outside libav*.
 */
typedef struct AVStream {
    int index;    /**< stream index in AVFormatContext */
    /**
     * Format-specific stream ID.
     * decoding: set by libavformat
     * encoding: set by the user, replaced by libavformat if left unset
     */
    int id;
#if FF_API_LAVF_AVCTX
    /**
     * @deprecated use the codecpar struct instead
     */
    attribute_deprecated
    AVCodecContext *codec;
#endif
    void *priv_data;

    /**
     * This is the fundamental unit of time (in seconds) in terms
     * of which frame timestamps are represented.
     *
     * decoding: set by libavformat
     * encoding: May be set by the caller before avformat_write_header() to
     *           provide a hint to the muxer about the desired timebase. In
     *           avformat_write_header(), the muxer will overwrite this field
     *           with the timebase that will actually be used for the timestamps
     *           written into the file (which may or may not be related to the
     *           user-provided one, depending on the format).
     */
    AVRational time_base;

    /**
     * Decoding: pts of the first frame of the stream in presentation order, in stream time base.
     * Only set this if you are absolutely 100% sure that the value you set
     * it to really is the pts of the first frame.
     * This may be undefined (AV_NOPTS_VALUE).
     * @note The ASF header does NOT contain a correct start_time the ASF
     * demuxer must NOT set this.
     */
    int64_t start_time;

    /**
     * Decoding: duration of the stream, in stream time base.
     * If a source file does not specify a duration, but does specify
     * a bitrate, this value will be estimated from bitrate and file size.
     *
     * Encoding: May be set by the caller before avformat_write_header() to
     * provide a hint to the muxer about the estimated duration.
     */
    int64_t duration;

    int64_t nb_frames;                 ///< number of frames in this stream if known or 0

    int disposition; /**< AV_DISPOSITION_* bit field */

    enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed.

    /**
     * sample aspect ratio (0 if unknown)
     * - encoding: Set by user.
     * - decoding: Set by libavformat.
     */
    AVRational sample_aspect_ratio;

    AVDictionary *metadata;

    /**
     * Average framerate
     *
     * - demuxing: May be set by libavformat when creating the stream or in
     *             avformat_find_stream_info().
     * - muxing: May be set by the caller before avformat_write_header().
     */
    AVRational avg_frame_rate;

    /**
     * For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet
     * will contain the attached picture.
     *
     * decoding: set by libavformat, must not be modified by the caller.
     * encoding: unused
     */
    AVPacket attached_pic;

    /**
     * An array of side data that applies to the whole stream (i.e. the
     * container does not allow it to change between packets).
     *
     * There may be no overlap between the side data in this array and side data
     * in the packets. I.e. a given side data is either exported by the muxer
     * (demuxing) / set by the caller (muxing) in this array, then it never
     * appears in the packets, or the side data is exported / sent through
     * the packets (always in the first packet where the value becomes known or
     * changes), then it does not appear in this array.
     *
     * - demuxing: Set by libavformat when the stream is created.
     * - muxing: May be set by the caller before avformat_write_header().
     *
     * Freed by libavformat in avformat_free_context().
     *
     * @see av_format_inject_global_side_data()
     */
    AVPacketSideData *side_data;
    /**
     * The number of elements in the AVStream.side_data array.
     */
    int            nb_side_data;

    /**
     * Flags for the user to detect events happening on the stream. Flags must
     * be cleared by the user once the event has been handled.
     * A combination of AVSTREAM_EVENT_FLAG_*.
     */
    int event_flags;
#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.

    /**
     * Real base framerate of the stream.
     * This is the lowest framerate with which all timestamps can be
     * represented accurately (it is the least common multiple of all
     * framerates in the stream). Note, this value is just a guess!
     * For example, if the time base is 1/90000 and all frames have either
     * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
     */
    AVRational r_frame_rate;

#if FF_API_LAVF_FFSERVER
    /**
     * String containing pairs of key and values describing recommended encoder configuration.
     * Pairs are separated by ','.
     * Keys are separated from values by '='.
     *
     * @deprecated unused
     */
    attribute_deprecated
    char *recommended_encoder_configuration;
#endif

    /**
     * Codec parameters associated with this stream. Allocated and freed by
     * libavformat in avformat_new_stream() and avformat_free_context()
     * respectively.
     *
     * - demuxing: filled by libavformat on stream creation or in
     *             avformat_find_stream_info()
     * - muxing: filled by the caller before avformat_write_header()
     */
    AVCodecParameters *codecpar;

    /*****************************************************************
     * All fields below this line are not part of the public API. They
     * may not be used outside of libavformat and can be changed and
     * removed at will.
     * Internal note: be aware that physically removing these fields
     * will break ABI. Replace removed fields with dummy fields, and
     * add new fields to AVStreamInternal.
     *****************************************************************
     */

#define MAX_STD_TIMEBASES (30*12+30+3+6)
    /**
     * Stream information used internally by avformat_find_stream_info()
     */
    struct {
        int64_t last_dts;
        int64_t duration_gcd;
        int duration_count;
        int64_t rfps_duration_sum;
        double (*duration_error)[2][MAX_STD_TIMEBASES];
        int64_t codec_info_duration;
        int64_t codec_info_duration_fields;
        int frame_delay_evidence;

        /**
         * 0  -> decoder has not been searched for yet.
         * >0 -> decoder found
         * <0 -> decoder with codec_id == -found_decoder has not been found
         */
        int found_decoder;

        int64_t last_duration;

        /**
         * Those are used for average framerate estimation.
         */
        int64_t fps_first_dts;
        int     fps_first_dts_idx;
        int64_t fps_last_dts;
        int     fps_last_dts_idx;

    } *info;

    int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */

    // Timestamp generation support:
    /**
     * Timestamp corresponding to the last dts sync point.
     *
     * Initialized when AVCodecParserContext.dts_sync_point >= 0 and
     * a DTS is received from the underlying container. Otherwise set to
     * AV_NOPTS_VALUE by default.
     */
    int64_t first_dts;
    int64_t cur_dts;
    int64_t last_IP_pts;
    int last_IP_duration;

    /**
     * Number of packets to buffer for codec probing
     */
    int probe_packets;

    /**
     * Number of frames that have been demuxed during avformat_find_stream_info()
     */
    int codec_info_nb_frames;

    /* av_read_frame() support */
    enum AVStreamParseType need_parsing;
    struct AVCodecParserContext *parser;

    /**
     * last packet in packet_buffer for this stream when muxing.
     */
    struct AVPacketList *last_in_packet_buffer;
    AVProbeData probe_data;
#define MAX_REORDER_DELAY 16
    int64_t pts_buffer[MAX_REORDER_DELAY+1];

    AVIndexEntry *index_entries; /**< Only used if the format does not
                                    support seeking natively. */
    int nb_index_entries;
    unsigned int index_entries_allocated_size;

    /**
     * Stream Identifier
     * This is the MPEG-TS stream identifier +1
     * 0 means unknown
     */
    int stream_identifier;

    /**
     * Details of the MPEG-TS program which created this stream.
     */
    int program_num;
    int pmt_version;
    int pmt_stream_idx;

    int64_t interleaver_chunk_size;
    int64_t interleaver_chunk_duration;

    /**
     * stream probing state
     * -1   -> probing finished
     *  0   -> no probing requested
     * rest -> perform probing with request_probe being the minimum score to accept.
     */
    int request_probe;
    /**
     * Indicates that everything up to the next keyframe
     * should be discarded.
     */
    int skip_to_keyframe;

    /**
     * Number of samples to skip at the start of the frame decoded from the next packet.
     */
    int skip_samples;

    /**
     * If not 0, the number of samples that should be skipped from the start of
     * the stream (the samples are removed from packets with pts==0, which also
     * assumes negative timestamps do not happen).
     * Intended for use with formats such as mp3 with ad-hoc gapless audio
     * support.
     */
    int64_t start_skip_samples;

    /**
     * If not 0, the first audio sample that should be discarded from the stream.
     * This is broken by design (needs global sample count), but can't be
     * avoided for broken by design formats such as mp3 with ad-hoc gapless
     * audio support.
     */
    int64_t first_discard_sample;

    /**
     * The sample after last sample that is intended to be discarded after
     * first_discard_sample. Works on frame boundaries only. Used to prevent
     * early EOF if the gapless info is broken (considered concatenated mp3s).
     */
    int64_t last_discard_sample;

    /**
     * Number of internally decoded frames, used internally in libavformat, do not access
     * its lifetime differs from info which is why it is not in that structure.
     */
    int nb_decoded_frames;

    /**
     * Timestamp offset added to timestamps before muxing
     */
    int64_t mux_ts_offset;

    /**
     * Internal data to check for wrapping of the time stamp
     */
    int64_t pts_wrap_reference;

    /**
     * Options for behavior, when a wrap is detected.
     *
     * Defined by AV_PTS_WRAP_ values.
     *
     * If correction is enabled, there are two possibilities:
     * If the first time stamp is near the wrap point, the wrap offset
     * will be subtracted, which will create negative time stamps.
     * Otherwise the offset will be added.
     */
    int pts_wrap_behavior;

    /**
     * Internal data to prevent doing update_initial_durations() twice
     */
    int update_initial_durations_done;

    /**
     * Internal data to generate dts from pts
     */
    int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
    uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];

    /**
     * Internal data to analyze DTS and detect faulty mpeg streams
     */
    int64_t last_dts_for_order_check;
    uint8_t dts_ordered;
    uint8_t dts_misordered;

    /**
     * Internal data to inject global side data
     */
    int inject_global_side_data;

    /**
     * display aspect ratio (0 if unknown)
     * - encoding: unused
     * - decoding: Set by libavformat to calculate sample_aspect_ratio internally
     */
    AVRational display_aspect_ratio;

    /**
     * An opaque field for libavformat internal usage.
     * Must not be accessed in any way by callers.
     */
    AVStreamInternal *internal;
} AVStream;

avformat_new_stream

创建新流媒体

AVCodecParameters

avcodec_parameters_to_context

int avcodec_parameters_to_context(AVCodecContext *codec,
                                  const AVCodecParameters *par);

参数配置拷贝。

avcodec_parameters_copy

拷贝编器参数

avcodec_parameters_free

AVRational

AVCodec

真正的编解码器,其中有编解码需要调用的函数

avcodec_find_decoder

AVCodec *avcodec_find_decoder(enum AVCodecID id);

查找解码器

avcodec_open2

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

打开解码器2

avcodec_close

int avcodec_close(AVCodecContext *avctx);

关闭解码器

avcodec_send_packet

int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);

将packet包发送到多线程解码器,此函数与avcode_receive_frame()对应。按 dts 递增的顺序向解码器送入编码帧 packet,解码器按 pts 递增的顺序输出原始帧 frame,实际上解码器不关注输入 packe t的 dts(错值都没关系),它只管依次处理收到的 packet,按需缓冲和解码。

avcodec_send_packet() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF

avcodec_send_packet() 多次发送 NULL 并不会导致解码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉解码器中缓存帧。因此播放完毕时应 avcodec_send_packet(NULL) 来取完缓存的帧,而 SEEK 操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧。

解码器通常的冲洗方法:调用一次 avcodec_send_packet(NULL)(返回成功),然后不停调用 avcodec_receive_frame() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_frame() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志。

avcodec_receive_frame

int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

从多线程中接收已解码的frame。avcodec_receive_frame输出 frame 时,会根据各种因素设置好 frame->best_effort_timestamp(文档明确说明),实测 frame->pts 也会被设置(通常直接拷贝自对应的 packet.pts,文档未明确说明)用户应确保 avcodec_send_packet() 发送的 packet 具有正确的 pts,编码帧 packet 与原始帧 frame 间的对应关系通过 pts 确定。

输出 frame 时,frame->pkt_dts 拷贝自当前avcodec_send_packet() 发送的 packet 中的 dts,如果当前 packet 为 NULL(flush packet),解码器进入 flush 模式,当前及剩余的 frame->pkt_dts 值总为 AV_NOPTS_VALUE。因为解码器中有缓存帧,当前输出的 frame 并不是由当前输入的 packet 解码得到的,所以这个 frame->pkt_dts 没什么实际意义,可以不必关注。

avcodec_send_frame

avcodec_send_frame() 发送 NULL frame 时,编码器进入 flush 模式。

avcodec_send_frame() 发送第一个 NULL 会返回成功,后续的 NULL 会返回 AVERROR_EOF。

avcodec_send_frame() 多次发送 NULL 并不会导致编码器中缓存的帧丢失,使用 avcodec_flush_buffers() 可以立即丢掉编码器中缓存帧。因此编码完毕时应使用 avcodec_send_frame(NULL) 来取完缓存的帧,而SEEK操作或切换流时应调用 avcodec_flush_buffers() 来直接丢弃缓存帧。

编码器通常的冲洗方法:调用一次 avcodec_send_frame(NULL)(返回成功),然后不停调用 avcodec_receive_packet() 直到其返回 AVERROR_EOF,取出所有缓存帧,avcodec_receive_packet() 返回 AVERROR_EOF 这一次是没有有效数据的,仅仅获取到一个结束标志。

对音频来说,如果 AV_CODEC_CAP_VARIABLE_FRAME_SIZE(在 AVCodecContext.codec.capabilities 变量中,只读)标志有效,表示编码器支持可变尺寸音频帧,送入编码器的音频帧可以包含任意数量的采样点。如果此标志无效,则每一个音频帧的采样点数目(frame->nb_samples)必须等于编码器设定的音频帧尺寸(avctx->frame_size),最后一帧除外,最后一帧音频帧采样点数可以小于 avctx->frame_size。

avcodec_receive_packet

按 pts 递增的顺序向编码器送入原始帧 frame,编码器按 dts 递增的顺序输出编码帧 packet,实际上编码器关注输入 frame 的 pts 不关注其 dts,它只管依次处理收到的 frame,按需缓冲和编码。

avcodec_receive_packet() 输出 packet 时,会设置 packet.dts,从 0 开始,每次输出的 packet 的 dts 加 1,这是视频层的 dts,用户写输出前应将其转换为容器层的 dts。

avcodec_receive_packet() 输出 packet 时,packet.pts 拷贝自对应的 frame.pts,这是视频层的 pts,用户写输出前应将其转换为容器层的 pts。

avcodec_decode_video2

解码视频数据

avcodec_encode_video2

int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt,
                          const AVFrame *frame, int *got_packet_ptr);

编码一帧视频数据,参数描述如下:

  • avctx:编码器的AVCodecContext
  • avpkt:编码输出的AVPacket
  • frame:编输入的AVFrame
  • got_packet_ptr:成功编码一个AVPacket的时候设置为1

avcodec_flush_buffers

void avcodec_flush_buffers(AVCodecContext *avctx);

立即丢掉解码器中缓存帧。

AVFrame

用于保存数据帧的数据结构,这里的两个帧分别是保存颜色转换前后的两帧图像。AVFrame中存储的是经过解码后的原始数据。在解码中,AVFrame是解码器的输出;在编码中,AVFrame是编码器的输入。

AVFrame的用法:

  1. AVFrame对象必须调用av_frame_alloc()在堆上分配,注意此处指的是AVFrame对象本身,AVFrame对象必须调用av_frame_free()进行销毁。
  2. AVFrame中包含的数据缓冲区是
  3. AVFrame通常只需分配一次,然后可以多次重用,每次重用前应调用av_frame_unref()将frame复位到原始的干净可用的状态。

存储原始帧数据(未编码的原始图像或音频格式,作为解码器的输出或编码器的输入)。

  • data是一个指针数组,数组的每一个元素是一个指针,指向视频中图像的某一plane或音频中某一声道的plane。
    关于图像plane的详细说明参考“色彩空间与像素格式”,音频plane的详细说明参数“ffplay源码解析6-音频重采样 6.1.1节”。下面简单说明:
    对于packet格式,一幅YUV图像的Y、U、V交织存储在一个plane中,形如YUVYUV...,data[0]指向这个plane;
    一个双声道的音频帧其左声道L、右声道R交织存储在一个plane中,形如LRLRLR...,data[0]指向这个plane。
    对于planar格式,一幅YUV图像有Y、U、V三个plane,data[0]指向Y plane,data[1]指向U plane,data[2]指向V plane;
    一个双声道的音频帧有左声道L和右声道R两个plane,data[0]指向L plane,data[1]指向R plane。

  • linesize 对于视频来说,linesize是每行图像的大小(字节数)。注意有对齐要求。
    对于音频来说,linesize是每个plane的大小(字节数)。音频只使用linesize[0]。对于planar音频来说,每个plane的大小必须一样。
    linesize可能会因性能上的考虑而填充一些额外的数据,因此linesize可能比实际对应的音视频数据尺寸要大。

  • extended_data 对于视频来说,直接指向data[]成员。
    对于音频来说,packet格式音频只有一个plane,一个音频帧中各个声道的采样点交织存储在此plane中;planar格式音频每个声道一个plane。在多声道planar格式音频中,必须使用extended_data才能访问所有声道,什么意思?
    在有效的视频/音频frame中,data和extended_data两个成员都必须设置有效值。

  • width,height视频帧宽和高(像素)

  • nb_samples 音频帧中单个声道中包含的采样点数

  • format 帧格式,如果未知格式或未设置,则为-1.对于视频帧,此值对应于"enum AVPixelFormat"结构。

enum AVPixelFormat {
    AV_PIX_FMT_NONE = -1,
    AV_PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
    AV_PIX_FMT_YUYV422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
    AV_PIX_FMT_RGB24,     ///< packed RGB 8:8:8, 24bpp, RGBRGB...
    AV_PIX_FMT_BGR24,     ///< packed RGB 8:8:8, 24bpp, BGRBGR...
    ......
}

对于音频帧,此值对应于"enum AVSampleFormat"格式

enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double

    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar
    AV_SAMPLE_FMT_S64,         ///< signed 64 bits
    AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar

    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};
  • key_frame 视频帧是否是关键帧的标识,1->关键帧,0->非关键帧。
  • pict_type 视频帧类型(I、B、P等)。如下:
enum AVPictureType {
    AV_PICTURE_TYPE_NONE = 0, ///< Undefined
    AV_PICTURE_TYPE_I,     ///< Intra
    AV_PICTURE_TYPE_P,     ///< Predicted
    AV_PICTURE_TYPE_B,     ///< Bi-dir predicted
    AV_PICTURE_TYPE_S,     ///< S(GMC)-VOP MPEG-4
    AV_PICTURE_TYPE_SI,    ///< Switching Intra
    AV_PICTURE_TYPE_SP,    ///< Switching Predicted
    AV_PICTURE_TYPE_BI,    ///< BI type
};
  • sample_aspect_ratio 视频帧的宽高比。

  • pts 显示时间戳。单位是time_base。

  • pkt_pts 此frame对应的packet中的显示时间戳。是从对应packet(解码生成此frame)中拷贝PTS得到此值。

    此frame对应的packet中的解码时间戳。是从对应packet(解码生成此frame)中拷贝DTS得到此值。
    如果对应的packet中只有dts而未设置pts,则此值也是此frame的pts。

  • coded_picture_number 在编码流中当前图像的序号。

  • display_picture_number 在显示序列中当前图像的序号。

  • interlaced_frame 图像逐行/隔行模式标识。

  • sample_rate 音频采样率。

  • channel_layout 音频声道布局。每bit代表一个特定的声道,参考channel_layout.h中的定义,一目了然。

  • buf

    此帧的数据可以由AVBufferRef管理,AVBufferRef提供AVBuffer引用机制。这里涉及到缓冲区引用计数概念:
    AVBuffer是FFmpeg中很常用的一种缓冲区,缓冲区使用引用计数(reference-counted)机制。
    AVBufferRef则对AVBuffer缓冲区提供了一层封装,最主要的是作引用计数处理,实现了一种安全机制。用户不应直接访问AVBuffer,应通过AVBufferRef来访问AVBuffer,以保证安全。
    FFmpeg中很多基础的数据结构都包含了AVBufferRef成员,来间接使用AVBuffer缓冲区。
    相关内容参考“FFmpeg数据结构AVBuffer
    帧的数据缓冲区AVBuffer就是前面的data成员,用户不应直接使用data成员,应通过buf成员间接使用data成员。那extended_data又是做什么的呢??

    如果buf[]的所有元素都为NULL,则此帧不会被引用计数。必须连续填充buf[] - 如果buf[i]为非NULL,则对于所有j<i,buf[j]也必须为非NULL。
    每个plane最多可以有一个AVBuffer,一个AVBufferRef指针指向一个AVBuffer,一个AVBuffer引用指的就是一个AVBufferRef指针。
    对于视频来说,buf[]包含所有AVBufferRef指针。对于具有多于AV_NUM_DATA_POINTERS个声道的planar音频来说,可能buf[]存不下所有的AVBbufferRef指针,多出的AVBufferRef指针存储在extended_buf数组中。

  • extended_buf&nb_extended_buf

    对于具有多于AV_NUM_DATA_POINTERS个声道的planar音频来说,可能buf[]存不下所有的AVBbufferRef指针,多出的AVBufferRef指针存储在extended_buf数组中。
    注意此处的extended_buf和AVFrame.extended_data的不同,AVFrame.extended_data包含所有指向各plane的指针,而extended_buf只包含AVFrame.buf中装不下的指针。
    extended_buf是构造frame时av_frame_alloc()中自动调用av_malloc()来分配空间的。调用av_frame_unref会释放掉extended_buf。
    nb_extended_buf是extended_buf中的元素数目。

  • best_effort_timestamp

  • pkt_pos 记录最后一个扔进解码器的packet在输入文件中的位置偏移量。

  • pkt_duration 对应packet的时长,单位是AVStream->time_base。

  • channels 音频声道数量。

  • pkt_size 对应packet的大小。

  • crop_ 用于视频帧图像裁切。四个值分别为从frame的上/下/左/右边界裁切的像素数。

      size_t crop_top;
      size_t crop_bottom;
      size_t crop_left;
      size_t crop_right;
    

av_frame_alloc

AVFrame *av_frame_alloc(void);

构造一个frame,对象各成员被设为默认值。
此函数只分配AVFrame对象本身,而不分配AVFrame中的数据缓冲区。

av_frame_free

void av_frame_free(AVFrame **frame);

释放一个frame。

av_frame_ref

int av_frame_ref(AVFrame *dst, const AVFrame *src);

为src中的数据建立一个新的引用。
将src中帧的各属性拷到dst中,并且为src中每个AVBufferRef创建一个新的引用。
如果src未使用引用计数,则dst中会分配新的数据缓冲区,将将src中缓冲区的数据拷贝到dst中的缓冲区。

av_frame_clone

AVFrame *av_frame_clone(const AVFrame *src);

创建一个新的frame,新的frame和src使用同一数据缓冲区,缓冲区管理使用引用计数机制。
本函数相当于av_frame_alloc()+av_frame_ref()。

av_frame_unref

void av_frame_unref(AVFrame *frame);

解除本frame对本frame中所有缓冲区的引用,并复位frame中各成员。

av_frame_move_ref

void av_frame_move_ref(AVFrame *dst, AVFrame *src);

将src中所有数据拷贝到dst中,并复位src。
为避免内存泄漏,在调用av_frame_move_ref(dst, src)之前应先调用av_frame_unref(dst)

av_frame_get_buffer

int av_frame_get_buffer(AVFrame *frame, int align);

为音频或视频数据分配新的缓冲区。
调用本函数前,帧中的如下成员必须先设置好:

  • format (视频像素格式或音频采样格式)
  • width、height(视频画面和宽和高)
  • nb_samples、channel_layout(音频单个声道中的采样点数目和声道布局)

本函数会填充AVFrame.data和AVFrame.buf数组,如果有需要,还会分配和填充AVFrame.extended_data和AVFrame.extended_buf。
对于planar格式,会为每个plane分配一个缓冲区。

av_frame_copy

int av_frame_copy(AVFrame *dst, const AVFrame *src);

将src中的帧数据拷贝到dst中。
本函数并不会有任何分配缓冲区的动作,调用此函数前dst必须已经使用了和src同样的参数完成了初始化。
本函数只拷贝帧中的数据缓冲区的内容(data/extended_data数组中的内容),而不涉及帧中任何其他的属性。

AVPacket

解析文件时会将音/视频帧读入到packet中,对于视频而言,一个AVPacket通常只包含一个压缩视频帧。而对于音频而言,一个AVPacket可能包含多个完整的音频压缩帧。AVPacket也可以不包含压缩编码数据,而只包含side data,这种包可以称为空packet。例如,编码结束后只需要更新一些参数时就可以发空packet。

AVPacket对象可以在栈上分配,注意此处指的是AVPacket对象本身。而AVPacket中包含的数据缓冲区是通过av_malloc()在堆上分配的。

音视频数据缓冲区

  • uint8_t *data:
  • int size:
    数据缓冲区地址与大小。音视频编码压缩数据存储于此片内存区域。此内存区域由AVBufferRef *buf管理。
  • AVBufferRef *buf:
    数据缓冲区引用,也可叫引用计数缓冲区。对上一字段uint8_t *data指向的内存区域提供引用计数等管理机制。
    AVBufferRef对数据缓冲区提供了管理机制,用户不应直接访问数据缓冲区。参考“FFmpeg数据结构AVBuffer
    如果buf值为NULL,则data指向的数据缓冲区不使用引用计数机制。av_packet_ref(dst, src)将执行数据缓冲区的拷贝,而非仅仅增加缓冲区引用计数。
    如果buf值非NULL,则data指向的数据缓冲区使用引用计数机制。av_packet_ref(dst, src)将不拷贝缓冲区,而仅增加缓冲区引用计数。av_packet_unref()将数据缓冲区引用计数减1,当缓冲区引用计数为0时,缓冲区内存被FFmpeg回收。
    对于struct AVPacket pkt对象,如果pkt.buf值非NULL,则有pkt.data == pkt.buf->data == pkt.buf->buffer.data

额外类型数据

  • AVPacketSideData *side_data
  • int side_data_elems
    由容器(container)提供的额外包数据。TODO: 待研究

packet属性

  • AVBufferRef *buf

    用来管理data指针引用的数据缓存,其使用在后面介绍。

  • AVPacketSideData *side_data

    容器提供的一些附加数据

  • int64_t pts:
    显示时间戳。单位time_base,帧率的倒数。

  • int64_t dts:
    解码时间戳。单位time_base,帧率的倒数。

  • uint8_t *data

    指向保存压缩数据的指针,这就是AVPacket的实际数据。

  • int size

    data的大小。

  • int stream_index:
    当前包(packet)所有流(stream)的索引(index)。

  • int flags:
    packet标志位。比如是否关键帧等。

    结合AV_PKT_FLAG使用,其中最低为1表示该数据是一个关键帧。

    #define AV_PKT_FLAG_KEY 0x0001 //关键帧

    #define AV_PKT_FLAG_CORRUPT 0x0002 //损坏的数据

    #define AV_PKT_FLAG_DISCARD 0x0004 /丢弃的数据

  • int side_data_elems:

    边缘数据元数个数。

  • int64_t duration:
    当前包解码后的帧播放持续的时长。单位timebase。值等于下一帧pts减当前帧pts。

  • int64_t pos:
    当前包在流中的位置,单位字节。未知则值 默认为-1。

  • int64_t convergence_duration

    该字段已deprecated,不在使用。

av_packet_alloc

创建AVPacket空间初始化

av_init_packet

初始化AVPacket

av_packet_ref

av_packet_ref()作了处理如下:

  • 如果src->buf为NULL,则将src缓冲区拷贝到新创建的dst缓冲区,注意src缓冲区不支持引用计数,但新建的dst缓冲区是支持引用计数的,因为dst->buf不为NULL。
  • 如果src->buf不为空,则dst与src共用缓冲区,调用av_buffer_ref()增加缓冲区引用计数即可。av_buffer_ref()分析参考“FFmpeg数据结构AVBuffer

av_packet_clone

创建AVPacket复制,先创建一个新的AVPacket,然后再进行计数引用+数据拷贝,使得新的AVPacket指向老的AVPacket同一个data。

av_copy_packet

复制一个新的packet,包括数据缓存。

av_packet_unref

void av_packet_unref(AVPacket *pkt)
{
    av_packet_free_side_data(pkt);
    av_buffer_unref(&pkt->buf);
    av_init_packet(pkt);
    pkt->data = NULL;
    pkt->size = 0;
}

av_packet_unref()注销AVPacket *pkt对象,并调用av_buffer_unref(&pkt->buf);将缓冲区引用计数减1。
av_buffer_unref()中将缓冲区引用计数减1后,若缓冲区引用计数变成0,则回收缓冲区内存。av_buffer_unref()分析

av_packet_free

void av_packet_free(AVPacket **pkt);

av_packet_free是先把pkt中的内容清空,然后再把指针清空,让pkt彻底无法使用了,如果需要重新使用,需要重新分配内存

av_free_packet

void av_free_packet(AVPacket *pkt);

av_free_packet其实就是清空pkt中data以及buf的内容,并没有把pkt的指针清空,我们可以看到其函数内部调用了av_buffer_unref。释放packet,包括其data引用的数据缓存,现在可以使用av_packet_unref代替。

av_packet_from_data

av_packet_from_data(AVPacket *pkt, uint8_t *data, int size)

初始化一个引用计数的packet,并指定了其数据缓存。

av_dup_packet

通过调用 av_malloc、memcpy、memset等函数, 将shared buffer 的AVPacket duplicate(复制)到独立的buffer中。并且修改AVPacket的析构函数指针av_destruct_pkt。(此函数已被弃用),是复制src->data引用的数据缓存,赋值给dst。也就是创建两个独立packet,这个功能现在可用使用函数av_packet_ref来代替。

av_grow_packet

int av_grow_packet(AVPacket *pkt, int grow_by);

增大Packet->data指向的数据缓存。

av_shrink_packet

void av_shrink_packet(AVPacket *pkt, int size)

减小Packet->data指向的数据缓存。

AVDictionary

av_dict_free

void av_dict_free(AVDictionary **mm)

释放AVDictionary.

AVIOContext

其中AVIOContext是FFMPEG管理输入输出数据的结构体。(位于avio.h)。AVIOContext中有以下几个变量比较重要:
unsigned char *buffer:缓存开始位置

int buffer_size:缓存大小(默认32768)

unsigned char *buf_ptr:当前指针读取到的位置

unsigned char *buf_end:缓存结束的位置

void *opaque:URLContext结构体

avio_alloc_context

SwsContext

swr_init

初始化SwsContext

swr_alloc

申请空间,音频重采样上下文

swr_alloc_set_opts

设置音频重采样

swr_free

释放SwsContext空间

swr_convert

重采样

sws_scale

sws_scale函数主要是用来做视频像素格式和分辨率的转换,其优势在于:可以在同一个函数里实现:1.图像色彩空间转换, 2:分辨率缩放,3:前后图像滤波处理。不足之处在于:效率相对较低,不如libyuv或OpenGL_Shader。

sws_getContext

struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH, enum AVPixelFormat dstFormat,
                                  int flags, SwsFilter *srcFilter,
                                  SwsFilter *dstFilter, const double *param);

sws_getCachedContext

struct SwsContext *sws_getCachedContext(struct SwsContext *context,
                                        int srcW, int srcH, enum AVPixelFormat srcFormat,
                                        int dstW, int dstH, enum AVPixelFormat dstFormat,
                                        int flags, SwsFilter *srcFilter,
                                        SwsFilter *dstFilter, const double *param);

AVFilter

AVFilter库提供了一个通用的音频,视频,字幕等滤镜处理框架。在AVFilter中,有多个输入和多个输出。

AVBuffer

struct AVBuffer定义于“libavutil/buffer_internal.h”,buffer_internal.h位于FFmpeg工程源码中,而FFmpeg提供的开发库头文件中并无此文件,因此这是一个内部数据结构,不向用户开放,用户不应直接访问AVBuffer,应通过AVBufferRef来访问AVBuffer,以保证安全。

struct AVBuffer {
    uint8_t *data; /**< data described by this buffer */
    int      size; /**< size of data in bytes */

    /**
     *  number of existing AVBufferRef instances referring to this buffer
     */
    atomic_uint refcount;

    /**
     * a callback for freeing the data
     */
    void (*free)(void *opaque, uint8_t *data);

    /**
     * an opaque pointer, to be used by the freeing callback
     */
    void *opaque;

    /**
     * A combination of BUFFER_FLAG_*
     */
    int flags;
};
  • data: 缓冲区地址
  • size: 缓冲区大小
  • refcount: 引用计数值
  • free: 用于释放缓冲区内存的回调函数
  • opaque: 提供给free回调函数的参数
  • flags: 缓冲区标志

AVBufferRef

struct AVBufferRef定义于buffer.h中:

/**
 * A reference to a data buffer.
 *
 * The size of this struct is not a part of the public ABI and it is not meant
 * to be allocated directly.
 */
typedef struct AVBufferRef {
    AVBuffer *buffer;

    /**
     * The data buffer. It is considered writable if and only if
     * this is the only reference to the buffer, in which case
     * av_buffer_is_writable() returns 1.
     */
    uint8_t *data;
    /**
     * Size of data in bytes.
     */
    int      size;
} AVBufferRef;
  • buffer: AVBuffer
  • data: 缓冲区地址,实际等于buffer->data
  • size: 缓冲区大小,实际等于buffer->size

av_buffer_alloc

AVBufferRef *av_buffer_alloc(int size)
{
    AVBufferRef *ret = NULL;
    uint8_t    *data = NULL;

    data = av_malloc(size);
    if (!data)
        return NULL;

    ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
    if (!ret)
        av_freep(&data);

    return ret;
}

av_buffer_alloc()作了如下处理:
a) 使用av_malloc分配缓冲区
b) 调用av_buffer_create()创建AVBuffer AVBufferRef::*buffer成员,用于管理AVBuffer缓冲区
c) 返回AVBufferRef *对象

av_buffer_create

AVBufferRef *av_buffer_create(uint8_t *data, int size,
                              void (*free)(void *opaque, uint8_t *data),
                              void *opaque, int flags)
{
    AVBufferRef *ref = NULL;
    AVBuffer    *buf = NULL;

    buf = av_mallocz(sizeof(*buf));
    if (!buf)
        return NULL;

    buf->data     = data;
    buf->size     = size;
    buf->free     = free ? free : av_buffer_default_free;
    buf->opaque   = opaque;

    atomic_init(&buf->refcount, 1);

    if (flags & AV_BUFFER_FLAG_READONLY)
        buf->flags |= BUFFER_FLAG_READONLY;

    ref = av_mallocz(sizeof(*ref));
    if (!ref) {
        av_freep(&buf);
        return NULL;
    }

    ref->buffer = buf;
    ref->data   = data;
    ref->size   = size;

    return ref;
}

av_buffer_create()是一个比较核心的函数,从其实现代码很容易看出AVBufferRef和AVBuffer这间的关系。
函数主要功能就是初始化AVBuffer AVBufferRef::*buffer成员,即为上述清单ref->buffer各字段赋值,最终,AVBufferRef *ref全部构造完毕,将之返回。

其中void (*free)(void *opaque, uint8_t *data)参数赋值为av_buffer_default_free,实现如下。其实就是直接调用了av_free回收内存。

void av_buffer_default_free(void *opaque, uint8_t *data)
{
    av_free(data);
}

av_buffer_ref

AVBufferRef *av_buffer_ref(AVBufferRef *buf)
{
    AVBufferRef *ret = av_mallocz(sizeof(*ret));

    if (!ret)
        return NULL;

    *ret = *buf;

    atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed);

    return ret;
}

av_buffer_ref()处理如下:
a) *ret = *buf;一句将buf各成员值赋值给ret中对应成员,buf和ret将共用同一份AVBuffer缓冲区
b) atomic_fetch_add_explicit(...);一句将AVBuffer缓冲区引用计数加1
注意此处的关键点:共用缓冲区(缓冲区不拷贝),缓冲区引用计数加1

av_buffer_unref

static void buffer_replace(AVBufferRef **dst, AVBufferRef **src)
{
    AVBuffer *b;

    b = (*dst)->buffer;

    if (src) {
        **dst = **src;
        av_freep(src);
    } else
        av_freep(dst);

    if (atomic_fetch_add_explicit(&b->refcount, -1, memory_order_acq_rel) == 1) {
        b->free(b->opaque, b->data);
        av_freep(&b);
    }
}

void av_buffer_unref(AVBufferRef **buf)
{
    if (!buf || !*buf)
        return;

    buffer_replace(buf, NULL);
}

av_buffer_unref()处理如下:
a) 回收AVBufferRef **buf内存
b) 将(*buf)->buffer(即AVBAVBufferRef的成员AVBuffer)的引用计数减1,若引用计数为0,则通过b->free(b->opaque, b->data);调用回调函数回收AVBuffer缓冲区内存
注意此处的关键点:销毁一个AVBufferRef时,将其AVBuffer缓冲区引用计数减1,若缓冲区引用计数变为0,则将缓冲区也回收,这很容易理解,只有当缓冲区不被任何对象引用时,缓冲区才能被销毁

AVUtil

AVUtil是一个实用库,用于辅助多媒体编程。此库包含安全的可移植字符串函数、随机数生成器、数据结构、附加数学函数、加密和多媒体相关功能(如像素和样本格式的枚举)。libavcodec和libavformat并不依赖此库。

FFmpeg function

av_register_all

定义在 libavformat 里,调用它用以注册所有支持的文件格式以及编解码器,从其实现代码里可以看到它会调用
avcodec_register_all,因此之后就可以用所有ffmpeg支持的codec了。

avformat_network_init

初始化网络

avcodec_register_all

注册所有解码器

av_find_best_stream

int av_find_best_stream(AVFormatContext *ic,
                        enum AVMediaType type,
                        int wanted_stream_nb,
                        int related_stream,
                        AVCodec **decoder_ret,
                        int flags);

获取音视频对应的stream_index。其实,还有另外一种方法来获得音视频对应的stream_index

avio_open

int avio_open(AVIOContext **s, const char *filename, int flags)
{  
    return avio_open2(s, filename, flags, NULL, NULL);  
}  

是avio_open2的早期版本,比avio_open2少了最后2个参数。而它前面几个参数的含义和avio_open2是一样的。从源代码中可以看出,avio_open内部调用了avio_open2,并且把avio_open2的后2个参数设置成了NULL,因此它的功能实际上和avio_open2是一样的。

avio_open2

int avio_open2(AVIOContext **s, const char *url, int flags,  
               const AVIOInterruptCB *int_cb, AVDictionary **options);

该函数用于打开FFmpeg的输入输出文件。avio_open2()的声明位于libavformat\avio.h文件中。

  • s:函数调用成功之后创建的AVIOContext结构体。
  • url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
  • flags:打开地址的方式。可以选择只读,只写,或者读写。取值如下。
    • AVIO_FLAG_READ:只读。
    • AVIO_FLAG_WRITE:只写。
    • AVIO_FLAG_READ_WRITE:读写。
  • int_cb:目前还没有用过。
  • options:目前还没有用过

av_packet_rescale_ts

avformat_write_header

写入头文件信息。

av_write_frame

av_interleaved_write_frame

av_write_trailer

int av_write_trailer(AVFormatContext *s); 

用于输出 文件尾,它的声明位于libavformat\avformat.h

av_read_frame

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

从AVFormatContext中读取数据到pkt中。

av_guess_format

AVOutputFormat *av_guess_format(const char *short_name,
                                const char *filename,
                                const char *mime_type);
  • short_name:格式的名称。
  • filename:文件的名称。
  • mime_type:MIME类型。

返回最匹配的AVOutputFormat,如果没有很匹配的AVOutputFormat,则返回NULL。

av_oformat_netx

AVOutputFormat *av_oformat_next(const AVOutputFormat *f);

参数不为NULL的时候用于获得下一个AVOutputFormat否则获得第一个AVOutputFormat.

av_match_name

int av_match_name(const char *name, const char *names);

用于比较两个格式的名称。简单地说就是比较字符串。注意该函数的字符串是不区分大小写的:字符都转换为小写进行比较。

av_match_ext

int av_match_ext(const char *filename, const char *extensions);

用于比较文件的后缀。该函数首先通过反向查找的方式找到输入文件名中的“.”,就可以通过获取“.”后面的字符串来得到该文件的后缀。然后调用av_match_name(),采用和比较格式名称的方法比较两个后缀。

av_dump_format

最后调用一个帮助函数av_dump_format,输出文件的信息,也就是我们在使用ffmpeg时能看到的文件详细信息。第二个参数指定输出哪条流的信息,-1表示给ffmpeg自己选择。最后一个参数用于指定dump的是不是输出文件,我们dump的是输入文件,因此一定要是0。

av_image_fill_arrays

int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
                         const uint8_t *src,
                         enum AVPixelFormat pix_fmt, int width, int height, int align);

av_image_get_buffer_size

int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);

获取计算得到的buffer空间size.

posted @ 2024-06-30 17:25  alvinlyb  阅读(14)  评论(0编辑  收藏  举报