Demuxers读取一个媒体文件并将其分割成数据块(数据包)。
分组包含属于单个基本流的一个或多个编码帧。 在lavf API中,此过程由用于打开文件的avformat_open_input()函数表示,av_read_frame()用于读取单个数据包,最后是avformat_close_input(),该文件执行清理。
一、数据结构
struct | AVInputFormat{ const char * name; // 用逗号分隔的格式的短名单。 const char * long_name; //格式的描述性名称,意思是更加人性化的名字。 int flags; //可以使用标志:AVFMT_NOFILE,AVFMT_NEEDNUMBER,AVFMT_SHOW_IDS,AVFMT_GENERIC_INDEX,AVFMT_TS_DISCONT,AVFMT_NOBINSEARCH,AVFMT_NOGENSEARCH,AVFMT_NO_BYTE_SEEK,AVFMT_SEEK_TO_PTS。 const char * extensions; // 如果定义了扩展名,则不会进行任何探针。 const struct AVCodecTag *const * codec_tag; const AVClass * priv_class; // AVClass为私有上下文。 const char * mime_type; // 逗号分隔的MIME类型列表。 struct AVInputFormat * next; int raw_codec_id; // 原始解码器在这里存储他们的编解码器ID。 int priv_data_size; // 私有数据的大小可以在包装器中分配。 int(* read_probe )(AVProbeData *); // 告诉某个给定的文件是否有机会被解析为这种格式。 int(* read_header )(struct AVFormatContext *); // 读取格式头并初始化AVFormatContext结构。 int(* read_packet )(struct AVFormatContext *, AVPacket *pkt); // 读取一个数据包并将其放在“pkt”中。 int(* read_close )(struct AVFormatContext *); //关闭流。 int(* read_seek )(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags); // 寻求相对于流组件stream_index中的帧的给定时间戳。 int64_t(* read_timestamp )(struct AVFormatContext *s, int stream_index, int64_t *pos, int64_t pos_limit); // 以stream [stream_index] .time_base为单位获取下一个时间戳。 int(* read_play )(struct AVFormatContext *); // 开始/恢复播放 - 只有使用基于网络的格式(RTSP)才有意义。 int(* read_pause )(struct AVFormatContext *); // 暂停播放 - 只有使用基于网络的格式(RTSP)才有意义。 int(* read_seek2 )(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); // 寻求时间戳ts。 int(* get_device_list )(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); // 返回带有属性的设备列表。 int(* create_device_capabilities )(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); // 初始化设备功能子模块。 int(* free_device_capabilities )(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps); // 免费设备功能子模块。 } |
二、函数
AVInputFormat * | av_find_input_format (const char *short_name) |
AVInputFormat * | av_probe_input_format (AVProbeData *pd, int is_opened) |
参数:pd:要探测的数据
is_opened:文件是否已经打开; 确定是否探测具有或不具有AVFMT_NOFILE的分离器。
AVInputFormat * | av_probe_input_format2 (AVProbeData *pd, int is_opened, int *score_max) |
参数:pd:要探测的数据
is_opened:文件是否已经打开; 确定是否探测具有或不具有AVFMT_NOFILE的分离器。
score_max:探测分数越大,需要接受检测,该变量随后被设置为实际检测分数。 如果分数<= AVPROBE_SCORE_MAX / 4,建议用较大的探针缓冲区重试。
AVInputFormat * | av_probe_input_format3 (AVProbeData *pd, int is_opened, int *score_ret) |
参数:is_opened:文件是否已经打开; 确定是否探测具有或不具有AVFMT_NOFILE的分离器。
score_ret:得分最好的检测。
int | av_probe_input_buffer2 (AVIOContext *pb, AVInputFormat **fmt, const char *url, void *logctx, unsigned int offset, unsigned int max_probe_size) |
每次探头返回的分数太低时,探头缓冲区大小会增加,并进行另一次尝试。 当达到最大探针尺寸时,返回具有最高分数的输入格式。
参数:pd:用来探测的比特流
fmt:输入格式放在这里
url:流url
logctx:日志上下文
offset:在字节流内的偏移量进行探测
max_probe_size:最大探针缓冲区大小(默认为零)
返回:在成功的情况下的分数,对应于最大分数的负值为AVPROBE_SCORE_MAX AVERROR代码
int | av_probe_input_buffer (AVIOContext *pb, AVInputFormat **fmt, const char *url, void *logctx, unsigned int offset, unsigned int max_probe_size) |
int | avformat_open_input (AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options) |
编解码器未打开。 流必须用avformat_close_input()关闭。
参数:ps:指向用户提供的AVFormatContext(由avformat_alloc_context分配)。 可能是指向NULL的指针,在这种情况下,AVFormatContext由该函数分配并写入ps。 请注意,用户提供的AVFormatContext将在失败时被释放。
url:要打开的流的URL。
fmt:如果非NULL,则此参数强制使用特定的输入格式。 否则格式将自动检测。
options:一个充满AVFormatContext和demuxer-private选项的字典。 返回时,此参数将被销毁,并替换为包含未找到的选项的dict。 可能为NULL。
返回:返回0成功,失败返回负的AVERROR
注意:如果要使用自定义IO,则预分配格式上下文并设置其pb字段。
attribute_deprecated int | av_demuxer_open (AVFormatContext *ic) |
int | avformat_find_stream_info (AVFormatContext *ic, AVDictionary **options) |
这对于没有诸如MPEG的标题的文件格式很有用。 该功能还可以在MPEG-2重复帧模式的情况下计算真实的帧率。 此功能不会更改逻辑文件位置; 检查的分组可以被缓冲以供稍后处理。
参数:ic:媒体文件句柄
options:如果非NULL,则ic.nb_streams指向字典的长数组指针,其中第i个成员包含对应于第i个流的编解码器选项。 返回时,每个字典将填充未找到的选项。
返回:> = 0如果OK,AVERROR_xxx出错
注意:这个功能不能保证打开所有的编解码器,所以返回时非空的选项是完全正常的行为。
提示:让用户以某种方式决定需要什么信息,以免我们浪费时间获取用户不需要的东西。
AVProgram * | av_find_program_from_stream (AVFormatContext *ic, AVProgram *last, int s) |
参数:ic:媒体文件句柄
last:最后找到的程序,搜索将在此程序后启动,或者从开始,如果为NULL
s:流索引
返回:属于s的下一个程序,如果没有程序被找到,则为NULL,或者最后一个程序不在ic程序中。
void | av_program_add_stream_index (AVFormatContext *ac, int progid, unsigned int idx) |
int | av_find_best_stream (AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags) |
最好的流是根据各种启发式确定的,最有可能是用户期望的。 如果解码器参数不为NULL,则av_find_best_stream将找到流的编解码器的默认解码器; 不能找到解码器的流被忽略。
参数:ic:媒体文件句柄
type:流类型:视频,音频,字幕等
wanted_stream_nb:用户请求的流号码,或-1表示自动选择
related_stream:尝试找到与此相关的流(例如,在同一程序中),如果没有,则返回-1
decoder_ret:如果非NULL,则返回所选流的解码器
flags:标志; 目前没有定义
返回:在成功的情况下为非负流数,AVERROR_STREAM_NOT_FOUND如果没有可以找到请求类型的流,则AVERROR_DECODER_NOT_FOUND如果发现流,但没有解码器
注意:如果av_find_best_stream成功返回并且decode_ret不为NULL,则* decode_ret保证被设置为有效的AVCodec。
int | av_read_frame (AVFormatContext *s, AVPacket *pkt) |
此函数返回存储在文件中的内容,并且不会验证解码器有什么有效帧。 它会将存储在文件中的内容分成帧,并为每个调用返回一个。 它不会在有效帧之间省略无效数据,以便为解码器提供可能进行解码的最大信息。
如果pkt-> buf为NULL,则数据包有效直到下一个av_read_frame()或直到avformat_close_input()。 否则数据包无限期有效。 在这两种情况下,当不再需要数据包时,必须使用av_packet_unref来释放数据包。 对于视频,数据包只包含一帧。 对于音频,如果每个帧具有已知的固定大小(例如PCM或ADPCM数据),则它包含整数帧。 如果音频帧具有可变大小(例如,MPEG音频),则它包含一帧。
pkt-> pts,pkt-> dts和pkt->持续时间始终设置为在AVStream.time_base单位中修正值(如果格式无法提供)。 如果视频格式有B帧,pkt-> pts可以是AV_NOPTS_VALUE,所以如果不解压缩有效载荷,最好依靠pkt-> dts。
返回:0表示成功,<0表示错误或者文件的尾部
int | av_seek_frame (AVFormatContext *s, int stream_index, int64_t timestamp, int flags) |
参数:s:媒体文件句柄
stream_index:如果stream_index为(-1),则选择默认流,并将时间戳从AV_TIME_BASE单位自动转换为流特定的time_base。
timestamp:AVStream.time_base单位中的时间戳记,或者如果没有指定流,则以AV_TIME_BASE为单位。
flags:选择方向和寻找模式的标志
返回:>=0表示成功
int | avformat_seek_file (AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags) |
要完成这一工作,所以可以成功地显示所有活动流的点将最接近ts并在最小/最大值范围内。 活动流都是具有AVStream.discard <AVDISCARD_ALL的流。
如果标志包含AVSEEK_FLAG_BYTE,则所有时间戳都是以字节为单位,并且是文件位置(所有分离器可能不支持)。 如果标志包含AVSEEK_FLAG_FRAME,则所有时间戳都与stream_index的流中的帧相同(所有分离器可能不支持)。 否则所有时间戳都是以stream_index选择的流或者stream_index为-1,以AV_TIME_BASE为单位。 如果标志包含AVSEEK_FLAG_ANY,则非关键帧将被视为关键帧(所有分离器可能不支持此关键帧)。 如果标志包含AVSEEK_FLAG_BACKWARD,则会被忽略。
参数:s:媒体文件句柄
stream_index:用作时基参考的流的索引
min_ts:最小可接受的时间戳
ts:目标时间戳
max_ts:最大可接受的时间戳
flags:标志
返回:> = 0成功,否则为错误代码
注意:这是新的API API的一部分,它仍在建设中。 因此,不要使用它。 它可能随时改变,不要指望ABI兼容性!
int | avformat_flush (AVFormatContext *s) |
当处理字节流中的不连续性时,这可能很有用。 通常只能使用可以重新同步的格式。 这包括无标题格式,如MPEG-TS / TS,但也应该使用NUT,Ogg和有限的方式AVI。
调用此功能时,流的集合,检测到的持续时间,流参数和编解码器不会改变。 如果你想要一个完整的重置,最好打开一个新的AVFormatContext。
这不刷新AVIOContext(s-> pb)。 如有必要,请在调用此函数之前调用avio_flush(s-> pb)。
参数:s:媒体文件句柄
返回:> = 0成功,否则为错误代码
int | av_read_play (AVFormatContext *s) |
int | av_read_pause (AVFormatContext *s) |
void | avformat_close_input (AVFormatContext **s) |
释放它及其所有内容,并将*设置为NULL。
三、打开媒体文件
打开文件所需的最少信息是其URL,它传递给avformat_open_input(),如以下代码所示:
const char *url = "file:in.mp3";
AVFormatContext *s = NULL;
int ret = avformat_open_input(&s, url, NULL, NULL);
if (ret < 0)
abort();
上述代码尝试分配AVFormatContext,打开指定的文件(自动检测格式)并读取头文件,将存储在其中的信息导出到s中。 某些格式没有标题或不存储足够的信息,因此建议您调用avformat_find_stream_info()函数,该函数尝试读取和解码几个帧以找到缺少的信息。
在某些情况下,您可能希望使用avformat_alloc_context()自己预先分配AVFormatContext,并在将其转换为avformat_open_input()之前对其进行一些调整。 一种情况是当您想要使用自定义函数来读取输入数据而不是lavf内部I / O层。 要做到这一点,使用avio_alloc_context()创建自己的AVIOContext,将读取回调传递给它。 然后将AVFormatContext的pb字段设置为新创建的AVIOContext。
由于打开的文件的格式通常在avformat_open_input()返回之后才知道,所以不可能在预先分配的上下文中设置demuxer的私有选项。 相反,应该将选项传递给包含在AVDictionary中的avformat_open_input():
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);
该代码将私有选项'video_size'和'pixel_format'传递给分解器。 它们将是必需的。 原始视频分解器,因为它不知道如何解释原始视频数据否则。 如果格式与原始视频不同,则这些选项将不会被解码器识别,因此不会被应用。 然后在选项字典中返回这些无法识别的选项(已识别的选项已被使用)。 呼叫程序可以根据需要处理这些未被识别的选项,例如
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();
}
读完文件后,您必须使用avformat_close_input()关闭文件。 它将释放与文件相关的所有内容。
四、从打开的文件读取
从打开的AVFormatContext读取数据是通过重复调用av_read_frame()来完成的。 每个呼叫如果成功,将返回包含由AVPacket.stream_index标识的一个AVStream的编码数据的AVPacket。 如果呼叫者希望解码数据,则该数据包可以直接传递到libavcodec解码函数avcodec_send_packet()或avcodec_decode_subtitle2()。
如果已知AVPacket.pts,AVPacket.dts和AVPacket.duration定时信息将被设置。 如果流不提供它们,也可能未设置它们(即,对于pts / dts为AV_NOPTS_VALUE,持续时间为0)。 时间信息将以AVStream.time_base为单位,即必须乘以时基将其转换为秒。
如果在返回的数据包上设置了AVPacket.buf,则动态分配数据包,用户可以无限期地保留该数据包。 否则,如果AVPacket.buf为NULL,则分组数据由解复用器内部某处的静态存储器支持,并且数据包仅在下一个av_read_frame()调用或关闭文件之前有效。 如果调用者需要更长的生命周期,av_dup_packet()将会生成一个av_malloc()的副本。 在这两种情况下,当不再需要数据包时,必须使用av_packet_unref()来释放数据包。