说明 :将 avfromatContext 的变量依次打印分析,根据ffmpeg 给的说明,猜测,结合网上的文章字节写测试代码分析二。
37 AVInputFormat *iformat;
/**
* The input container format.
*
* Demuxing only, set by avformat_open_input().
*/
ff_const59 struct AVInputFormat *iformat;
38 AVFormatInternal *internal; 对开发者隐藏,但是对于阅读ffmpeg源码有用。
/**
* An opaque field for libavformat internal usage.
* Must not be accessed in any way by callers.
*/
AVFormatInternal *internal;
*libavformat内部使用的不透明字段。
*使用不得以任何方式访问。
AVFormatContext 中的 AVFormatInternal *internal
字段是 FFmpeg 内部用于存储与解封装/封装过程相关的私有状态数据的结构体7。以下是其核心特性的总结:
1. 设计目的
- 内部数据隔离:
internal
封装了AVFormatContext
运行时的临时状态和中间数据,避免用户直接操作导致的结构不稳定或版本兼容性问题7。 - 模块化扩展:通过独立结构体管理内部状态,方便 FFmpeg 在不同版本中扩展或修改内部实现逻辑,而无需修改
AVFormatContext
的公开接口7。
2. 关键特性
-
不透明性:
AVFormatInternal
的具体定义未在公共头文件(如avformat.h
)中公开,开发者无法直接访问其内部字段7。- 所有操作需通过 FFmpeg 提供的 API 完成(如
avformat_open_input()
、av_read_frame()
等)78。
-
生命周期管理:
internal
的内存分配与释放由 FFmpeg 内部自动完成,用户无需手动干预(例如在调用avformat_close_input()
时会自动清理)7。- 初始化时,
internal
通常通过avformat_alloc_context()
函数间接创建7。
3. 关联功能
- 流探测缓冲:缓存输入数据的部分内容,用于格式探测(如
avformat_find_stream_info()
中的流参数分析)8。 - 中间状态存储:
- 封装/解封装过程中的临时数据包队列3。
- 时间戳校正、流交错(interleaving)等算法所需的上下文信息38。
4. 开发者注意事项
- 禁止直接访问:由于 FFmpeg 不保证
AVFormatInternal
的跨版本二进制兼容性,直接操作其字段可能导致程序崩溃或未定义行为7。 - 调试限制:若需调试内部状态,通常需结合 FFmpeg 源码中的日志输出或使用调试器跟踪相关函数调用7。
与类似字段的对比
字段 | 作用域 | 可见性 | 管理方式 |
---|---|---|---|
priv_data | 格式私有数据 | 半公开(需API) | 由 AVInputFormat/AVOutputFormat 管理14 |
internal | 全局运行时 | 完全内部 | FFmpeg 内部自动管理7 |
此设计模式体现了 FFmpeg 对“接口稳定”和“实现灵活”的平衡,确保用户仅通过规范 API 即可安全使用核心功能78。
39.AVIOInterruptCB interrupt_callback
/**
* Custom interrupt callbacks for the I/O layer.
*
* demuxing: set by the user before avformat_open_input().
* muxing: set by the user before avformat_write_header()
* (mainly useful for AVFMT_NOFILE formats). The callback
* should also be passed to avio_open2() if it's used to
* open the file.
*/
AVIOInterruptCB interrupt_callback;
/**
* Callback for checking whether to abort blocking functions.
* AVERROR_EXIT is returned in this case by the interrupted
* function. During blocking operations, callback is called with
* opaque as parameter. If the callback returns 1, the
* blocking operation will be aborted.
*
* No members can be added to this struct without a major bump, if
* new elements have been added after this struct in AVFormatContext
* or AVIOContext.
*/
typedef struct AVIOInterruptCB {
int (*callback)(void*);
void *opaque;
} AVIOInterruptCB;
AVFormatContext 中的 interrupt_callback
字段用于实现自定义 I/O 中断控制机制,主要解决网络流处理时的阻塞问题。以下是其核心功能与使用方法的总结:
1. 功能与作用
- 阻塞中断:在
avformat_open_input()
或av_read_frame()
等操作中,若因网络延迟、设备断开或无数据导致长时间阻塞,可通过回调函数强制终止等待12。 - 超时控制:允许开发者设置最大等待时间,避免程序无响应(如 RTSP 流超时断开)48。
- 跨协议兼容:适用于 RTSP、RTMP 等网络协议,弥补某些协议(如 RTMP)不支持超时参数的缺陷16。
2. 结构体定义
interrupt_callback
是 AVFormatContext
的成员,类型为 AVIOInterruptCB
,包含两个关键字段:
-
callback
:函数指针,指向用户定义的中断检测函数。 -
opaque
:用户自定义的上下文指针(如对象实例、计时器等),传递给回调函数。
typedef struct AVIOInterruptCB {
int (*callback)(void*); // 中断检测函数
void *opaque; // 用户上下文数据
} AVIOInterruptCB;
3. 回调函数实现
回调函数需返回 1
(中断) 或 0
(继续等待)。典型实现逻辑:
- 通过
opaque
获取上下文数据(如超时起始时间)。 - 计算当前时间与起始时间的差值。
- 若超过预设阈值(如 5 秒),返回
1
终止操作。
// 示例:超时检测回调
int interrupt_cb(void *ctx) {
int64_t start_time = *(int64_t*)ctx; // 通过 opaque 传递起始时间
int64_t current_time = av_gettime(); // 获取当前时间(微秒)
return (current_time - start_time > 5000000) ? 1 : 0; // 超过5秒则中断
}
4. 设置步骤
- 分配上下文:创建
AVFormatContext
实例。 - 绑定回调:设置
interrupt_callback
的callback
和opaque
。 - 启动计时:在关键操作前记录起始时间(如
av_gettime()
)。 - 打开输入流:调用
avformat_open_input()
,阻塞操作将触发回调检测。
AVFormatContext *fmt_ctx = avformat_alloc_context();
int64_t timeout_start = av_gettime(); // 记录起始时间
// 设置回调函数及上下文
fmt_ctx->interrupt_callback.callback = interrupt_cb;
fmt_ctx->interrupt_callback.opaque = &timeout_start;
// 打开输入流(如 RTSP)
if (avformat_open_input(&fmt_ctx, "rtsp://example.com", NULL, NULL) < 0) {
// 错误处理
}
5. 注意事项
- 协议差异:对于 RTSP,可同时设置
stimeout
参数(单位:微秒)增强超时控制68:cCopy Code
AVDictionary *options = NULL; av_dict_set(&options, "stimeout", "2000000", 0); // 2秒超时 avformat_open_input(&fmt_ctx, url, NULL, &options);
- 线程安全:回调函数需确保线程安全,避免竞态条件8。
- 资源释放:无需手动释放
interrupt_callback
,其生命周期与AVFormatContext
绑定5。
6. 典型问题与调试
- 回调未触发:检查是否在
avformat_open_input()
前正确设置回调8。 - 误中断:调整超时阈值,或在回调中增加状态判断(如网络重连尝试次数)6。
40 回调函数 io_open
谨慎
/**
* A callback for opening new IO streams.
*
* Whenever a muxer or a demuxer needs to open an IO stream (typically from
* avformat_open_input() for demuxers, but for certain formats can happen at
* other times as well), it will call this callback to obtain an IO context.
*
* @param s the format context
* @param pb on success, the newly opened IO context should be returned here
* @param url the url to open
* @param flags a combination of AVIO_FLAG_*
* @param options a dictionary of additional options, with the same
* semantics as in avio_open2()
* @return 0 on success, a negative AVERROR code on failure
*
* @note Certain muxers and demuxers do nesting, i.e. they open one or more
* additional internal format contexts. Thus the AVFormatContext pointer
* passed to this callback may be different from the one facing the caller.
* It will, however, have the same 'opaque' field.
*/
int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
int flags, AVDictionary **options);
目前看的信息是:avformatContext 的 io_open 回调函数 在默认情况下叫 io_open_default,在解复用的 avformat_open_input 方法中一定会调用。
io_open_default
是 FFmpeg 中用于初始化输入/输出上下文 (AVIOContext
) 的关键函数,主要在解封装流程中处理协议检测与连接。以下是其核心机制与作用解析:
一、功能定位
-
协议初始化入口
在avformat_open_input()
调用流程中,io_open_default
作为默认的 I/O 上下文初始化函数,负责根据输入 URL 的协议类型(如 HTTP、HLS、文件)创建对应的AVIOContext
,并绑定底层协议实现1。 -
协议白名单校验
通过调用ffio_open_whitelist
,该函数会校验协议是否在允许范围内,防止加载未授权的协议实现(如自定义或非标准协议),确保安全性1。 -
多层封装调用链
其执行链路为:
avformat_open_input
→init_input
→io_open_default
→ffio_open_whitelist
→ffurl_open_whitelist
→ffurl_connect
→ 具体协议实现(如http_open
)1。
最终通过系统调用(如socket
)建立网络连接或打开本地文件12。
二、实现机制
-
协议适配层
io_open_default
通过协议前缀(如http://
、file://
)匹配对应的URLProtocol
实现(如ff_http_protocol
),并初始化协议上下文 (URLContext
),最终关联到AVIOContext
的opaque
字段15。 -
缓冲与回调设置
自动配置读写缓冲区大小,并设置默认的read_packet
、write_packet
等回调函数,确保数据通过协议层高效传输至解封装模块17。 -
错误处理
若协议检测失败(如 URL 无效或协议未注册),函数返回错误码并终止后续流程,触发avformat_open_input
的异常处理逻辑1。
三、应用场景
- 标准协议处理:自动处理 HTTP、RTMP、HLS 等协议连接的初始化,无需手动创建
AVIOContext
15。 - 安全限制场景:通过白名单机制限制可访问的协议类型,适用于需严格管控输入源的场景(如嵌入式设备)1。
四、扩展性
- 自定义协议支持:若需支持私有协议,需绕过
io_open_default
,通过avio_alloc_context
手动创建AVIOContext
并注册自定义协议实现37。
41 回调函数io_close
/**
* A callback for closing the streams opened with AVFormatContext.io_open().
*/
void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
42 int io_repositioned;
/**
* IO repositioned flag.
* This is set by avformat when the underlaying IO context read pointer
* is repositioned, for example when doing byte based seeking.
* Demuxers can use the flag to detect such changes.
*/
int io_repositioned;
io_repositioned
是 FFmpeg 中 AVFormatContext
结构体的一个标志字段,用于指示底层 I/O 上下文的读指针是否发生了位置重置。其核心作用与应用场景如下:
一、功能定义
-
标志位作用
io_repositioned
由 FFmpeg 的解封装模块(avformat
)自动设置,主要用于标识底层 I/O 上下文(如AVIOContext
)的读指针是否被显式重定位。例如,在基于字节的随机访问(byte-based seeking)时,该标志会被触发。 -
触发条件
当调用av_seek_frame()
或类似函数导致底层 I/O 缓冲区的读指针位置发生变化时,io_repositioned
会被设置为非零值,表示发生了位置重置。
二、应用场景
-
解封装逻辑适配
解复用器(Demuxer)可通过检查io_repositioned
标志判断是否需要重新同步内部状态(如重新解析流头信息或调整时间戳计算逻辑),以应对底层 I/O 位置突变带来的数据不一致问题。 -
性能优化
在处理网络流或大文件时,通过检测io_repositioned
标志,可避免无效的缓存数据读取,确保后续操作基于最新的 I/O 位置执行1。
三、实现关联
- 与
AVIOContext
的交互
io_repositioned
直接关联AVIOContext
的seek
操作,若用户代码通过avio_seek()
手动调整读指针位置,也会触发该标志的更新
打印值是0
avformatContext->io_repositioned;
cout << "avformatContext->io_repositioned = " << avformatContext->io_repositioned << endl;
43 const uint8_t *key; int keylen;作用未知
const uint8_t *key;
int keylen;
44 int64_t max_analyze_duration;
/**
* Maximum duration (in AV_TIME_BASE units) of the data read
* from input in avformat_find_stream_info().
* Demuxing only, set by the caller before avformat_find_stream_info().
* Can be set to 0 to let avformat choose using a heuristic.
*/
int64_t max_analyze_duration;
参见
36 int64_t max_analyze_duration; 解复用时使用,在avformat_find_stream_info 方法中使用
avfromatContext 中可以优化的参数。format_probesize,fps_probe_size,probesize,max_analyze_duration-优快云博客
avformatContext->max_analyze_duration = 0
45 int max_chunk_duration;
/**
* Max chunk time in microseconds.
* Note, not all formats support this and unpredictable things may happen if it is used when not supported.
* - encoding: Set by user
* - decoding: unused
*/
int max_chunk_duration;
用于设置流媒体传输中的最大块持续时间。在FFmpeg的RTMP协议中,这个参数定义了每个RTMP消息的最大持续时间。如果超过这个时间,FFmpeg会自动将数据分成多个块进行传输,以确保流媒体的稳定性和效率。
max_chunk_duration的作用和重要性
- 稳定性:通过限制每个数据块的大小,max_chunk_duration有助于确保流媒体的稳定性。如果数据块过大,可能会导致网络延迟或丢包,影响观看体验。
- 效率:合理设置max_chunk_duration可以提高传输效率,减少缓冲时间,提升用户体验。
- 兼容性:不同的设备和网络环境对数据块的大小有不同的要求,合理设置max_chunk_duration可以增强兼容性。
如何设置max_chunk_duration
在FFmpeg的命令行中,可以通过-max_chunk_duration
选项来设置
打印默认值
avformatContext->max_chunk_duration = 0
46 int max_chunk_size;
/**
* Max chunk size in bytes
* Note, not all formats support this and unpredictable things may happen if it is used when not supported.
* - encoding: Set by user
* - decoding: unused
*/
int max_chunk_size;
avformatContext->max_chunk_size = 0
avformat_context
结构体中的 max_chunk_size
字段在 FFmpeg 库中用于控制读取媒体文件时的最大块大小。这个字段主要用于某些特定的格式处理,尤其是在处理非常大的数据块时,例如在处理非常大的音频或视频帧时。
字段的作用
max_chunk_size
字段通常在处理某些格式的流时非常有用,特别是在需要分割大型文件或流为较小块进行处理时。例如,在某些情况下,直接读取整个大型文件到一个内存块可能会导致内存不足的问题。通过设置 max_chunk_size
,你可以指定每次读取的最大字节数,这有助于更有效地管理内存使用。
如何使用
在 FFmpeg 的 C API 中,你可以在打开媒体文件后设置 max_chunk_size
。例如:
AVFormatContext* formatCtx = NULL;
avformat_open_input(&formatCtx, "input.file", NULL, NULL);
// 设置最大块大小
if (formatCtx) {
formatCtx->max_chunk_size = 1024 * 1024; // 例如,设置为1MB
}
注意事项
-
兼容性:不是所有的格式和流都支持
max_chunk_size
。在某些情况下,即使设置了此值,FFmpeg 也可能忽略它。因此,最好检查文档和源代码来确认你的格式支持此功能。 -
性能影响:虽然
max_chunk_size
可以帮助管理内存使用,但设置过小的值可能会导致性能下降,因为频繁的磁盘I/O操作会增加。 -
默认值:如果没有特别设置,FFmpeg 可能使用其默认的块大小处理策略。
结论
max_chunk_size
是一个有用的工具,特别是在处理大型媒体文件时,可以帮助优化内存使用和改善应用程序的稳定性和性能。然而,正确使用它需要你对你的媒体文件和FFmpeg的内部机制有一定的了解。在使用之前,最好查阅最新的FFmpeg文档和源代码以了解更多细节和最佳实践。
47 int max_delay;
max_delay字段的作用
max_delay
字段主要用于控制媒体流在播放时的最大延迟。在一些情况下,比如直播流或需要低延迟的场景,控制延迟是非常重要的。例如,在视频会议或实时视频流中,延迟过高会导致用户体验不佳。
如何设置max_delay
在FFmpeg中,你可以通过编程方式设置AVFormatContext
的max_delay
字段来控制延迟。以下是一个基本的示例代码,展示了如何设置这个字段:
#include <libavformat/avformat.h>
int main() {
AVFormatContext* formatCtx = NULL;
if (avformat_open_input(&formatCtx, "input.mp4", NULL, NULL) != 0) {
fprintf(stderr, "Could not open input file\n");
return -1;
}
if (avformat_find_stream_info(formatCtx, NULL) < 0) {
fprintf(stderr, "Could not find stream information\n");
return -1;
}
// 设置最大延迟,单位为微秒(μs)
formatCtx->max_delay = (int64_t)(1000000); // 例如,设置为1秒的延迟
// 继续处理...
avformat_close_input(&formatCtx);
return 0;
}
注意事项
-
单位:
max_delay
的单位是微秒(μs)。因此,如果你想设置延迟为1秒,应该将值设置为1000000(因为1秒=1000毫秒=1000000微秒)。 -
影响:设置
max_delay
可能会影响流的播放性能和延迟特性。需要根据具体应用场景进行合理设置。 -
兼容性:不是所有的媒体格式或编解码器都支持动态调整延迟。某些实现可能不完全遵循此设置。
-
4. 与其他参数的关系
-
AVCodecContext
的max_b_frames
若编码层存在 B 帧(双向预测帧),需确保max_delay
大于 B 帧的解码依赖时间,否则可能导致解码错误4。 -
网络协议缓冲区
当通过AVIOContext
处理网络流时,max_delay
需与协议层的缓冲区大小(如buffer_size
)协同配置,避免网络抖动影响同步37。
结论
通过合理设置max_delay
,你可以更好地控制媒体流的播放延迟,这对于需要低延迟的应用场景(如实时视频通信)尤其重要。确保在实际应用中根据具体需求调整此参数。
打印结果为-1
avformatContext->max_delay;
cout << "avformatContext->max_delay = " << avformatContext->max_delay << endl;
48 unsigned int max_index_size;
/**
* Maximum amount of memory in bytes to use for the index of each stream.
* If the index exceeds this size, entries will be discarded as
* needed to maintain a smaller size. This can lead to slower or less
* accurate seeking (depends on demuxer).
* Demuxers for which a full in-memory index is mandatory will ignore
* this.
* - muxing: unused
* - demuxing: set by user
*/
unsigned int max_index_size;
max_index_size
字段是用来指示该容器中索引条目可能达到的最大数量。
解释
max_index_size
字段通常用在需要快速访问媒体文件中的多个片段或关键帧的场景。例如,在视频文件中,你可能想要快速跳转到文件的某个特定时间点。在这种情况下,索引可以帮助你快速定位到包含目标时间点的数据块。
使用场景
-
视频播放:在播放视频时,播放器可能需要根据用户操作(如快进、快退)快速定位到视频的特定部分。
-
媒体编辑:在编辑视频或音频文件时,快速访问不同部分的内容。
如何设置和使用
在FFmpeg的API中,你通常不需要手动设置 max_index_size
,因为这个值会根据你打开的媒体文件的具体格式和内容自动确定。当你使用 avformat_open_input()
函数打开一个媒体文件时,FFmpeg会自动填充 AVFormatContext
结构体,包括 max_index_size
。
历史版本兼容性
某些旧版本FFmpeg可能曾定义过类似字段,但在当前主流版本(如6.x)中被移除或更名。建议通过最新源码或官方文档确认
avformatContext->max_index_size = 1048576
49 int64_t max_interleave_delta;
/**
* Maximum buffering duration for interleaving.
*
* To ensure all the streams are interleaved correctly,
* av_interleaved_write_frame() will wait until it has at least one packet
* for each stream before actually writing any packets to the output file.
* When some streams are "sparse" (i.e. there are large gaps between
* successive packets), this can result in excessive buffering.
*
* This field specifies the maximum difference between the timestamps of the
* first and the last packet in the muxing queue, above which libavformat
* will output a packet regardless of whether it has queued a packet for all
* the streams.
*
* Muxing only, set by the caller before avformat_write_header().
*/
int64_t max_interleave_delta;
max_interleave_delta
字段是用于控制输出文件中的数据交织(interleaving)的一个参数。
什么是交织(Interleaving)?
交织是指在输出文件时,将不同流(如视频流、音频流)的数据交错写入,以减少文件头部的延迟并提高播放的流畅性。例如,在一个视频文件中,视频帧和音频帧可能会交错出现,这样播放器就可以在等待下一个视频帧的同时播放音频。
max_interleave_delta 的作用
max_interleave_delta
字段用于控制交织过程中允许的最大时间差(以时间戳为单位)。这个参数决定了在输出文件时,不同流之间的最大时间差。例如,如果一个视频帧的时间戳与紧接着的音频帧的时间戳相差超过了这个值,FFmpeg 可能会选择等待或插入额外的数据包来调整这种差值,以确保流的正确交织。
如何设置 max_interleave_delta
在 FFmpeg 的 API 中,你可以通过直接设置 AVFormatContext
结构体的 max_interleave_delta
字段来调整这个值。例如:
AVFormatContext *fmt_ctx;
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, "output.mp4");
if (!fmt_ctx) {
fprintf(stderr, "Could not create format context\n");
exit(1);
}
// 设置最大交织延迟为 100ms
fmt_ctx->max_interleave_delta = 100000; // 单位为微秒(1秒=1000000微秒)
注意事项
-
单位:
max_interleave_delta
的单位是微秒(1秒 = 1,000,000微秒)。 -
性能影响:增加
max_interleave_delta
的值可以减少编码时的等待时间,但可能会增加播放时的延迟。 -
合理设置:根据你的具体需求(如实时性要求、文件大小优化等)合理设置此值。
通过调整 max_interleave_delta
,你可以更好地控制输出多媒体文件的性能和播放体验。
打印
avformatContext->max_interleave_delta = 10000000
avformatContext->max_interleave_delta;
cout << "avformatContext->max_interleave_delta = " << avformatContext->max_interleave_delta << endl;
50 unsigned int max_picture_buffer;
/**
* Maximum amount of memory in bytes to use for buffering frames
* obtained from realtime capture devices.
*/
unsigned int max_picture_buffer;
用于缓冲从实时捕获设备获得的帧的最大内存量(字节)。
打印
avformatContext->max_picture_buffer = 3041280
51 int max_probe_packets;
/**
* Maximum number of packets that can be probed
* - encoding: unused
* - decoding: set by user
*/
int max_probe_packets;
设置最大 可探测的最大数据包数
AVFormatContext
结构体中的 max_probe_packets
字段用于控制格式探测阶段的数据包数量限制,其作用与封装格式的自动识别逻辑直接相关36。以下是详细解析:
1. 核心功能
-
格式探测(Probing)
当未明确指定输入文件的封装格式时,FFmpeg 会通过读取文件头部数据(或前几个数据包)自动推测格式类型。max_probe_packets
定义了此阶段允许读取的最大数据包数量,以避免因探测耗时过长影响性能36。 -
默认值与调整
- 典型默认值为
50
(具体值可能因 FFmpeg 版本而异)3。 - 若输入文件头部信息复杂或分布在多个数据包中,需适当增大此值以提高探测准确性36。
- 典型默认值为
2. 应用场景
-
特殊文件处理
对于头部信息分散的非标准文件(如某些流媒体分段文件),增加max_probe_packets
可确保 FFmpeg 读取足够的数据包完成格式识别36。 -
延时敏感场景
在实时流处理中,若需降低初始探测阶段的延迟,可减少此参数值,但可能牺牲格式识别的可靠性36。
3. 参数关联性
-
与
probesize
的区别
probesize
限制探测阶段的总字节数,而max_probe_packets
限制数据包数量。两者共同作用,确保探测过程在资源消耗和准确性间权衡36。 -
封装格式影响
某些格式(如 MPEG-TS)依赖多个数据包头信息,需更高的max_probe_packets
值以保证正确识别6。
4. 配置示例
AVFormatContext *fmt_ctx = avformat_alloc_context();
fmt_ctx->max_probe_packets = 100; // 允许探测最多100个数据包
avformat_open_input(&fmt_ctx, input_file, NULL, NULL);
总结
max_probe_packets
是 FFmpeg 格式探测机制的关键参数,通过控制数据包数量平衡识别精度与效率36。实际应用中需结合文件特性与性能需求动态调整。
默认打印值
avformatContext->max_probe_packets = 2500
52 int max_streams;一般也没啥用,什么情况下1000个不够用呢?
/**
* The maximum number of streams.
* - encoding: unused
* - decoding: set by user
*/
int max_streams;
在解复用的时候使用
打印值是 1000
cout << "avformatContext->max_streams = " << avformatContext->max_streams << endl;
结果为:
avformatContext->max_streams = 1000
53 int max_ts_probe,在解复用ts文件的时候用
/**
* Maximum number of packets to read while waiting for the first timestamp.
* Decoding only.
*/
int max_ts_probe;
int max_ts_probe:解码TS格式时,在得到第一个PTS前可解码的最大packet的数量。
同时关联的还有 avformatcontext 的 ts_id .
int ts_id:TS格式中流的pid
打印结果 avformatContext->max_ts_probe = 50
avformatContext->max_ts_probe;
cout << "avformatContext->max_ts_probe = " << avformatContext->max_ts_probe << endl;
从实现 看,是在 avformat_find_stream_info方法内部使用。
也就是说:如果我们读取的ts文件,那么有可能需要调整 max_ts_probe的值,当然越大,解析的包越多,花费的时间越多
54 元数据 AVDictionary *metadata;
/**
* Metadata that applies to the whole file.
*
* - demuxing: set by libavformat in avformat_open_input()
* - muxing: may be set by the caller before avformat_write_header()
*
* Freed by libavformat in avformat_free_context().
*/
AVDictionary *metadata;
参考
55 int metadata_header_padding;
/**
* Number of bytes to be written as padding in a metadata header.
* Demuxing: Unused.
* Muxing: Set by user via av_format_set_metadata_header_padding.
*/
int metadata_header_padding;
写入元数据头部时要写入的填充字节数。仅在封装时使用:通过av_format_set_metadata_header_padding由用户设置。
默认值是-1 avformatContext->metadata_header_padding = -1
avformatContext->metadata_header_padding;
cout << "avformatContext->metadata_header_padding = " << avformatContext->metadata_header_padding << endl;
56 unsigned int nb_chapters;
/**
* Number of chapters in AVChapter array.
* When muxing, chapters are normally written in the file header,
* so nb_chapters should normally be initialized before write_header
* is called. Some muxers (e.g. mov and mkv) can also write chapters
* in the trailer. To write chapters in the trailer, nb_chapters
* must be zero when write_header is called and non-zero when
* write_trailer is called.
* - muxing: set by user
* - demuxing: set by libavformat
*/
unsigned int nb_chapters;
AVChapter **chapters;
参考
中的 15 AVChapter **chapters;
57unsigned int nb_programs; and AVProgram **programs;-多路复用节目格式的封装,(如 MPEG-TS、MPEG-PS)
unsigned int nb_programs;
AVProgram **programs;
AVFormatContext
结构体中的 programs
字段用于管理与多节目(Program)相关的流集合,常见于支持多路复用节目格式的封装(如 MPEG-TS、MPEG-PS)。其具体作用如下:
1. 功能解析
-
节目(Program)的定义
某些容器格式(如 MPEG-TS)允许在单个文件中包含多个独立的节目(Program),每个节目由一组关联的音视频流构成(例如直播中的不同频道)。
programs
字段记录了这些节目的元数据及其关联的流索引。 -
字段类型与结构
programs
是AVProgram
结构体指针的数组,长度由nb_programs
字段表示;AVProgram
包含id
(节目唯一标识)、pmt_pid
(TS 流中的 PMT 表标识)、stream_index
数组(关联的流索引)等成员8。
2. 使用场景
// 遍历所有节目
for (int i = 0; i < fmt_ctx->nb_programs; i++) {
AVProgram *pgm = fmt_ctx->programs[i];
// 获取节目流信息
for (int j = 0; j < pgm->nb_stream_indexes; j++) {
int stream_idx = pgm->stream_index[j];
AVStream *stream = fmt_ctx->streams[stream_idx];
// 处理流...
}
}
-
流筛选优化
若需仅处理某一节目内的流,可直接通过programs
字段过滤,减少无效数据处理开销8。
3. 与其他字段的关系
-
streams
字段
streams
存储所有流的信息,而programs
通过流索引将特定流分组到节目中,形成逻辑上的层级关系58。
例如,TS 流中一个节目可能包含视频流、音频流和字幕流,这些流的索引会在AVProgram
的stream_index
数组中列出。 -
priv_data
字段
节目相关的高级参数(如 TS 流的 PMT 解析规则)可能存储在封装格式的私有数据(如MOVContext
、MPEGTSContext
)中,需结合priv_data
访问58。
总结
programs
主要用于支持多节目容器格式的解封装和流管理,是处理复杂流媒体场景(如数字电视、直播多路复用)的关键字段58。实际开发中需结合具体封装格式(如 TS)的文档进一步优化逻辑。
58 unsigned int nb_streams;
1. unsigned int nb_streams;
代表 avfromatContext 中 AVStream **streams 的个数
/**
* Number of elements in AVFormatContext.streams.
*
* Set by avformat_new_stream(), must not be modified by any other code.
*/
unsigned int nb_streams;
59 struct AVOutputFormat *oformat;
/**
* The output container format.
*
* Muxing only, must be set by the caller before avformat_write_header().
*/
ff_const59 struct AVOutputFormat *oformat;
60 open_cb 已过时,使用 io_open 替换
int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
@deprecated Use io_open and io_close.
61 int64_t output_ts_offset;
/**
* Output timestamp offset, in microseconds.
* Muxing: set by user
*/
int64_t output_ts_offset;
输出时间戳偏移,以微秒为单位。封装时由用户设置
AVFormatContext结构体中的output_ts_offset
字段用于在复用(Muxing)过程中对输出数据包的时间戳进行全局偏移调整。
AVFormatContext
中的 output_ts_offset
字段用于指定输出流的时间戳偏移值,主要影响封装时音视频数据的时间戳计算逻辑。以下是其核心作用和用法说明:
1. 功能解析
-
时间戳调整
在复用(Muxing)过程中,output_ts_offset
会将所有流的时间戳(PTS/DTS)增加一个固定偏移量,用于解决时间戳归零或对齐问题68。
例如,若需让输出文件的起始时间戳从特定值(如 10 秒)开始,可设置output_ts_offset = 10 * AV_TIME_BASE
。 -
适用场景
- 拼接多段媒体文件时,避免时间戳重叠;
- 直播推流中调整时间戳起始点以适配播放器要求6。
2. 设置方式
2.1 直接赋值
若封装格式支持该参数,可直接对 AVFormatContext
的 output_ts_offset
字段赋值:
AVFormatContext *oc = ...; // 封装上下文
oc->output_ts_offset = 1000000; // 偏移值为 1 秒(单位:AV_TIME_BASE)
2.2 通过 AVOption 设置
部分封装器支持通过 av_opt_set()
动态设置此参数:
av_opt_set_int(oc, "output_ts_offset", 1000000, 0);
3. 注意事项
- 封装格式兼容性
并非所有封装格式均支持此参数,需结合目标格式验证(如 MPEG-TS 支持,而 FLV 可能不支持); - 单位与精度
偏移值以AV_TIME_BASE
(微秒)为单位,需确保计算时避免溢出; - 与流级参数的关系
此参数作用于全局时间戳,若需单独调整某条流的时间戳,需通过AVStream
的时间基参数配合实现。
4. 示例场景
场景:分段录制文件拼接
录制多段视频后合并,需确保每段起始时间戳连续:
cCopy Code
// 第二段文件的偏移值 = 第一段总时长
oc->output_ts_offset = first_segment_duration;
avformat_write_header(oc, NULL); // 写入头 // 写入数据包...
总结
output_ts_offset
是控制封装时间戳对齐的关键参数,适用于时间戳重置、流拼接等场景68。使用时需确认目标封装格式的支持性,并注意时间单位转换。
1. 功能解析
- 时间戳偏移:
output_ts_offset
会对所有输出流的AVPacket的pts
和dts
值施加一个固定偏移量。用户可通过设置该字段实现时间戳的全局校准3。 - 应用场景:常用于多段内容拼接时对齐时间戳起始点,或调整不同流之间的同步关系。
2. 具体实现
- 写入时机:在调用
av_interleaved_write_frame()
写入数据包前,FFmpeg会根据output_ts_offset
调整时间戳值3。 - 与流级偏移的关系:若同时设置
AVStream
的mux_ts_offset
字段,output_ts_offset
会叠加生效3。
3. 使用注意事项
- 设置阶段:需在调用
avformat_write_header()
写入文件头前配置该字段3。 - 参数单位:偏移量以时间基(timebase)为单位,需与输出流的
AVStream.time_base
一致。 - 默认值:初始值为0,表示不应用偏移。
4. 示例代码片段
cCopy Code
AVFormatContext *oc; // ... 初始化输出上下文(oc)
oc->output_ts_offset = 1000; // 设置全局时间戳偏移量
avformat_write_header(oc, NULL); // 后续写入数据包时,pts/dts将自动加上该偏移
5. 相关字段对比
字段 | 作用范围 | 说明 |
---|---|---|
output_ts_offset | 全局生效 | 影响所有输出流的时间戳偏移 |
AVStream.mux_ts_offset | 单个流生效 | 仅作用于特定流的时间戳偏移3 |
该字段是FFmpeg复用层实现时间戳灵活控制的关键参数,适用于需要精确时间轴管理的场景(如直播推流、分段录制等)。
打印log
cout << "avformatContext->output_ts_offset = " << avformatContext->output_ts_offset << endl;
默认值是 avformatContext->output_ts_offset = 0
62 void *opaque;
/**
* User data.
* This is a place for some private data of the user.
*/
void *opaque;
AVFormatContext
结构体中的opaque
字段是用来存储用户自定义数据的。
什么是opaque
字段?
opaque
字段是一个指向void
的指针,这意味着你可以将任何类型的指针赋值给这个字段。FFmpeg在设计时考虑到了灵活性,允许用户在不修改FFmpeg核心代码的情况下,通过opaque
字段传递额外的信息或上下文到回调函数或其他部分。这在处理复杂的多媒体处理任务时非常有用,比如当你需要在解码或编码过程中访问特定的用户数据时。
如何使用opaque
字段?
1. 设置opaque
字段
你可以在初始化AVFormatContext
之后,通过直接赋值的方式设置opaque
字段:
AVFormatContext* format_ctx = avformat_alloc_context();
MyCustomData* data = malloc(sizeof(MyCustomData));
// 初始化data...
format_ctx->opaque = data;
2. 访问opaque
字段
在需要访问这些自定义数据时,你可以直接从AVFormatContext
中获取:
MyCustomData* data = format_ctx->opaque;
// 使用data...
注意事项
-
内存管理:当你不再需要使用通过
opaque
传递的数据时,确保适当地释放内存,以避免内存泄漏。 -
线程安全:如果你在多线程环境中使用FFmpeg,确保对
opaque
字段的访问是安全的,特别是在多个线程可能同时读写此字段时。 -
文档和示例:FFmpeg的官方文档和示例代码提供了关于如何使用
opaque
字段的更多指导和示例。查看这些资源可以帮助你更好地理解和应用这一功能。
通过合理利用opaque
字段,你可以在FFmpeg的框架内实现更加灵活和强大的多媒体处理功能。
63 unsigned int packet_size;
打印log 默认值为
avformatContext->packet_size = 0
在FFmpeg中,AVFormatContext
结构体的packetsize
字段用于指定输出封装格式的固定数据包大小,尤其在处理需要固定包大小的格式(如MPEG-TS流)时十分重要。以下是详细用法解析:
1. 核心功能与适用场景
- 固定包大小封装:某些封装格式(如MPEG-TS)要求每个数据包(Packet)具有固定大小。例如,TS流的标准包大小为188字节。通过
packetsize
字段,可强制设定输出数据包的固定大小。 - 网络传输优化:在UDP等网络协议传输中,合理设置包大小可避免IP分片,提升传输效率。例如,将
packetsize
设为1316字节(188 * 7)以适应以太网MTU限制。
2. 参数设置方法
- 直接赋值:在初始化
AVFormatContext
后,直接设置packetsize
字段:cCopy Code
AVFormatContext *fmt_ctx; // ... 初始化fmt_ctx(如通过avformat_alloc_output_context2) fmt_ctx->packetsize = 188; // 设置TS包大小为188字节
- 格式私有选项:某些格式(如MPEG-TS)允许通过
AVDictionary
传递私有参数(如mpegts_packet_size
),优先级高于packetsize
字段:cCopy Code
AVDictionary *options = NULL; av_dict_set(&options, "mpegts_packet_size", "188", 0); avformat_write_header(fmt_ctx, &options); // 写入头时传递选项
3. 使用注意事项
- 设置时机:需在调用
avformat_write_header()
前设置packetsize
,否则可能不生效。 - 格式兼容性:仅对支持固定包大小的封装格式有效。常见的适用格式包括:
- MPEG-TS(
mpegts
) - UDP传输(需结合协议层参数)
- MPEG-TS(
- 默认值:若未设置,某些格式(如TS)可能使用默认值(如188字节)。
- 优先级冲突:若同时设置
packetsize
和私有选项(如mpegts_packet_size
),后者优先级更高。
4. 示例代码(MPEG-TS封装)
cCopy Code
AVFormatContext *fmt_ctx = NULL; AVOutputFormat *fmt = av_guess_format("mpegts", NULL, NULL); avformat_alloc_output_context2(&fmt_ctx, fmt, NULL, "output.ts"); // 设置固定包大小 fmt_ctx->packetsize = 188; // 或通过字典选项设置(可选) AVDictionary *options = NULL; av_dict_set(&options, "mpegts_packet_size", "188", 0); // 写入文件头 avformat_write_header(fmt_ctx, &options); // ... 写入数据包(av_interleaved_write_frame)
5. 相关参数对比
参数/字段 | 作用范围 | 示例格式 | 说明 |
---|---|---|---|
AVFormatContext.packetsize | 全局生效 | MPEG-TS, UDP | 通用固定包大小设置(可能被覆盖) |
格式私有选项(如mpegts_packet_size ) | 格式特定 | MPEG-TS | 更高优先级的格式专用参数 |
6. 常见问题
- 为何设置后包大小仍不固定?
检查是否目标格式支持固定包大小,或是否被格式私有选项覆盖。 - TS包大小必须为188字节吗?
否。某些场景(如卫星传输)允许使用204字节(含16字节RS纠错码),需同步配置复用器和解析器。
通过合理使用packetsize
字段,可确保输出数据包符合特定封装格式或传输协议的要求,是流媒体开发中优化兼容性和性能的关键参数。
64 AVIOContext *pb;
/**
* I/O context.
*
* - demuxing: either set by the user before avformat_open_input() (then
* the user must close it manually) or set by avformat_open_input().
* - muxing: set by the user before avformat_write_header(). The caller must
* take care of closing / freeing the IO context.
*
* Do NOT set this field if AVFMT_NOFILE flag is set in
* iformat/oformat.flags. In such a case, the (de)muxer will handle
* I/O in some other way and this field will be NULL.
*/
AVIOContext *pb;
65 void *priv_data;
/**
* 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;
一、常规场景下的设置流程 有可能会用到。
-
上下文初始化
- 解封装(输入):
调用avformat_open_input()
时,FFmpeg自动探测媒体格式并实例化对应的AVInputFormat
,此时priv_data
会被自动分配并初始化(如MP4对应MOVContext
)。- 通过
avformat_open_input
函数解析文件时,FFmpeg会检测文件格式。对于MP4文件,识别到其封装格式对应的AVInputFormat为ff_mov_demuxer
可以看到 priv_data_size 就是 sizeof(MOVContext).
const AVInputFormat ff_mov_demuxer = { .name = "mov,mp4,m4a,3gp,3g2,mj2", .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), .priv_class = &mov_class, .priv_data_size = sizeof(MOVContext), .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,avif", .flags_internal = FF_FMT_INIT_CLEANUP, .read_probe = mov_probe, .read_header = mov_read_header, .read_packet = mov_read_packet, .read_close = mov_read_close, .read_seek = mov_read_seek, .flags = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS | AVFMT_SHOW_IDS, };
AVFormatContext的
priv_data
字段与MP4文件对应的MOVContext关联机制如下: -
格式探测与AVInputFormat确定
通过avformat_open_input
函数解析文件时,FFmpeg会检测文件格式。对于MP4文件,识别到其封装格式对应的AVInputFormat为ff_mov_demuxer
(即MP4/MOV解复用器)46。 -
私有上下文分配
AVInputFormat结构体中包含priv_data_size
字段,表示私有上下文结构体的大小。ff_mov_demuxer
会为MOVContext分配内存,并将其地址赋值给AVFormatContext的priv_data
字段67。例如:cCopy Code
s->priv_data = av_mallocz(sizeof(MOVContext));
-
初始化与数据填充
在mov_read_header
函数中,会进一步初始化MOVContext,解析MP4文件的moov
等原子结构,填充流信息(如stsz、stco等元数据)到MOVContext和AVStream的priv_data
中46。 -
后续操作依赖
解封装过程中(如av_read_frame
),FFmpeg通过AVFormatContext的priv_data
访问MOVContext,实现MP4文件样本索引定位、数据块偏移计算等逻辑48。 -
关键数据结构关系
plaintextCopy Code
AVFormatContext ├── iformat → ff_mov_demuxer ├── priv_data → MOVContext └── streams[] └── AVStream.priv_data → MOVStreamContext
其中
MOVContext
保存全局MP4文件属性,MOVStreamContext
存储单个流的具体样本信息46。
- 通过
- 封装(输出):
需手动创建AVFormatContext
后,通过avformat_alloc_context()
分配上下文,再通过avformat_new_stream()
等接口关联AVOutputFormat
,最终由avformat_write_header()
初始化priv_data
78。
- 解封装(输入):
-
参数配置
避免直接操作priv_data
内部成员,而是通过FFmpeg的AVOptions
机制间接设置参数。例如:av_opt_set(avfmt_ctx->priv_data, "movflags", "faststart", 0); // 设置MP4的快速启动参数
该方法通过键值对动态修改格式相关参数15。
二、自定义格式开发场景 基本用不到。
-
定义私有结构体
创建与格式绑定的私有数据结构(如MyFormatPrivateData
),并在AVInputFormat
/AVOutputFormat
中声明priv_data_size
字段以指定结构体大小:static const AVInputFormat my_demuxer = { .name = "my_format", .priv_data_size = sizeof(MyFormatPrivateData), .read_header = my_read_header, //... };
-
此设计确保FFmpeg在初始化时正确分配内存26。static const AVInputFormat my_demuxer = {
.name = "my_format",
.priv_data_size = sizeof(MyFormatPrivateData),
.read_header = my_read_header,
//...
};
-
操作私有数据
在格式的回调函数(如read_header
、write_packet
)中,通过AVFormatContext
访问priv_data
:cCopy Code
static int my_read_header(AVFormatContext *s) { MyFormatPrivateData *priv = s->priv_data; // 操作私有数据逻辑 }
此模式确保格式逻辑与数据隔离26。
三、关键注意事项
- 禁止直接内存操作
直接修改priv_data
指针或手动释放其内存可能导致内存泄漏或段错误,生命周期应由FFmpeg接口(如avformat_close_input()
)管理38。 - 版本兼容性
不同FFmpeg版本的同一格式可能修改私有数据结构,需通过AVOptions
或API接口保持兼容16。 - 调试技巧
使用av_dump_format()
或GDB检查priv_data
内容时,需结合具体格式的私有结构体定义解析内存布局8。
总结
priv_data
的设置高度依赖FFmpeg的封装格式框架:常规使用通过API自动管理,自定义格式开发需定义结构体并通过回调函数操作。参数调整优先使用AVOptions
机制,避免直接访问底层数据以提升健壮性12。
66 int64_t probesize;
/**
* Maximum size of the data read from input for determining
* the input container format.
* Demuxing only, set by the caller before avformat_open_input().
*/
int64_t probesize;
在avformat_find_stream_info 方法中使用,目的是找到该文件的format
avfromatContext 中可以优化的参数。format_probesize,fps_probe_size,probesize,max_analyze_duration-优快云博客
67 int probe_score;
/**
* format probing score.
* The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes
* the format.
* - encoding: unused
* - decoding: set by avformat, read by user
*/
int probe_score;
在 FFmpeg 库中,AVFormatContext
结构体是用于表示多媒体容器格式的上下文。这个结构体包含了关于媒体文件的各种信息,例如流的类型、时长、比特率等。probe_score
是 AVFormatContext
结构体中的一个字段,它主要用于指示探测器(demuxer)对该格式的置信度评分。
解释 probe_score
probe_score
是一个整数,它的值范围通常是 0 到 100。这个分数越高,表示 FFmpeg 对当前媒体文件格式的猜测越有信心。这个分数是基于多种因素计算得出的,包括但不限于:
-
文件头部的特定标志(magic number)。
-
文件内容的特定模式或结构。
-
文件名(虽然这不是决定性的,但在某些情况下可以作为辅助判断)。
如何使用 probe_score
当你使用 FFmpeg 的 API 来打开一个媒体文件时,FFmpeg 会尝试通过多种方式探测文件的格式。这个过程通常涉及多个步骤,每个步骤都可能增加或减少 probe_score
的值。例如:
-
初始探测:基于文件头部的几个字节(通常是前几个字节或几个特定的模式),FFmpeg 可以初步判断文件类型。
-
完整探测:如果需要更精确的结果,FFmpeg 会尝试读取更多的文件内容来进行更深入的分析。
示例代码
下面是一个简单的示例,展示如何在使用 FFmpeg 的 API 时获取并检查 probe_score
#include <libavformat/avformat.h>
int main(int argc, char *argv[]) {
AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) != 0) {
fprintf(stderr, "Cannot open input file\n");
return -1;
}
// 探测格式
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "Cannot find stream information\n");
return -1;
}
// 输出 probe_score
printf("Probe score: %d\n", fmt_ctx->iformat->flags & AVFMT_GENERIC_INDEX ? 50 : 100);
// 注意:在一些情况下,可能需要手动设置或检查 probe_score,因为标准的 API 可能不直接提供对 probe_score 的访问。
// 但通常,你可以通过上下文来判断探测的可靠性。例如,使用 iformat->flags 检查是否为通用索引格式可能会有所帮助。
// 释放资源
avformat_close_input(&fmt_ctx);
return 0;
}
注意点
在最新的 FFmpeg 版本中,直接访问 probe_score
可能不是标准 API 的一部分。通常,你可以通过上下文的其他属性或通过探测过程的结果来间接判断。例如,你可以检查 fmt_ctx->iformat
是否为 NULL
来判断是否成功探测到格式,或者通过 avformat_find_stream_info()
的返回值来判断信息是否被成功读取。如果需要更详细的控制或了解探测的细节,可能需要查看 FFmpeg 的内部实现或使用更底层的函数来手动管理探测过程。
总之,虽然直接访问 probe_score
可能不是最直接的方法,但通过理解 FFmpeg 的探测机制和如何利用其返回的其他信息,你可以有效地管理和利用这些信息来处理媒体文件。
打印log
cout << "avformatContext->probe_score = " << avformatContext->probe_score << endl;
avformatContext->probe_score = 100
应用场景
该字段主要用于格式探测阶段的决策:
- 当多个解封装器(Demuxer)匹配同一文件时,选择
probe_score
最高的格式8 - 调试时可观察该值判断格式探测准确性5
68 unsigned int nb_programs; and AVProgram **programs;-多路复用节目格式的封装,(如 MPEG-TS、MPEG-PS)
参见57
69 char *protocol_blacklist;
/**
* ',' separated list of disallowed protocols.
* - encoding: unused
* - decoding: set by user
*/
char *protocol_blacklist;
AVFormatContext的protocol_blacklist
字段用于限制FFmpeg在访问媒体流时禁止使用的协议类型,其核心特性及用法如下:
一、字段作用与语法
-
协议过滤机制
该字段通过逗号分隔的协议名称列表(如tcp,udp
)指定禁用协议,常用于以下场景:- 强制使用安全协议(如禁用
rtmp
而启用https
) - 规避网络环境限制(如禁用UDP传输)
- 解决协议兼容性问题56
- 强制使用安全协议(如禁用
-
优先级规则
与protocol_whitelist
共存时,黑名单优先级更高(若同一协议同时出现在白名单和黑名单中,最终会被禁用)6
二、设置方法
-
通过
av_dict_set()
动态设置
在调用avformat_open_input()
前通过AVDictionary
传递参数:AVDictionary *options = NULL;
av_dict_set(&options, "protocol_blacklist", "rtmp,udp", 0); // 禁用RTMP和UDP协议 avformat_open_input(&fmt_ctx, url, NULL, &options); -
命令行参数设置
在使用FFmpeg工具时直接指定:bashCopy Code
ffmpeg -protocol_blacklist file,http -i input.m3u8
三、关联逻辑
-
协议探测阶段生效
此字段在avformat_open_input()
的协议初始化阶段被解析,影响后续网络层协议选择56 -
全局与上下文作用域
- 全局设置:通过
avformat_network_init()
影响所有上下文 - 上下文设置:仅对当前
AVFormatContext
实例生效67
- 全局设置:通过
四、注意事项
- 协议名称规范
需使用FFmpeg内部定义的协议标识符(如tls
而非https
),可通过ffmpeg -protocols
查看完整列表6 - 错误处理
若黑名单包含所有可用协议,FFmpeg会返回AVERROR_PROTOCOL_NOT_FOUND
错误56
示例场景
禁用所有非安全协议访问HLS流:
AVDictionary *options = NULL;
av_dict_set(&options, "protocol_whitelist", "https,tls", 0); // 启用安全协议 av_dict_set(&options, "protocol_blacklist", "http,udp", 0); // 禁用非加密协议 avformat_open_input(&fmt_ctx, url, NULL, &options);
此配置强制使用HTTPS/TLS协议建立连接56
总结
protocol_blacklist
通过协议过滤机制增强了媒体访问的安全性及可控性,开发者应结合protocol_whitelist
实现细粒度的协议管控,尤其在处理网络流时需注意协议名称的精确匹配
70 char *protocol_whitelist;
/**
* ',' separated list of allowed protocols.
* - encoding: unused
* - decoding: set by user
*/
char *protocol_whitelist;
AVFormatContext
的protocol_whitelist
字段用于设定FFmpeg访问媒体流时允许使用的协议类型,其核心特性及用法如下:
一、字段作用与语法
-
协议白名单机制
protocol_whitelist
通过逗号分隔的协议名称列表(如https,tls
)指定允许使用的协议,典型应用场景包括:- 限制仅使用安全协议(如仅允许
https
) - 强制使用特定传输方式(如仅允许本地文件协议
file
) - 避免协议探测冲突(如排除冗余协议干扰)13
- 限制仅使用安全协议(如仅允许
-
与黑名单的优先级
若同时设置protocol_whitelist
和protocol_blacklist
,黑名单生效优先级更高(例如白名单包含http,https
,黑名单包含http
,则最终仅允许https
)15
二、设置方法
-
通过
av_dict_set()
动态配置
在调用avformat_open_input()
前通过字典传递参数:AVDictionary *options = NULL;
av_dict_set(&options, "protocol_whitelist", "file,https", 0); // 仅允许文件协议和HTTPS
avformat_open_input(&fmt_ctx, url, NULL, &options);此方法适用于需要动态控制协议类型的场景16
-
命令行参数设置
直接通过FFmpeg工具指定:bashCopy Code
ffmpeg -protocol_whitelist https -i input.m3u8
此方式适用于快速测试或脚本调用17
三、注意事项
- 协议名称规范
需使用FFmpeg内部定义的协议标识符(如tls
对应加密协议),可通过ffmpeg -protocols
查看完整列表15 - 生效阶段
协议白名单在avformat_open_input()
的协议探测阶段生效,直接影响后续网络层的协议选择56 - 错误处理
若白名单中无可用协议,FFmpeg将返回AVERROR_PROTOCOL_NOT_FOUND
错误57
四、示例场景
强制使用加密协议访问直播流:
AVDictionary *options = NULL;
av_dict_set(&options, "protocol_whitelist", "https,tls", 0); // 仅允许HTTPS和TLS av_dict_set(&options, "protocol_blacklist", "http,rtmp", 0); // 排除非加密协议 avformat_open_input(&fmt_ctx, url, NULL, &options);
此配置可防止使用非安全协议建立连接13
总结
protocol_whitelist
通过正向过滤机制强化了协议选择的可靠性,建议与protocol_blacklist
配合使用以实现更精细的协议控制。开发中需注意协议名称的精确匹配及初始化阶段的参数传递时序15。
log 打印默认值:
avformatContext->protocol_blacklist = nullptr
avformatContext->protocol_whitelist = file,crypto,data
if (avformatContext->protocol_blacklist == nullptr) {
cout << "avformatContext->protocol_blacklist = nullptr" << endl;
}
else {
cout << "avformatContext->protocol_blacklist = " << avformatContext->protocol_blacklist << endl;
}
if (avformatContext->protocol_whitelist == nullptr) {
cout << "avformatContext->protocol_whitelist = nullptr" << endl;
}
else {
cout << "avformatContext->protocol_whitelist = " << avformatContext->protocol_whitelist << endl;
}
70 int seek2any;
在解码时候,可以让user 跳转到任何的 frame。
如果是视频,I,P,B 帧,如果跳转到的frame不是I帧,则肯定花屏。
/**
* Force seeking to any (also non key) frames.
* - encoding: unused
* - decoding: Set by user
*/
int seek2any;
该字段 和 int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts,
int64_t ts, int64_t max_ts, int flags) 方法的最后一个 flags 共同作用于 seek file时的影响。
参考avformat_seek_file源码。只要seek2any>0, avformat_seek_file中的flag就会 |=AVSEEK_FLAG_ANY
if (s->seek2any > 0)
flags |= AVSEEK_FLAG_ANY;
avformat_seek_file 函数详解,av_seek_frame函数-优快云博客
int avformat_seek_file(AVFormatContext *s, int stream_index, int64_t min_ts,
int64_t ts, int64_t max_ts, int flags)
{
if (min_ts > ts || max_ts < ts)
return -1;
if (stream_index < -1 || stream_index >= (int)s->nb_streams)
return AVERROR(EINVAL);
if (s->seek2any > 0)
flags |= AVSEEK_FLAG_ANY;
flags &= ~AVSEEK_FLAG_BACKWARD;
打印默认值 avformatContext->seek2any = 0
cout << "avformatContext->seek2any = " << avformatContext->seek2any << endl;
71 int skip_estimate_duration_from_pts;
跳过estimate_times_from_pts中的持续时间计算
/**
* Skip duration calcuation in estimate_timings_from_pts.
* - encoding: unused
* - decoding: set by user
*/
int skip_estimate_duration_from_pts;
从源码可以看到,是在 estimate_timings_from_pts方法中使用,该方法声明为:在 MPEG-PS streams 时会使用。
/* only usable for MPEG-PS streams */
static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset)
也就是说,要明白为什么有这个字段,以及真正的用法,首先要明白 MPEG-PS 协议,才能深刻理解。
todo
打印默认值
avformatContext->skip_estimate_duration_from_pts = 0
72 int64_t skip_initial_bytes;
/**
* Skip initial bytes when opening stream
* - encoding: unused
* - decoding: Set by user
*/
int64_t skip_initial_bytes;
skip_initial_bytes
字段是用来指示在读取数据时应该跳过的初始字节数。这在某些情况下非常有用,比如当你想要跳过文件头部的某些特定数据(例如某些特定的文件格式可能在文件开头有特定的标记或者元数据),或者在某些编解码器需要特定的初始化数据但又不想让它们出现在解码的输出流中时。
如何使用 skip_initial_bytes
-
设置
skip_initial_bytes
:在打开媒体文件之前,你可以设置
AVFormatContext
的skip_initial_bytes
字段
AVFormatContext* formatCtx = NULL;
avformat_open_input(&formatCtx, "input.mp4", NULL, NULL);
if (!formatCtx) {
fprintf(stderr, "Could not open input file\n");
return -1;
}
// 设置跳过的字节数
formatCtx->skip_initial_bytes = 1024; // 例如,跳过前1024字节
2. 读取媒体信息:
在设置了 skip_initial_bytes
后,当你调用 avformat_find_stream_info
或其他相关函数来读取媒体信息时,前定义的字节数将被跳过。
if (avformat_find_stream_info(formatCtx, NULL) < 0) {
fprintf(stderr, "Could not get stream information\n");
return -1;
}
注意事项
-
正确性: 确保你知道要跳过的字节是正确的,否则可能会导致数据解析错误或丢失重要的媒体信息。
-
兼容性: 不是所有的容器格式都支持在读取前跳过字节。某些格式可能在文件头部有固定的结构,强行跳过可能导致无法正确解析。
-
调试: 如果在处理过程中遇到问题,可以尝试不设置
skip_initial_bytes
或设置为0,看是否能正常解析。
通过合理使用 skip_initial_bytes
,你可以更灵活地处理特定的媒体文件格式,尤其是在处理那些有特殊文件头或需要特殊处理的媒体文件时。
打印默认值
avformatContext->skip_initial_bytes = 0
73 int64_t start_time;
/**
* Position of the first frame of the component, in
* AV_TIME_BASE fractional seconds. NEVER set this value directly:
* It is deduced from the AVStream values.
*
* Demuxing only, set by libavformat.
*/
int64_t start_time;
AVFormatContext 再分析一-优快云博客 5. int64_t start_time;
74 int64_t start_time_realtime;
从1970年1月1号00:00开始的 microseconds 数,如果为AV_NOPTS_VALUE,表示没有设置。
/**
* Start time of the stream in real world time, in microseconds
* since the Unix epoch (00:00 1st January 1970). That is, pts=0 in the
* stream was captured at this real world time.
* - muxing: Set by the caller before avformat_write_header(). If set to
* either 0 or AV_NOPTS_VALUE, then the current wall-time will
* be used.
* - demuxing: Set by libavformat. AV_NOPTS_VALUE if unknown. Note that
* the value may become known after some number of frames
* have been received.
*/
int64_t start_time_realtime;
打印默认值
cout << "avformatContext->start_time_realtime = " << hex << avformatContext->start_time_realtime << endl;
cout << "AV_NOPTS_VALUE = " << hex << AV_NOPTS_VALUE << endl;
avformatContext->start_time_realtime = 8000000000000000
AV_NOPTS_VALUE = 8000000000000000
这个有啥用呢?todo
75 AVStream **streams;
/**
* A list of all streams in the file. New streams are created with
* avformat_new_stream().
*
* - demuxing: streams are created by libavformat in avformat_open_input().
* If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also
* appear in av_read_frame().
* - muxing: streams are created by the user before avformat_write_header().
*
* Freed by libavformat in avformat_free_context().
*/
AVStream **streams;
在解码时,通过 avformat_open_input 方法获得,如果没有头部,那可能需要等到av_read_frame方法的时候才能获得。
在编码时候,需要在avformat_write_header()方法调用前创建。
/**
* A list of all streams in the file. New streams are created with
* avformat_new_stream().
*
* - demuxing: streams are created by libavformat in avformat_open_input().
* If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also
* appear in av_read_frame().
* - muxing: streams are created by the user before avformat_write_header().
*
* Freed by libavformat in avformat_free_context().
*/
AVStream **streams;
AVFormatContext 结构体中 streams
字段解析
1. 定义与作用
streams
是 AVFormatContext
的核心成员之一,用于存储媒体文件中所有音视频流、字幕流等的信息12。
- 数据类型:
AVStream **
(指向AVStream
指针数组的指针); - 流数量:通过
nb_streams
字段获取当前媒体文件中的总流数57; - 流类型:每个
AVStream
对应一个独立的媒体流(如视频流、音频流、字幕流等)
76 int strict_std_compliance;
该字段设置和 在 AVCodecContext.strict设置的功能一样
/**
* Allow non-standard and experimental extension
* @see AVCodecContext.strict_std_compliance
*/
int strict_std_compliance;
strict_std_compliance
用于控制 FFmpeg 在处理多媒体流时的严格性标准。这个字段的类型通常是 int
,并且它影响了 FFmpeg 在解析、编码和解码过程中的行为。
strict_std_compliance 的值
strict_std_compliance
字段的值可以是以下几种:
-
MKV_EBML_STRICT_INVALID_TOP_SCOPE:通常设置为 0,表示不对顶级作用域(top scope)的无效元素进行严格检查。这对于一些不符合标准的 EBML 文件是有用的。
-
MKV_EBML_STRICT_UTF8:设置为 1,要求所有字符串都必须是有效的 UTF-8 编码,这对于 EBML 文件来说很重要。
-
FF_COMPLIANCE_NORMAL:通常设置为 -1,表示使用默认的兼容性设置,这通常是最宽松的设置,旨在最大化兼容性。
-
FF_COMPLIANCE_STRICT:设置为 2,这要求 FFmpeg 严格遵守标准,对于不符合标准的输入将报错或警告。
-
FF_COMPLIANCE_EXPERIMENTAL:设置为 3,允许使用实验性的编解码器或特性,这可能会导致不稳定或不可预测的行为。
#define FF_COMPLIANCE_VERY_STRICT 2 ///< Strictly conform to an older more strict version of the spec or reference software.
#define FF_COMPLIANCE_STRICT 1 ///< Strictly conform to all the things in the spec no matter what consequences.
#define FF_COMPLIANCE_NORMAL 0
#define FF_COMPLIANCE_UNOFFICIAL -1 ///< Allow unofficial extensions
#define FF_COMPLIANCE_EXPERIMENTAL -2 ///< Allow nonstandardized experimental things.
FF_COMPLIANCE_EXPERIMENTAL 是ffmpeg后期版本添加的
如何设置 strict_std_compliance
在 FFmpeg 的 API 中,你可以通过设置 AVFormatContext
的 strict_std_compliance
字段来控制其行为。例如:
AVFormatContext* formatCtx = avformat_alloc_context();
if (!formatCtx) {
// 错误处理
}
// 设置 strict_std_compliance 为严格模式
formatCtx->strict_std_compliance = FF_COMPLIANCE_STRICT;
使用场景
当你需要确保你的多媒体处理严格符合某个标准(例如,当你处理的是需要精确遵循规范的媒体文件时),你可以设置 strict_std_compliance
为 FF_COMPLIANCE_STRICT
。相反,如果你需要最大化兼容性或者正在处理一些不那么严格的文件格式,可以设置为 FF_COMPLIANCE_NORMAL
或其他值。
注意
在使用这些设置时,要确保你对所处理的媒体文件格式有足够的了解,以避免因过于严格的设置而导致的错误或兼容性问题。在某些情况下,过于严格的设置可能会导致 FFmpeg 无法正确处理某些合法的媒体文件。因此,选择合适的设置是很重要的。
77 AVCodec *subtitle_codec;
允许使用一样的AVCodec 解析字幕,
AVFormatContext 再分析一-优快云博客 10-11
/**
* Forced subtitle codec.
* This allows forcing a specific decoder, even when there are multiple with
* the same codec_id.
* Demuxing: Set by user
*/
AVCodec *subtitle_codec;
78 enum AVCodecID subtitle_codec_id;
79 AVCodec *video_codec;
允许使用一样的AVCodec 解析字幕,
AVFormatContext 再分析一-优快云博客 8-9
/**
* Forced video codec.
* This allows forcing a specific decoder, even when there are multiple with
* the same codec_id.
* Demuxing: Set by user
*/
AVCodec *video_codec;
80 enum AVCodecID video_codec_id;
/**
* Forced video codec_id.
* Demuxing: Set by user.
*/
enum AVCodecID video_codec_id;
81 int ts_id;
/**
* Transport stream id.
* This will be moved into demuxer private options. Thus no API/ABI compatibility
*/
int ts_id;
AVFormatContext 中的 ts_id
字段解析
1. 字段定义与作用
ts_id
是 AVFormatContext 结构体中用于标识多信道 TS (Transport Stream) 视频文件的文件 ID 的成员变量。根据搜索结果,该字段在多信道 TS 流处理中具有以下特性1:
- 固定值属性:在多信道 TS 文件中,
ts_id
通常为固定值,用于区分不同的传输流文件; - 关联菜单 ID:与
programs
结构体中的id
字段配合,可定位特定信道对应的视频、音频或菜单流1。
2. 应用场景示例
在处理多信道 TS 文件(如数字电视广播流)时,ts_id
可用于筛选特定信道的媒体流:
AVFormatContext *fmt_ctx = avformat_alloc_context();
avformat_open_input(&fmt_ctx, "multi_channel.ts", NULL, NULL); avformat_find_stream_info(fmt_ctx, NULL);
// 遍历所有节目(programs)
for (int i = 0; i < fmt_ctx->nb_programs; i++)
{
AVProgram *program = fmt_ctx->programs[i];
if (program->id == fmt_ctx->ts_id) {
// 匹配当前文件的 ts_id // 处理该信道关联的流
}
}
typedef struct AVProgram {
int id;
int flags;
enum AVDiscard discard; ///< selects which program to discard and which to feed to the caller
unsigned int *stream_index;
unsigned int nb_stream_indexes;
AVDictionary *metadata;
int program_num;
int pmt_pid;
int pcr_pid;
int pmt_version;
/*****************************************************************
* 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.
* New public fields should be added right above.
*****************************************************************
*/
int64_t start_time;
int64_t end_time;
int64_t pts_wrap_reference; ///< reference dts for wrap detection
int pts_wrap_behavior; ///< behavior on wrap detection
} AVProgram;
3. 注意事项
- 字段可见性:
ts_id
可能仅在处理 TS 封装格式时有效,其他格式(如 MP4、FLV)中可能无意义或未定义1; - 版本兼容性:部分 FFmpeg 版本可能将此字段作为私有数据(通过
fmt_ctx->priv_data
访问),需结合源码验证具体实现16; - 流选择逻辑:多信道 TS 文件的流顺序不确定,需结合
programs
结构体的id
和streams
的codec_type
综合筛选目标流1。
4. 功能关联扩展
- 流类型识别:通过
AVStream->codecpar->codec_type
区分视频、音频流18; - 时间基准:使用
AVStream->time_base
转换时间戳,与ts_id
共同实现精确流同步
82 int use_wallclock_as_timestamps;
/**
* forces the use of wallclock timestamps as pts/dts of packets
* This has undefined results in the presence of B frames.
* - encoding: unused
* - decoding: Set by user
*/
int use_wallclock_as_timestamps;
-
类型:通常是一个整数(int),在某些版本的 FFmpeg 中也可能是布尔值(例如,从 FFmpeg 4.1 开始,它被标记为布尔值)。
-
作用:这个字段用于指示解码器是否应该使用系统时钟作为时间戳。当设置为非零值时,解码器将使用系统时间(墙上时间或墙钟时间)作为时间戳的基础。这对于某些特定的应用场景(如需要精确到毫秒级的同步处理)非常有用。
使用场景
-
精确同步:如果你在处理视频或音频流时需要非常精确的时间控制,比如同步多个流或者进行实时处理,设置
use_wallclock_as_timestamps
为真可以帮助你更准确地控制播放或处理的时间点。 -
实时应用:在开发实时流媒体应用时,确保时间戳的准确性对于同步多个来源的数据至关重要。
如何设置
在初始化 AVFormatContext
并调用 avformat_open_input
后,你可以直接设置 use_wallclock_as_timestamps
字段:
AVFormatContext* formatCtx = NULL;
avformat_open_input(&formatCtx, "input.file", NULL, NULL);
if (formatCtx) {
// 设置 use_wallclock_as_timestamps 为 true
formatCtx->use_wallclock_as_timestamps = 1; // 或者在某些版本中使用 formatCtx->pb->is_writable = 1;
// 其他初始化代码...
}
注意事项
-
版本兼容性:确保你的 FFmpeg 版本支持这一特性。在一些旧版本中,这一字段可能不存在或行为不同。
-
性能影响:使用系统时间作为时间戳可能会增加一些性能开销,尤其是在高频率的时间戳更新时。
-
精确度:虽然使用系统时间可以提高时间戳的精确度,但也可能受到系统时间同步性的影响。
总之,use_wallclock_as_timestamps
是一个有用的选项,可以帮助开发者在需要高精度时间控制的场景中更好地处理多媒体数据。在使用时,务必考虑其对性能和同步准确性的影响。
FFmpeg 在处理实时流(如 RTP、UDP 等)时的一个解封装选项。该选项通过 AVDictionary
传递给解封装器,用于控制时间戳的生成方式58。
- 功能:当设置为
1
时,强制使用系统时钟(wall clock)作为输入流的时间戳基准,替代默认的流内部时间戳5; - 适用场景:实时流中存在时间戳错误或缺失时,防止播放卡顿或同步问题8。
2. 使用方法示例
通过 av_dict_set()
设置选项,并在打开输入流时传递参数:
cCopy Code
AVFormatContext *fmt_ctx = avformat_alloc_context(); AVDictionary *options = NULL; av_dict_set(&options, "use_wallclock_as_timestamps", "1", 0); // 启用系统时钟时间戳 avformat_open_input(&fmt_ctx, "rtsp://example.com/live", NULL, &options); avformat_find_stream_info(fmt_ctx, NULL);
3. 核心关联机制
- 时间基准调整:启用后,
AVPacket.pts
和AVPacket.dts
将基于系统时钟生成,而非流内部时间戳58; - 流同步影响:需结合
AVStream.time_base
和AVCodecContext.pkt_timebase
进行时间戳转换,确保音视频同步68。
4. 注意事项
- 格式限制:仅对支持实时流传输的封装格式(如 RTSP、UDP)有效,文件类格式(MP4、FLV)通常忽略此选项5;
- 性能影响:若网络流本身时间戳准确,启用此选项可能导致额外计算开销8;