FFMPEG4.1源码解析之 libavformat库解读
目录0 前言1avformat.h1.1 重要的结构体 && API1.1.1 lavf && av_register_all &&avformat_network_init1.1.2 AVInputFormat && AVOutputFormat &&av_iformat_next &...
目录
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.2.2 avformat_open_input && avformat_alloc_context
1.2.3 avformat_open_input && options
1.2.4 av_read_frame() && AVPacket
1.3.3 av_write_frame() && av_interleaved_write_frame()
1.3.4 av_write_trailer && avformat_free_context
1.4.1 avio_open_dir && AVIODirContext
1.4.1 avio_read_dir&& AVIODirEntry
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
从以下描述中提炼出以下信息:
- avformat.h是libavformat库的Public API头文件;
- avformat.h包含I/O功能,Muxing/Demuxing功能;
- lavf是Libavformat库的简称,这个在很多地方都会出现,相当重要;
- lavf可以处理各种各样的容器封装;
- lavf主要目的就是解封装(分割媒体文件信息到流),以及相反的处理,进行封装(将媒体数据写入特定的容器封装中);
- lavf包含了I/O模块,“lavf_io”,用以支持各种获取数据的协议,比如本地文件file协议,tcp协议,http协议或者其他;
- 在使用lavf之前,需要调用av_register_all()来注册所有已经编译进来的封装器/解封装器,协议/解协议器,详见FFRMPEG4.1源码分析之 av_register_all()
- 除非你明确的知道你不会使用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
从以下关于输入文件格式,输出文件格式,协议的描述提炼以下几点:
- 支持的输入文件格式以AVInputFormat结构体描述;
- 支持的输出文件格式以AVOutputFormat结构体描述;
- 使用API方法 av_iformat_next()和 av_oformat_next()可以迭代所有的已注册的输入和输出文件格式;
- 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描述中可以得知:
- AVFormatContext是lavf库最重要的结构体,封装和解封装都使用到,其包含了文件如何读取,如何写入的所有必要信息;
- 和绝大多数的lavf库中的其他结构体一样,它的大小并不是公共应用程序二进制接口(public ABI)的一部分,因此AVFormatContext不能分配在栈上,也不能直接使用av_malloc来分配;
- 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结构体中最重要的几个成员的描述:
- AVFormatContext.iformat是输入文件格式,类型AVInputFormat,解封装的时候由用户指定格式或者自动检测文件格式;
- AVFormatContext.oformat是输出文件格式,类型AVOutputFormat,封装的时候由用户指定格式;
- AVFormatContext.streams是AVStreams数组,描述了文件中的音视频流。AVStreams 的引用方式一般就是通过数组下标来访问;
- 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机制:
- ffmpeg中提供了avoptions机制来配置封装/解封装相关的参数,这个机制核心概念就是“根据字符串操作结构体的属性值”;
- 一般的(文件格式无关的属性)lavf属性由AVFormatContext结构体提供,可以对已分配AVInputFormat对象或者是AVFormatContext的AVClass对象(通过avformat_get_class方法获取),使用av_opt_next()或者是av_opt_find()方法进行属性的查找检测;
- 私有的(文件格式相关的属性)lavf属性由AVFormatContext.priv_data提供,并且仅在AVInputFormat.priv_class或者是AVOutputFormat.priv_class相关结构体非空的情况下;
- 更多的选项(协议层,协议相关的选项),可能由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相关:
- ffmpeg中URL串由协议名 + ":" + 协议相关字符串组成。
- URLs中没有协议名 + ":"被认为是本地文件,虽然支持这种格式,但是被认为是过时的,因此最好使用 "file:",即file协议来访问本地文件
- 协议串不要从不被信任,并且没有经过检查的地方获取
- 注意:有些协议很强大,不仅允许访问本地文件,还允许访问远端文件。
* @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
解封装的过程简单介绍:
- 解封装器将读取媒体文件,并将其分割成数据块,即AVPackets对象。一个AVPacket对象包含属于同一个流的一帧或多帧被编码的数据。
- 在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的部分论述,非常重要的点:
- avformat_open_input()方法用于打开一个文件,其需要的最小信息就是文件的URL,如下示例中所示
- 示例中的 avformat_open_input() 方法会在内部调用 avformat_alloc_context() 方法去创建一个AVFormatContext 对象;会探测这个文件的文件格式,并打开这个指定的文件,读取文件头,将读取的信息填充到AVFormatContext的字段中去。注意注意注意!!!由于有的文件格式没有文件头,或者文件头中并没有存储足够的有效信息,那么推荐在使用avformat_open_input() 之后再调用 avformat_find_stream_info() 方法,该方法会读取并解码一些帧,去获取足够的信息。
- 虽然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以及选项传递:
- 一般情况下(用户未指明输入文件格式),只有在avformat_open_input()方法返回后才知道输入文件格式,因此,这之前是没法设置预创建的AVFormatContext的关于解封装的私有选项信息,之前提到过通用的信息直接存储在AVFormatContext的直接成员字段中,格式相关的信息存储在AVInputFormat和AVOutputFormat结构中(当然,这两个结构体也是AVFormatContext的直接成员字段,我的意思,大家应该懂的)。那么用户传递进来的选项设置如何生效呢?答案是将这些选项装进一个AVDictionary对象中,并以入参的形式传递给avformat_open_input();
- 示例中传递两个选项参数,分别是视频分辨率video_size和像素格式pixel_format。大家想想这两个参数在是什么时候会起到作用?比如我们解码数据是原始视频数据,这些数据并没有包含描述数据本身的metadata信息,因此就需要外部额外的给出信息才能正常解码,这些信息就通过该参数传递。注意:如果对于此示例来说,需要解码的不是原始视频数据,而是其他格式数据,那么传入的选项信息将不会被解封装器AVInputFormat识别,也就不会生效。被识别的选项将被消费掉,无法识别的选项信息仍通过该参数传递出来(由于该参数传递的是引用),因此用户还可通过该参数获知哪些选项是没有生效的,可以做相应的处理,比如后面这个示例所示。
- 当结束文件读取的时候,需要调用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:
- 从已打开的文件中读取数据(从打开的AVFormatContext中),是通过对AVFormatContext反复调用av_read_frame()来实现。每次av_read_frame()的调用,如果成功,将会返回一个AVPacket对象,内含某一个流AVStream的编码过的数据,可以通过AVPacket.stream_index来识别属于哪个流;
- 如果希望进行解码操作,可以将读取的AVPacket可以直接传递给libavcode库的解码函数avcodec_send_packet()或者avcodec_decode_subtitle2()
- 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为单位的度量上来。
- 注意注意注意!!! 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
封装的基本过程:
- 封装是将编码后的数据(以AVPacket结构表示)写入文件,或者以输出他字节流形式写入指定的封装容器;
- 封装涉及的主要的API为avformat_write_header()用来写入文件头,av_write_frame() / av_interleaved_write_frame()用来写入packets(音视频数据),av_write_trailer()用来结束文件写入;
- 在封装过程之前,首先需要使用avformat_alloc_context()来创建一个进行封装的上下文对象AVFormatContext,通过对该上下文对象的成员字段设置来初始化封装器。这些必要的字段如 4 5 6 7 8 所描述;
- AVFormatContext.oformat:AVOutputFormat对象,必须设置,这个表征的是封装器;
- AVFormatContext.pb:AVIOContext对象,除非文件格式是AVFMT_NOFILE类型,否则pb字段必须设置为一个“已打开”的IO上下文对象,这对象要么是由avio_open2()返回,要么是用户来设置一个;
- 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将被设置为用户期望该流所使用的时间基(封装器实际所使用的时间基可能会与流中的时间基可能不同);
- AVFormatContext.streams.codecpar: 建议只初始化AVCodecParameters必要的字段,而不推荐使用avcodec_parameters_copy()从解封装的AVCodecParameters对象中来生成一份拷贝,因为无法保证同一份AVCodecParameters中的所有字段对于封装和解封装的都是合法的;
- 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()
写入文件头信息:
- 当封装上下文对象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()
音视频数据写入:
- 写入文件头后,音视频数据通过重复调用 av_write_frame() 或者 av_interleaved_write_frame() 将数据写入封装,这两个函数的区别将在其他文章中进行分析,但注意一点,二者不能同时混用。
- 注意:写入包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
文件结束:
- 一旦所有音视频数据被写入,调用者必须调用 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
目录的打开:
- directory listing API提供了打开远端服务器目录,并枚举目录下文件的能力
- 枚举文件之前,需要使用 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
文件枚举:
- 每个目录的条目(文件,子目录,或者是AVIODirEntryType枚举中的其他类型,发现还挺多的...)可以使用AVIODirEntry结构体来表示。
- 读取已打开的目录AVIODirContext的连续条目,可以通过反复调用avio_read_dir()来实现。每次调用成功将返回非负数。读取可以在返回的条目为空后停止,表面没有可以读取的条目了。
- 示例代码枚举与某个目录关联的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源码的深入在后续的文章中一一解析。
更多推荐
所有评论(0)