目录

0 前言

1 avformat.h

1.1  重要的结构体 && API

1.1.1 lavf  && av_register_all && avformat_network_init

1.1.2  AVInputFormat && AVOutputFormat && av_iformat_next && av_oformat_next && avio_enum_protocols

1.1.3  AVFormatContext && avformat_alloc_context && avformat_open_input

1.1.4  AVFormatContext(.iformat,.oformat,.streams,.pb)

1.1.5  AVOptions mechanism

1.1.6  Urls

1.2 解封装

1.2.1 Demuxing

1.2.2  avformat_open_input && avformat_alloc_context  

1.2.3  avformat_open_input && options

1.2.4  av_read_frame() && AVPacket

1.3 封装

1.3.1 Muxer

1.3.2  avformat_write_header() 

1.3.3 av_write_frame() && av_interleaved_write_frame()

1.3.4  av_write_trailer && avformat_free_context

1.4 目录访问

1.4.1  avio_open_dir && AVIODirContext 

1.4.1  avio_read_dir&& AVIODirEntry

2  总结


0 前言


libavformat库是FFMPEG的I/O以及Muxer/Demuxer库,主要由3个头文件avio.h,version.h,avformat.h以及一堆的源文件组成。其中avformat.h是最核心的,他是libavformat库的Public API头文件。本文就是基于该头文件所提供的信息整理而成。

1 avformat.h


avformat.h文件的前面提供了大量的文字说明,一方面告知该库提供了哪些重要的结构体,这些结构体在音视频中所代表的含义;另一方面简要告知关于I/O Muxer/Demuxer的重要API,这些API如何使用以及注意事项。以下就这些文字说明进行拆分介绍,从而对该库进行初步的,整体性的了解。

1.1  重要的结构体 && API


1.1.1 lavf  && av_register_all && avformat_network_init


从以下描述中提炼出以下信息:

  1. avformat.h是libavformat库的Public API头文件;
  2. avformat.h包含I/O功能,Muxing/Demuxing功能;
  3. lavf是Libavformat库的简称,这个在很多地方都会出现,相当重要;
  4. lavf可以处理各种各样的容器封装;
  5. lavf主要目的就是解封装(分割媒体文件信息到流),以及相反的处理,进行封装(将媒体数据写入特定的容器封装中);
  6. lavf包含了I/O模块,“lavf_io”,用以支持各种获取数据的协议,比如本地文件file协议,tcp协议,http协议或者其他;
  7. 在使用lavf之前,需要调用av_register_all()来注册所有已经编译进来的封装器/解封装器,协议/解协议器,详见FFRMPEG4.1源码分析之 av_register_all()
  8. 除非你明确的知道你不会使用lavf的网络访问能力,否则你还得调用avfromat_network_init()函数来初始化lavf的网络功能。
/**
 * @file
 * @ingroup libavf
 * Main libavformat public API header
 */

/**
 * @defgroup libavf libavformat
 * I/O and Muxing/Demuxing Library
 *
 * Libavformat (lavf) is a library for dealing with various media container
 * formats. Its main two purposes are demuxing - i.e. splitting a media file
 * into component streams, and the reverse process of muxing - writing supplied
 * data in a specified container format. It also has an @ref lavf_io
 * "I/O module" which supports a number of protocols for accessing the data (e.g.
 * file, tcp, http and others). Before using lavf, you need to call
 * av_register_all() to register all compiled muxers, demuxers and protocols.
 * Unless you are absolutely sure you won't use libavformat's network
 * capabilities, you should also call avformat_network_init().

1.1.2  AVInputFormat && AVOutputFormat && av_iformat_next && av_oformat_next && avio_enum_protocols


从以下关于输入文件格式,输出文件格式,协议的描述提炼以下几点:

  1. 支持的输入文件格式以AVInputFormat结构体描述;
  2. 支持的输出文件格式以AVOutputFormat结构体描述;
  3. 使用API方法 av_iformat_next()和 av_oformat_next()可以迭代所有的已注册的输入和输出文件格式;
  4. protocols协议层不是Public API的部分,因此,关于协议的多数接口都是ffmpeg内部使用,不对外暴露,你只能通过avio_enum_protocols()来获取支持的协议名
 * A supported input format is described by an AVInputFormat struct, conversely
 * an output format is described by AVOutputFormat. You can iterate over all
 * registered input/output formats using the av_iformat_next() /
 * av_oformat_next() functions. The protocols layer is not part of the public
 * API, so you can only get the names of supported protocols with the
 * avio_enum_protocols() function.

1.1.3  AVFormatContext && avformat_alloc_context && avformat_open_input


从lavf库的主要结构体AVFormatContext描述中可以得知:

  1. AVFormatContext是lavf库最重要的结构体,封装和解封装都使用到,其包含了文件如何读取,如何写入的所有必要信息;
  2. 和绝大多数的lavf库中的其他结构体一样,它的大小并不是公共应用程序二进制接口(public ABI)的一部分,因此AVFormatContext不能分配在栈上,也不能直接使用av_malloc来分配;
  3. avformat_alloc_context()是用来创建AVFormatContext对象的方法,当然还有其他方法,比如avformat_open_input()也会在必要条件下(传入的AVFormatContext为空的情况下),创建AVFormatContext对象,其内部也是调用avformat_alloc_context方法来创建AVFormatContext对象。
 * Main lavf structure used for both muxing and demuxing is AVFormatContext,
 * which exports all information about the file being read or written. As with
 * most Libavformat structures, its size is not part of public ABI, so it cannot be
 * allocated on stack or directly with av_malloc(). To create an
 * AVFormatContext, use avformat_alloc_context() (some functions, like
 * avformat_open_input() might do that for you).

1.1.4  AVFormatContext(.iformat,.oformat,.streams,.pb)


以下是对AVFormatContext结构体中最重要的几个成员的描述:

  1. AVFormatContext.iformat是输入文件格式,类型AVInputFormat,解封装的时候由用户指定格式或者自动检测文件格式
  2. AVFormatContext.oformat是输出文件格式,类型AVOutputFormat,封装的时候由用户指定格式
  3. AVFormatContext.streams是AVStreams数组,描述了文件中的音视频流。AVStreams 的引用方式一般就是通过数组下标来访问;
  4. AVFormatContext.pb是I/O上下文对象,类型AVIOContext。对于解封装(输入文件),AVIOContext要么是lavf 库内部来打开,要么是用户主动设置;对于封装(输出文件),总是用户主动来设置的,除非处理的是AVFMT_NOFILE非文件格式(其实就是输出到设备)。
 * Most importantly an AVFormatContext contains:
 * @li the @ref AVFormatContext.iformat "input" or @ref AVFormatContext.oformat
 * "output" format. It is either autodetected or set by user for input;
 * always set by user for output.
 * @li an @ref AVFormatContext.streams "array" of AVStreams, which describe all
 * elementary streams stored in the file. AVStreams are typically referred to
 * using their index in this array.
 * @li an @ref AVFormatContext.pb "I/O context". It is either opened by lavf or
 * set by user for input, always set by user for output (unless you are dealing
 * with an AVFMT_NOFILE format).

1.1.5  AVOptions mechanism


如何配置封装和解封装过程的相关参数需要通过所谓的avoptions机制:

  1. ffmpeg中提供了avoptions机制来配置封装/解封装相关的参数,这个机制核心概念就是“根据字符串操作结构体的属性值”
  2. 一般的(文件格式无关的属性)lavf属性由AVFormatContext结构体提供,可以对已分配AVInputFormat对象或者是AVFormatContext的AVClass对象(通过avformat_get_class方法获取),使用av_opt_next()或者是av_opt_find()方法进行属性的查找检测;
  3. 私有的(文件格式相关的属性)lavf属性由AVFormatContext.priv_data提供,并且仅在AVInputFormat.priv_class或者是AVOutputFormat.priv_class相关结构体非空的情况下;
  4. 更多的选项(协议层,协议相关的选项),可能由AVFormatContext.pb(AVIOContext)来提供,如果其AVClass不为空的情况下;
 * @section lavf_options Passing options to (de)muxers
 * It is possible to configure lavf muxers and demuxers using the @ref avoptions
 * mechanism. Generic (format-independent) libavformat options are provided by
 * AVFormatContext, they can be examined from a user program by calling
 * av_opt_next() / av_opt_find() on an allocated AVFormatContext (or its AVClass
 * from avformat_get_class()). Private (format-specific) options are provided by
 * AVFormatContext.priv_data if and only if AVInputFormat.priv_class /
 * AVOutputFormat.priv_class of the corresponding format struct is non-NULL.
 * Further options may be provided by the @ref AVFormatContext.pb "I/O context",
 * if its AVClass is non-NULL, and the protocols layer. See the discussion on
 * nesting in @ref avoptions documentation to learn how to access those.

    /**
     * Format private data. This is an AVOptions-enabled struct
     * if and only if iformat/oformat.priv_class is not NULL.
     *
     * - muxing: set by avformat_write_header()
     * - demuxing: set by avformat_open_input()
     */
    void *priv_data;

1.1.6  Urls


urls相关:

  1.  ffmpeg中URL串由协议名 + ":" + 协议相关字符串组成。
  2. URLs中没有协议名 + ":"被认为是本地文件,虽然支持这种格式,但是被认为是过时的,因此最好使用 "file:",即file协议来访问本地文件
  3. 协议串不要从不被信任,并且没有经过检查的地方获取
  4. 注意:有些协议很强大,不仅允许访问本地文件,还允许访问远端文件。
 * @section urls
 * URL strings in libavformat are made of a scheme/protocol, a ':', and a
 * scheme specific string. URLs without a scheme and ':' used for local files
 * are supported but deprecated. "file:" should be used for local files.
 *
 * It is important that the scheme string is not taken from untrusted
 * sources without checks.
 *
 * Note that some schemes/protocols are quite powerful, allowing access to
 * both local and remote files, parts of them, concatenations of them, local
 * audio and video devices and so on.

1.2 解封装


1.2.1 Demuxing


解封装的过程简单介绍:

  1. 解封装器将读取媒体文件,并将其分割成数据块,即AVPackets对象。一个AVPacket对象包含属于同一个流的一帧或多帧被编码的数据。
  2. 在lavf中,上诉过程由avformat_open_input()这个函数来打开一个文件,av_read_frame()来读取单个packet,最终用avformat_close_input()来进行关闭和最后的清理。
 * @defgroup lavf_decoding Demuxing
 * @{
 * Demuxers read a media file and split it into chunks of data (@em packets). A
 * @ref AVPacket "packet" contains one or more encoded frames which belongs to a
 * single elementary stream. In the lavf API this process is represented by the
 * avformat_open_input() function for opening a file, av_read_frame() for
 * reading a single packet and finally avformat_close_input(), which does the
 * cleanup.

1.2.2  avformat_open_input && avformat_alloc_context  


关于avformat_alloc_context以及avformat_open_input的部分论述,非常重要的点:

  1. avformat_open_input()方法用于打开一个文件,其需要的最小信息就是文件的URL,如下示例中所示
  2. 示例中的 avformat_open_input() 方法会在内部调用 avformat_alloc_context() 方法去创建一个AVFormatContext 对象;会探测这个文件的文件格式,并打开这个指定的文件,读取文件头,将读取的信息填充到AVFormatContext的字段中去。注意注意注意!!!由于有的文件格式没有文件头,或者文件头中并没有存储足够的有效信息,那么推荐在使用avformat_open_input() 之后再调用 avformat_find_stream_info() 方法该方法会读取并解码一些帧,去获取足够的信息。
  3. 虽然avformat_open_input()方法会主动创建AVFormatContext,但是有些情形下需要预创建AVFormatContext对象,并在其上做些操作之后再传递给avformat_open_input()方法。比如:想要使用自己的读取数据的IO方法,而不使用lavf内部I/O层的方法。这样就需要使用avio_alloc_context()方法创建自己的AVIOContext对象,传递自己的读取数据callbacks方法给自己创建的AVIOContext,然后将AVIOContext对象赋值给预先创建的AVFormatContext对象的pd字段。
 * @section lavf_decoding_open Opening a media file
 * The minimum information required to open a file is its URL, which
 * is passed to avformat_open_input(), as in the following code:
 * @code 示例
 * const char    *url = "file:in.mp3";
 * AVFormatContext *s = NULL;
 * int ret = avformat_open_input(&s, url, NULL, NULL);
 * if (ret < 0)
 *     abort();
 * @endcode
 * The above code attempts to allocate an AVFormatContext, open the
 * specified file (autodetecting the format) and read the header, exporting the
 * information stored there into s. Some formats do not have a header or do not
 * store enough information there, so it is recommended that you call the
 * avformat_find_stream_info() function which tries to read and decode a few
 * frames to find missing information.
 *
 * In some cases you might want to preallocate an AVFormatContext yourself with
 * avformat_alloc_context() and do some tweaking on it before passing it to
 * avformat_open_input(). One such case is when you want to use custom functions
 * for reading input data instead of lavf internal I/O layer.
 * To do that, create your own AVIOContext with avio_alloc_context(), passing
 * your reading callbacks to it. Then set the @em pb field of your
 * AVFormatContext to newly created AVIOContext.

1.2.3  avformat_open_input && options


关于avformat_open_input以及选项传递:

  1. 一般情况下(用户未指明输入文件格式),只有在avformat_open_input()方法返回后才知道输入文件格式,因此,这之前是没法设置预创建的AVFormatContext的关于解封装的私有选项信息,之前提到过通用的信息直接存储在AVFormatContext的直接成员字段中,格式相关的信息存储在AVInputFormat和AVOutputFormat结构中(当然,这两个结构体也是AVFormatContext的直接成员字段,我的意思,大家应该懂的)。那么用户传递进来的选项设置如何生效呢?答案是将这些选项装进一个AVDictionary对象中,并以入参的形式传递给avformat_open_input();
  2. 示例中传递两个选项参数,分别是视频分辨率video_size和像素格式pixel_format。大家想想这两个参数在是什么时候会起到作用?比如我们解码数据是原始视频数据,这些数据并没有包含描述数据本身的metadata信息,因此就需要外部额外的给出信息才能正常解码,这些信息就通过该参数传递。注意:如果对于此示例来说,需要解码的不是原始视频数据,而是其他格式数据,那么传入的选项信息将不会被解封装器AVInputFormat识别,也就不会生效。被识别的选项将被消费掉,无法识别的选项信息仍通过该参数传递出来(由于该参数传递的是引用),因此用户还可通过该参数获知哪些选项是没有生效的,可以做相应的处理,比如后面这个示例所示。
  3. 当结束文件读取的时候,需要调用avformat_close_input()来关闭和清理所有关于该文件的一切资源。
 * Since the format of the opened file is in general not known until after
 * avformat_open_input() has returned, it is not possible to set demuxer private
 * options on a preallocated context. Instead, the options should be passed to
 * avformat_open_input() wrapped in an AVDictionary:
 * @code
 * AVDictionary *options = NULL;
 * av_dict_set(&options, "video_size", "640x480", 0);
 * av_dict_set(&options, "pixel_format", "rgb24", 0);
 *
 * if (avformat_open_input(&s, url, NULL, &options) < 0)
 *     abort();
 * av_dict_free(&options);
 * @endcode
 * This code passes the private options 'video_size' and 'pixel_format' to the
 * demuxer. They would be necessary for e.g. the rawvideo demuxer, since it
 * cannot know how to interpret raw video data otherwise. If the format turns
 * out to be something different than raw video, those options will not be
 * recognized by the demuxer and therefore will not be applied. Such unrecognized
 * options are then returned in the options dictionary (recognized options are
 * consumed). The calling program can handle such unrecognized options as it
 * wishes, e.g.
 * @code
 * AVDictionaryEntry *e;
 * if (e = av_dict_get(options, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
 *     fprintf(stderr, "Option %s not recognized by the demuxer.\n", e->key);
 *     abort();
 * }
 * @endcode
 *
 * After you have finished reading the file, you must close it with
 * avformat_close_input(). It will free everything associated with the file.

1.2.4  av_read_frame() && AVPacket


关于读取原始数据帧,以及相应的结构体AVPacket:

  1. 从已打开的文件中读取数据(从打开的AVFormatContext中),是通过对AVFormatContext反复调用av_read_frame()来实现。每次av_read_frame()的调用,如果成功,将会返回一个AVPacket对象,内含某一个流AVStream的编码过的数据,可以通过AVPacket.stream_index来识别属于哪个流;
  2. 如果希望进行解码操作,可以将读取的AVPacket可以直接传递给libavcode库的解码函数avcodec_send_packet()或者avcodec_decode_subtitle2()
  3. AVPacket的三个关于时间的信息:AVPacket.pts(播放时间戳),AVPacket.dts(解码时间戳),AVPacket.duration(包持续时长)在av_read_frame()读取过程中会被设置,如果流没有提供相关信息,那么pts和dts将为AV_NOPTS_VALUE,duration为0。注意:AVPacket的时间信息是以AVStream.time_base为时间基,pts/dts/duration可以通过乘以时间基转换到以s为单位的度量上来。
  4. 注意注意注意!!! AVPacket.buf如果被赋值,则packet是动态分配的,用户可以可以无限期的使用该packet。否则,AVPacket.buf为空,AVPacket,那么AVPacket数据是引用的解封装器中的静态存储,其仅仅在下次av_read_frame()调用之前或者是关闭文件之前有效。如果调用者希望得到一个更长生命周期的AVPacket,那么使用av_dup_packet()将分配一个新的拷贝。在上诉两种情况之下,都需要使用av_packet_unref()函数去释放AVPacket对象。
 * @section lavf_decoding_read Reading from an opened file
 * Reading data from an opened AVFormatContext is done by repeatedly calling
 * av_read_frame() on it. Each call, if successful, will return an AVPacket
 * containing encoded data for one AVStream, identified by
 * AVPacket.stream_index. This packet may be passed straight into the libavcodec
 * decoding functions avcodec_send_packet() or avcodec_decode_subtitle2() if the
 * caller wishes to decode the data.
 *
 * AVPacket.pts, AVPacket.dts and AVPacket.duration timing information will be
 * set if known. They may also be unset (i.e. AV_NOPTS_VALUE for
 * pts/dts, 0 for duration) if the stream does not provide them. The timing
 * information will be in AVStream.time_base units, i.e. it has to be
 * multiplied by the timebase to convert them to seconds.
 *
 * If AVPacket.buf is set on the returned packet, then the packet is
 * allocated dynamically and the user may keep it indefinitely.
 * Otherwise, if AVPacket.buf is NULL, the packet data is backed by a
 * static storage somewhere inside the demuxer and the packet is only valid
 * until the next av_read_frame() call or closing the file. If the caller
 * requires a longer lifetime, av_dup_packet() will make an av_malloc()ed copy
 * of it.
 * In both cases, the packet must be freed with av_packet_unref() when it is no
 * longer needed.

1.3 封装


1.3.1 Muxer


封装的基本过程:

  1. 封装是将编码后的数据(以AVPacket结构表示)写入文件,或者以输出他字节流形式写入指定的封装容器;
  2. 封装涉及的主要的API为avformat_write_header()用来写入文件头,av_write_frame() / av_interleaved_write_frame()用来写入packets(音视频数据),av_write_trailer()用来结束文件写入;
  3. 在封装过程之前,首先需要使用avformat_alloc_context()来创建一个进行封装的上下文对象AVFormatContext,通过对该上下文对象的成员字段设置来初始化封装器。这些必要的字段如 4 5 6 7 8 所描述;
  4. AVFormatContext.oformat:AVOutputFormat对象,必须设置,这个表征的是封装器;
  5. AVFormatContext.pb:AVIOContext对象,除非文件格式是AVFMT_NOFILE类型,否则pb字段必须设置为一个“已打开”的IO上下文对象,这对象要么是由avio_open2()返回,要么是用户来设置一个;
  6. AVFormatContext.streams: 除非封装格式是AVFMT_NOSTREAMS类型,否则至少要调用 avformat_new_stream() 方法创建一个AVStream;调用者比如填充AVStream.codecpar(AVCodecParameters)的信息,比如AVCodecParameters.codec_type,AVCodecParameters.codec_id,以及其他参数比AVCodecParameters.width,AVCodecParameters.height(视频宽高),AVCodecParameters.format(像素格式,音频采样格式);AVStream.time_base将被设置为用户期望该流所使用的时间基(封装器实际所使用的时间基可能会与流中的时间基可能不同);
  7. AVFormatContext.streams.codecpar: 建议只初始化AVCodecParameters必要的字段,而不推荐使用avcodec_parameters_copy()从解封装的AVCodecParameters对象中来生成一份拷贝,因为无法保证同一份AVCodecParameters中的所有字段对于封装和解封装的都是合法的;
  8. AVFormatContext的其他成员:调用着还可以设置AVFormatContext的其他字段,比如AVFormatContext.metadata,AVFormatContext.chapters ,AVFormatContext.programs,AVFormatContext.steams.metadata等。设置的这些信息是否最终生效,取决于对应的封装容器格式和封装是否支持。
 * @defgroup lavf_encoding Muxing
 * @{
 * Muxers take encoded data in the form of @ref AVPacket "AVPackets" and write
 * it into files or other output bytestreams in the specified container format.
 *
 * The main API functions for muxing are avformat_write_header() for writing the
 * file header, av_write_frame() / av_interleaved_write_frame() for writing the
 * packets and av_write_trailer() for finalizing the file.
 *
 * At the beginning of the muxing process, the caller must first call
 * avformat_alloc_context() to create a muxing context. The caller then sets up
 * the muxer by filling the various fields in this context:
 *
 * - The @ref AVFormatContext.oformat "oformat" field must be set to select the
 *   muxer that will be used.
 * - Unless the format is of the AVFMT_NOFILE type, the @ref AVFormatContext.pb
 *   "pb" field must be set to an opened IO context, either returned from
 *   avio_open2() or a custom one.
 * - Unless the format is of the AVFMT_NOSTREAMS type, at least one stream must
 *   be created with the avformat_new_stream() function. The caller should fill
 *   the @ref AVStream.codecpar "stream codec parameters" information, such as the
 *   codec @ref AVCodecParameters.codec_type "type", @ref AVCodecParameters.codec_id
 *   "id" and other parameters (e.g. width / height, the pixel or sample format,
 *   etc.) as known. The @ref AVStream.time_base "stream timebase" should
 *   be set to the timebase that the caller desires to use for this stream (note
 *   that the timebase actually used by the muxer can be different, as will be
 *   described later).
 * - It is advised to manually initialize only the relevant fields in
 *   AVCodecParameters, rather than using @ref avcodec_parameters_copy() during
 *   remuxing: there is no guarantee that the codec context values remain valid
 *   for both input and output format contexts.
 * - The caller may fill in additional information, such as @ref
 *   AVFormatContext.metadata "global" or @ref AVStream.metadata "per-stream"
 *   metadata, @ref AVFormatContext.chapters "chapters", @ref
 *   AVFormatContext.programs "programs", etc. as described in the
 *   AVFormatContext documentation. Whether such information will actually be
 *   stored in the output depends on what the container format and the muxer
 *   support.
 *

1.3.2  avformat_write_header() 


写入文件头信息:

  1. 当封装上下文对象AVFormatContext完全建立后,调用着需要调用avformat_write_header()方法去初始化封装其内部并写入文件头。这一步是否真的有数据写入到IO上下文中,取决于封装器,但是这个方法必须总是被调用。一些封装器私有选项options必须通过该函数的参数来传入。
 * When the muxing context is fully set up, the caller must call
 * avformat_write_header() to initialize the muxer internals and write the file
 * header. Whether anything actually is written to the IO context at this step
 * depends on the muxer, but this function must always be called. Any muxer
 * private options must be passed in the options parameter to this function.

1.3.3 av_write_frame() && av_interleaved_write_frame()


音视频数据写入:

  1. 写入文件头后,音视频数据通过重复调用 av_write_frame() 或者 av_interleaved_write_frame() 将数据写入封装,这两个函数的区别将在其他文章中进行分析,但注意一点,二者不能同时混用。
  2. 注意:写入包AVPacket中的时间信息应该是以AVStream.time_base为时间基的。avformat_write_header()所设置的封装器使用的时间基与用户设置的流中的时间基可能不同。
 *
 * The data is then sent to the muxer by repeatedly calling av_write_frame() or
 * av_interleaved_write_frame() (consult those functions' documentation for
 * discussion on the difference between them; only one of them may be used with
 * a single muxing context, they should not be mixed). Do note that the timing
 * information on the packets sent to the muxer must be in the corresponding
 * AVStream's timebase. That timebase is set by the muxer (in the
 * avformat_write_header() step) and may be different from the timebase
 * requested by the caller.

1.3.4  av_write_trailer && avformat_free_context


文件结束:

  1. 一旦所有音视频数据被写入,调用者必须调用 av_write_trailer() 方法来flush所有被缓存的包,并且完成输出文件,然后去关闭IO上下问对象AVIOContext,最终调用 avformat_free_context() 释放封装上下文。
 *
 * Once all the data has been written, the caller must call av_write_trailer()
 * to flush any buffered packets and finalize the output file, then close the IO
 * context (if any) and finally free the muxing context with
 * avformat_free_context().

1.4 目录访问


1.4.1  avio_open_dir && AVIODirContext 


目录的打开:

  1. directory listing API提供了打开远端服务器目录,并枚举目录下文件的能力
  2. 枚举文件之前,需要使用 avio_open_dir() 方法打开一个目录,使用方法是传入一个URL,并可选性提供一个协议相关的选项参数,使用的方法见下方。该方法在调用成功后返回0或者正数,并且分配目录上下文对象AVIODirContext。
 * @defgroup lavf_io I/O Read/Write
 * @{
 * @section lavf_io_dirlist Directory listing
 * The directory listing API makes it possible to list files on remote servers.
 *
 * Some of possible use cases:
 * - an "open file" dialog to choose files from a remote location,
 * - a recursive media finder providing a player with an ability to play all
 * files from a given directory.
 *
 * @subsection lavf_io_dirlist_open Opening a directory
 * At first, a directory needs to be opened by calling avio_open_dir()
 * supplied with a URL and, optionally, ::AVDictionary containing
 * protocol-specific parameters. The function returns zero or positive
 * integer and allocates AVIODirContext on success.
 *
 * @code
 * AVIODirContext *ctx = NULL;
 * if (avio_open_dir(&ctx, "smb://example.com/some_dir", NULL) < 0) {
 *     fprintf(stderr, "Cannot open directory.\n");
 *     abort();
 * }
 * @endcode
 *
 * This code tries to open a sample directory using smb protocol without
 * any additional parameters.

1.4.1  avio_read_dir&& AVIODirEntry


文件枚举:

  1.  每个目录的条目(文件,子目录,或者是AVIODirEntryType枚举中的其他类型,发现还挺多的...)可以使用AVIODirEntry结构体来表示。
  2. 读取已打开的目录AVIODirContext的连续条目,可以通过反复调用avio_read_dir()来实现。每次调用成功将返回非负数。读取可以在返回的条目为空后停止,表面没有可以读取的条目了。
  3. 示例代码枚举与某个目录关联的AVIODirContext下所有条目。
 * @subsection lavf_io_dirlist_read Reading entries
 * Each directory's entry (i.e. file, another directory, anything else
 * within ::AVIODirEntryType) is represented by AVIODirEntry.
 * Reading consecutive entries from an opened AVIODirContext is done by
 * repeatedly calling avio_read_dir() on it. Each call returns zero or
 * positive integer if successful. Reading can be stopped right after the
 * NULL entry has been read -- it means there are no entries left to be
 * read. The following code reads all entries from a directory associated
 * with ctx and prints their names to standard output.
 * @code
 * AVIODirEntry *entry = NULL;
 * for (;;) {
 *     if (avio_read_dir(ctx, &entry) < 0) {
 *         fprintf(stderr, "Cannot list directory.\n");
 *         abort();
 *     }
 *     if (!entry)
 *         break;
 *     printf("%s\n", entry->name);
 *     avio_free_directory_entry(&entry);
 * }
 * @endcode
 * @}

/**
 * Directory entry types.
 */
enum AVIODirEntryType {
    AVIO_ENTRY_UNKNOWN,
    AVIO_ENTRY_BLOCK_DEVICE,
    AVIO_ENTRY_CHARACTER_DEVICE,
    AVIO_ENTRY_DIRECTORY,
    AVIO_ENTRY_NAMED_PIPE,
    AVIO_ENTRY_SYMBOLIC_LINK,
    AVIO_ENTRY_SOCKET,
    AVIO_ENTRY_FILE,
    AVIO_ENTRY_SERVER,
    AVIO_ENTRY_SHARE,
    AVIO_ENTRY_WORKGROUP,
};

2  总结


avformat.h头文件虽然给出了libavformat库中的Public API,但是该库中很多东西并没有在该头文件中进行详细的描述,但是头文件中有所提及: 详细的解封装器(Demuxers)信息,本地解封装器(Native Demuxers),外部的封装器(External Libaray Wrapper),I/O Protocols,AVFormatInternal等等。这些内容将会跟随看FFMPEG源码的深入在后续的文章中一一解析。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐