资讯详情

资讯详情

FFmpeg核心函数avformat_open_input深度解析与实践指南

avformat_open_input这个函数做FFmpeg开发的人几乎天天跟它打交道但说实话真正把它用明白、用透的人并不算多。很多刚接触音视频开发的读者照着网上代码抄了一遍发现能跑通就不再深究了结果一换到RTSP流、一换到特殊封装格式就各种踩坑。这篇文章我想把avformat_open_input掰开揉碎讲清楚从函数签名、参数细节、内部机制到实战代码再到常见错误的排查思路一次性说完。不管你是刚入门想搞懂API背后的逻辑还是已经写了几年FFmpeg想查漏补缺这篇内容都值得你花十几分钟认真看完。1. 函数签名与参数逐个拆解先看最基础的东西函数原型长这样int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);返回值是int类型返回0表示成功返回负值就是AVERROR系列的错误码。这四个参数每一个都有讲究我一个个说。1.1 第一个参数AVFormatContext **ps双重指针的用意这是一个双重指针指向AVFormatContext指针的地址。它是输入也是输出。调用之前你可以把*ps设为NULL也可以传入一个已经由avformat_alloc_context()分配好的上下文。如果传入NULLavformat_open_input内部会自动帮你分配一个AVFormatContext。函数成功返回后*ps就是一个已经打开了媒体文件、而且已经解析好封装格式头部的上下文。这个上下文之后要传给avformat_find_stream_info()继续探测流信息最终用完要调用avformat_close_input()释放。这里有个关键点你传入的AVFormatContext和函数内部实际使用的AVFormatContext可能是同一个也可能不是同一个。在极个别情况下当函数内部做了重新分配或格式探测行为改变时指针会被更新。这也是为什么参数要设计成AVFormatContext **而不是AVFormatContext *——就是为了能在函数内部替换掉这个指针指向。所以写代码时请一定记住要检查函数返回之后*ps的值别拿着旧指针当宝贝。我自己喜欢这样写AVFormatContext *fmt_ctx NULL; int ret avformat_open_input(fmt_ctx, url, NULL, NULL); if (ret 0) { // 处理错误 }先声明为NULL直接交给函数去分配。这个写法最简单也最不容易出错。1.2 第二个参数url不只是文件路径url参数字面意思是统一资源定位符。FFmpeg的架构里它既可以是本地文件路径也可以是网络流地址还可以是设备节点名。这个字符串会被传入协议层去解析决定最终通过哪种协议读取数据。常见的几种url形态包括本地文件/home/user/video.mp4、C:\videos\test.flvHTTP/HTTPShttp://example.com/file.mp4RTSP/RTPrtsp://192.168.1.100:554/live/0UDP组播udp://239.0.0.1:1234设备节点/dev/video0Linux下的V4L2设备自定义协议myprotocol://...URL能否被识别取决于FFmpeg编译时是否包含了对应的协议组件demuxer和protocol。这里有个坑想提醒大家在Windows平台上处理中文路径时FFmpeg的url解析可能出问题典型的就是文件路径里带中文打开就会返回AVERROR(ENOENT)。解决方法是把路径先转成UTF-8编码再传入或者直接用file:协议前缀比如file:///D:/视频/测试.mp4。我遇到过好几次都是因为路径编码问题排查了半天才发现是url的编码不对。1.3 第三个参数AVInputFormat *fmt选对格式事半功倍很多人在调用时直接传NULL让FFmpeg自己去探测封装格式这个当然可以而且是官方推荐的做法。但了解这个参数的意义还是有必要的。AVInputFormat本质上是一个描述“如何读取某种封装格式”的结构体它包含格式名称、扩展名、MIME类型、以及读头部和读数据包的回调函数指针。当你指定一个具体的AVInputFormat时FFmpeg会跳过格式探测阶段直接用你指定的格式去解析这样可以节省探测时间尤其在实时流的场景下非常有用。比如你知道要打开的一定是FLV流就可以通过av_find_input_format(flv)拿到对应的格式然后这样调用AVInputFormat *iformat av_find_input_format(flv); if (!iformat) { // 当前FFmpeg编译不支持flv } ret avformat_open_input(fmt_ctx, url, iformat, NULL);大多数情况下传NULL让FFmpeg自动探测就够了。但有两个场景我建议还是显式指定格式面对一些格式探测不稳定的流比如纯音频的AAC裸流有时会探测成ADTS和LATM不确定实时流要求快速出画面不想等探测阶段耗时太长。用NULL自动探测FFmpeg会先通过文件扩展名或协议头猜测可能格式列表然后逐个尝试读取数据来匹配这个过程消耗的时间在毫秒到几百毫秒之间。对于本地文件没问题但对于缓存有限的网络流来说有可能造成额外的延迟。1.4 第四个参数AVDictionary **options高级选项都藏在这里options参数是一个指向AVDictionary指针的地址用于传入额外的选项配置。这些选项会传递给解复用器、解码器或协议层用来控制具体的打开行为。传NULL表示不设置任何额外选项用默认行为。选项的实际支持范围取决于你使用的解复用器、协议和FFmpeg的编译配置。常见的几个选项probesize控制格式探测时读取的最大字节数默认是5000000约5MB调小可以加快打开速度但可能降低探测准确性analyzeduration控制avformat_find_stream_info()分析流信息的最大时长默认是5000000微秒5秒调小可以加快启动但可能拿不到完整的流信息rw_timeout设置IO读写的超时时间单位微秒对网络流尤其重要比如rw_timeout3000000表示3秒超时rtsp_transportRTSP协议下选择传输方式可以设成tcp或udp比如rtsp_transporttcpuser_agentHTTP请求的User-Agent头某些服务器会检查这个字段reconnectHTTP协议是否自动重连设成1表示开启reconnect_streamed对流式HTTP是否也自动重连。用代码设置选项的方式如下AVDictionary *opts NULL; av_dict_set(opts, rw_timeout, 3000000, 0); av_dict_set(opts, rtsp_transport, tcp, 0); ret avformat_open_input(fmt_ctx, url, NULL, opts);注意options这个指针在函数调用后可能被修改函数内部会对字典做处理如果之前设置过某些选项调用完avformat_open_input之后最好检查一下字典里的内容是否和你设置的一致。遇到不支持的选项字典里会保留该项遇到已被消费的选项它会从字典中被移除。所以如果函数返回了错误你可以通过遍历字典看到哪些选项没被消费帮助定位问题。2. 内部机制与工作流程分析这一节我们往下挖一层看看avformat_open_input打开文件时背后到底发生了什么事情。2.1 从URL到协议解析的完整链路avformat_open_input的内部流程可以简化成下面几个核心步骤如果*ps为NULL内部调用avformat_alloc_context()分配AVFormatContext创建AVIOContext通过url参数确定协议并调用对应的协议层代码建立输入输出通道如果fmt参数为NULL调用av_probe_input_format2()等探测函数从IO中读取数据最多读取probesize字节并匹配封装格式找到匹配的AVInputFormat后把它赋值给AVFormatContext的iformat字段调用fmt-read_header()即封装格式的读头部函数解析文件头、关键表比如MP4的moov box、视频流基本参数等函数返回。这个过程涉及三层协议层AVIOContext的底层实现、探测层格式探测算法、封装解析层read_header。每一层都有值得展开说的地方。2.2 格式探测为什么有些文件打开慢有些打不开av_probe_input_format2()的工作方式是拿着读取到的二进制数据和FFmpeg内置的每种封装格式的探测评分表做匹配。每种格式定义了一组“探测特征”并给出一个评分。比如FLV格式只要看到前几个字节是FLV这个ASCI I字符串评分就会非常高而MP4格式则是通过识别ftypbox等特征来判断。探测算法按评分从高到低排序选出一个最高分的格式作为最终结果。有个score阈值的概念——如果最高分低于一定阈值通常AVPROBE_SCORE_RETRY是25分FFmpeg会认为探测不可靠会继续读取更多数据再次尝试最多尝试若干次如果分数超过AVPROBE_SCORE_MAX100分就直接认定是这个格式不再继续探测。有些文件打开慢就是这个探测-重试的过程在反复读取数据。特别是某些流式协议如果封装格式不清晰、文件头不标准探测阶段可能要反复拉取数据导致延迟。这里就解释了为什么显式指定AVInputFormat可以加速打开——直接跳过整个探测阶段。通过probesize选项可以控制探测阶段读取的最大数据量默认值是500KB这个数值因版本略有差异老版本是5MB新版本是500KB。如果文件头部信息太靠后probesize太小会导致探测失败。比如有些MP4文件moov box位于文件末尾如果probesize不够大read_header读取完头部后流信息可能不完整但至少文件能打开。2.3 read_header具体做什么当封装格式确定后fmt-read_header()被调用这是解析封装格式的核心函数。不同类型的封装格式read_header做的事情完全不同MP4/MOV解析ftyp、moov等box结构读取trak、mdia、stbl等关键box填充视频和音频的流信息包括编码格式、分辨率、采样率、时长等FLV解析FLV文件头读取第一个PreviousTagSize0和后续的ScriptData包含onMetaData通过metadata获取分辨率、帧率等信息TS解析PAT/PMT表根据PMT里注册的stream type建立视频和音频流RTSP通过DESCRIBE请求获取SDP从SDP中解析出音视频流。read_header执行完之后AVFormatContext里的streams数组、nb_streams字段就已经被填充了每个AVStream里也已经有codecpar记录编码参数。到这一步avformat_open_input的主要使命就完成了。有个细节想在这里强调——avformat_open_input只负责读取头部和建立流的基础信息它不会读取音视频帧数据。真正读取媒体数据包是靠av_read_frame()来做的。很多人混淆了“打开输入”和“读取数据”这两个阶段导致调试时定位问题找错方向。3. 从打开到读取完整实战代码光讲理论不够这里给出一个完整、可编译的示例从打开输入到读取第一个音视频包带详细注释和错误处理。3.1 带错误处理的打开输入#include stdio.h #include libavformat/avformat.h #include libavutil/avutil.h int open_media_file(const char *url, AVFormatContext **fmt_ctx) { int ret; AVDictionary *opts NULL; // 设置网络流超时时间为5秒 av_dict_set(opts, rw_timeout, 5000000, 0); // RTSP强制走TCP避免UDP穿透问题 av_dict_set(opts, rtsp_transport, tcp, 0); ret avformat_open_input(fmt_ctx, url, NULL, opts); if (ret 0) { char errbuf[AV_ERROR_MAX_STRING_SIZE] {0}; av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); fprintf(stderr, Could not open input %s: %s\n, url, errbuf); goto cleanup; } // 调用完avformat_open_input之后opts中剩余的键就是未被消费的 // 在debug时可以打印出来确认每个选项是否生效 if (av_dict_count(opts) 0) { AVDictionaryEntry *e NULL; while ((e av_dict_get(opts, , e, AV_DICT_IGNORE_SUFFIX))) { fprintf(stderr, Unconsumed option: %s%s\n, e-key, e-value); } } cleanup: av_dict_free(opts); return ret; }这个函数里有几个细节值得注意。rw_timeout选项我平时几乎一定会设置尤其是打开网络流时不设置的话默认行为可能会导致长时间阻塞甚至在某些网络异常情况下看起来像卡死了一样。rtsp_transporttcp这个选项同样是针对RTSP场景的如果确认只用本地文件这个选项就不会被消费会被留在字典里但不会引发错误。3.2 为什么用avformat_find_stream_info继续探测avformat_open_input成功之后AVFormatContext里已经有了流的初步信息但如果你打印codecpar-width或codecpar-sample_rate有时候会发现它们是0或者明显不对。例如MP4文件moov box里记录的宽高一般是准的但某些流比如部分TS流得继续读取一些帧数据才能确定真正的参数。这个时候就需要调用avformat_find_stream_info()做进一步探测。ret avformat_find_stream_info(fmt_ctx, NULL); if (ret 0) { fprintf(stderr, Could not find stream information: %d\n, ret); return ret; }这个函数会实际读取一些音视频包解码部分帧或者只解析头从而得到更准确的编码参数、帧率、宽高、时长等信息。它会根据analyzeduration和probesize这两个参数决定最多读取多少数据、分析多长时间默认是5秒。avformat_find_stream_info()是独立于avformat_open_input()的步骤但几乎每次都要用。这里有个权衡分析的时长越长获取的参数越准确但打开耗时也会增加。视频播放器启动慢很大一部分时间就消耗在这个函数上。如果对启动速度有严格要求的场景比如直播预览可以调小analyzeduration但不建议直接跳过这个函数否则后续开解码器时拿到的参数可能不完整。另外有人问既然avformat_open_input已经知道封装格式了为什么不直接在里面做流信息探测这是FFmpeg架构层面的设计决策分开两个函数给开发者更大的灵活度——你可以选择不探测、浅探测或深探测完全按业务需要来。比如做流媒体转封装时有时只需要avformat_open_input拿到封装格式和流列表就直接输出转封装结果根本不需要avformat_find_stream_info省掉大量读帧时间。3.3 读取媒体数据包的正确姿势打开文件并完成流探测后就到了读取数据的阶段用av_read_frame()循环读取AVPacket *pkt av_packet_alloc(); if (!pkt) { return AVERROR(ENOMEM); } while (1) { ret av_read_frame(fmt_ctx, pkt); if (ret AVERROR_EOF) { // 文件正常读取完毕 break; } else if (ret 0) { char errbuf[AV_ERROR_MAX_STRING_SIZE] {0}; av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); fprintf(stderr, Error reading packet: %s\n, errbuf); break; } // 此时pkt-stream_index表示这个包属于哪个流 if (pkt-stream_index video_stream_index) { // 处理视频包 } else if (pkt-stream_index audio_stream_index) { // 处理音频包 } // 每次用完要unref释放引用否则内存会一直涨 av_packet_unref(pkt); } av_packet_free(pkt);有几个习惯用法这里必须说明。av_packet_alloc()分配出来的packet每次av_read_frame()之前要保证packet是空的。读取成功之后packet内部会引用一块数据缓冲区处理完必须调用av_packet_unref()否则下一次调用av_read_frame()会覆盖packet里的指针造成内存泄漏或重复释放。这个坑我在网上看到过无数次求助帖绝大多数就是忘了unref。AVERROR_EOF表示读到了文件末尾这是正常结束标志。其他负值才是真正的错误。这点在循环退出条件里要区分清楚。3.4 主函数中完整的调用顺序把上面的部件组装起来一个完整的小程序大致是这样的int main(int argc, char *argv[]) { AVFormatContext *fmt_ctx NULL; int ret; if (argc 2) { fprintf(stderr, Usage: %s input\n, argv[0]); return 1; } ret open_media_file(argv[1], fmt_ctx); if (ret 0) { return 1; } ret avformat_find_stream_info(fmt_ctx, NULL); if (ret 0) { fprintf(stderr, Could not find stream info\n); avformat_close_input(fmt_ctx); return 1; } // 打印一下基本流信息确认打开成功 for (int i 0; i fmt_ctx-nb_streams; i) { AVStream *st fmt_ctx-streams[i]; AVCodecParameters *par st-codecpar; if (par-codec_type AVMEDIA_TYPE_VIDEO) { fprintf(stderr, Stream #%d: video, %dx%d, codec%s\n, i, par-width, par-height, avcodec_get_name(par-codec_id)); } else if (par-codec_type AVMEDIA_TYPE_AUDIO) { fprintf(stderr, Stream #%d: audio, sample_rate%d, channels%d\n, i, par-sample_rate, par-ch_layout.nb_channels); } } // 到这里已经成功打开了输入可以进一步读取数据或做别的处理 avformat_close_input(fmt_ctx); return 0; }编译时记得链接FFmpeg库如果是用pkg-config管理的话命令大致是gcc -o open_demo open_demo.c $(pkg-config --cflags --libs libavformat libavutil)执行时传入一个媒体文件路径就能在终端看到输出的流信息。这套代码是一个标准的“打开输入-获取流信息-关闭”的骨架后续不管是做解码、转码、转封装、播放还是录制都从这段代码的基础上展开。4. 常见错误与排查思路实录avformat_open_input会遇到的错误其实不算多但每种错误背后对应的原因和排查方向差异很大。我把这些年遇过的高频问题整理成一个速查表再挑几个典型场景展开说明。4.1 错误码速查表错误码数值范围常见触发场景排查方向AVERROR(ENOENT)-2文件不存在、路径错误、目录权限不足检查路径拼写、权限、磁盘挂载AVERROR(EINVAL)-22参数非法、URL解析失败、自定协议名不对检查URL格式、是否传了非法参数AVERROR_INVALIDDATA对应值格式探测失败、文件损坏、不支持格式确认文件是否完整、格式是否被FFmpeg编译包含AVERROR_PROTOCOL_NOT_FOUND对应值协议不支持检查FFmpeg编译时是否包含该协议AVERROR_STREAM_NOT_FOUND对应值打开成功但找不到流一般是后续find_stream_info阶段的问题AVERROR_EOF-541478725读取过程中提前EOF网络中断、文件不完整、服务器异常其中的错误码数值我不能给你确切数字因为不同版本的FFmpeg中AVERROR宏定义会有调整。最关键的是要会用av_strerror()把错误码转成人类可读的字符串否则拿个负数在那猜效率太低。4.2 经典问题为什么打开RTSP总是卡顿或超时RTSP流是avformat_open_input用得最多的网络场景之一也是问题最多的场景。先说卡顿的原因。默认情况下RTSP的底层传输走的是UDPUDP在弱网环境下丢包严重FFmpeg内部的重传机制会反复等待丢失的RTP包表现出来就是打开慢、出画面慢、画面花屏。走TCP之后底层有TCP的重传保证虽然延迟稍高但稳定性好很多。所以我给RTSP流的建议是无脑加rtsp_transporttcp。再说超时。RTSP握手涉及多个RTSP请求OPTIONS、DESCRIBE、SETUP、PLAY任何一步服务器不响应都会让默认的阻塞行为拖很久。通过rw_timeout可以给IO层统一设置超时但要注意这个超时覆盖的是每次IO操作不是整个握手流程。要是握手过程中有多个请求都接近超时总耗时可能超过你设定的值好几倍。如果要更精细地控制总超时时间就得用avformat_open_input配合自定义中断回调AVIOInterruptCB这个我在后面会提到。有些服务器发来的SDP里包含的音视频编码格式你本地的FFmpeg不支持。这个时候avformat_open_input本身很可能还是成功的因为SDP的解析不要求所有编码都能解码但到后续avcodec_open2打开解码器的时候就会失败。排查思路是要区分“打开输入失败”和“后续解码失败”两者不是一回事。4.3 文件打开失败却不报错检查这五个地方有一种很让人头疼的情况avformat_open_input返回了错误但你觉得文件明明没问题。这时按下面顺序排查命中率很高路径里有没有特殊字符。空格、中文、引号、反斜杠转义。Windows路径要特别小心反斜杠和盘符文件权限是否足够。Linux下尤其注意当前用户对文件所在目录有没有读取权限FFmpeg编译的时候有没有把对应格式的demuxer编进去。可以执行ffmpeg -formats看列表里有没有对应格式url是否被误加上了协议前缀。比如/home/user/video.mp4在某些场景下会被解析成协议/home需要在前面补上file://文件是否正在被另一个进程占用或者文件是否正在被写入还没写完。片源录制过程中直接去读经常拿到的是不完整的文件头。第五种情况我遇到过太多次。录播文件还在写入的时候就去打开MP4文件头moov box还没最终封口avformat_open_input调用read_header后解析不到完整的trak信息直接返回错误。这种情况要等录制进程优雅关闭文件之后才能安全读取或者使用一些针对边写边读的特殊封装模式比如fmp4。4.4 自定义IO回调与超时中断前面提到中断回调。对于网络流或者一些特殊数据源标准的IO层不能满足需求时可以自己实现AVIOContext。手动创建AVIOContext的流程大概是这样static int read_packet(void *opaque, uint8_t *buf, int buf_size) { // 自定义数据读取逻辑返回实际读取字节数 // 返回0表示EOF返回负数表示错误 } AVIOContext *avio_ctx NULL; unsigned char *avio_buffer av_malloc(io_buffer_size); AVIOContext *avio_ctx avio_alloc_context( avio_buffer, io_buffer_size, 0, opaque, read_packet, NULL, NULL); fmt_ctx avformat_alloc_context(); fmt_ctx-pb avio_ctx; fmt_ctx-flags | AVFMT_FLAG_CUSTOM_IO;这样设置之后avformat_open_input在调用时不会自己创建IO而是使用你传入的AVIOContext去读取数据。这在处理内存中缓存数据、加密流、自定义协议时非常有用。不过要提醒一旦用了自定义IOurl参数就只是一个名义上的标识符协议层不会真正按照url去连接格式探测仍然会通过你的read_packet读取数据来判断封装格式。AVIOInterruptCB是另一套机制用来处理中断。通过avformat_open_input的option参数并不能直接设置中断回调因为它不是open_input层的选项而属于AVFormatContext的interrupt_callback字段。标准做法是AVFormatContext *fmt_ctx avformat_alloc_context(); fmt_ctx-interrupt_callback.callback interrupt_cb; fmt_ctx-interrupt_callback.opaque user_data;这个回调会在IO阻塞等待期间被周期调用返回非0值表示“我放弃了请立即中断”。有了这个就再也不用担心RTSP流卡死导致整个程序无法退出了。中断回调函数要注意的事项有两点回调运行在FFmpeg的内部IO线程里不能在其中做过于耗时的操作否则会拖慢IO响应回调里访问的数据结构要做好线程安全必要时用原子变量或加锁保护。4.5 打开成功但streams为空问题出在哪avformat_open_input返回了0但拿到的fmt_ctx-nb_streams为0接下来没法解码。这种场景一般出现在裸流如纯H.264的.h264文件、纯AAC的.aac文件上。裸流没有封装容器read_header读不出流信息。处理裸流时FFmpeg的做法是让demuxer创建一个“未知”的流然后由avformat_find_stream_info()通过对帧数据的分析推断编码格式。如果avformat_find_stream_info()也没能识别出来通常需要显式告诉它编码格式比如通过AVOptions指定video_size、pixel_format等参数或者设置fflagsgenpts之类的标记。对于H.264裸流还有种情况是ANX-B格式与AVCC格式的转换问题。裸流文件里存的是Annex-B格式带startcode而AVFormatContext的extradata里期望的是AVCC格式带length-prefixed。这个转换FFmpeg会自动处理大部分场景但有的边缘情况需要手动追加extradata。4.6 关于analyzeduration和probesize的调优心得最后聊聊两个重要的调优参数。默认的probesize和analyzeduration值设置得比较保守为了确保大多数文件都能正确识别。但在实际项目中这两个参数直接影响打开速度。如果你发现打开文件的耗时占比太大可以结合文件的实际情况动态调整。本地文件路径已知、格式已知的情况下建议走av_find_input_format()指定格式路线同时把analyzeduration调小到5000000.5秒甚至更低打开速度会明显提升。反过来如果遇到本地分析工具无法分析的流先把probesize调大比如5000000再试一次有时就能成功识别。这两个参数都是在调用avformat_open_input之前通过av_dict_set()设置的不过analyzeduration要保证在avformat_find_stream_info()调用前仍然有效因为它由后者消费。5. 实际项目中的经验与常见误区代码和原理都讲完这一节集中放一些我在实际项目中总结的经验细节比较零散但每条都是踩过坑之后才知道的。AVFMT_FLAG_NOBUFFER这个flag在直播低延迟场景下很常用。通过fmt_ctx-flags | AVFMT_FLAG_NOBUFFER;设置后读取出来的数据包不会被额外缓冲减少整体延迟。但代价是可能会增加IO次数。低延迟和稳定性之间需要根据业务取舍。avformat_open_input打开的输入并不会自动seek到0位置。如果你处理完某个流之后想重新读一遍比如做两次分析需要调用av_seek_frame()或者重新open。重新open的成本并不算高所以很多开源的播放器在切换清晰度时选择销毁旧上下文再重新打开简单粗暴且可靠。关于AVFormatContext的复用问题一个AVFormatContext在成功open之后你不能拿同一个指针再次调用avformat_open_input打开另一个文件必须先avformat_close_input()再重新分配。我见过有人试图用同一个上下文循环打开多个文件结果是各种诡异的内存错误这个问题在FFmpeg的文档里有明确说明但踩坑的人依然很多。关于avformat_alloc_context()配合avformat_open_input()使用有个细节是如果你自己alloc了context但又想通过avformat_open_input重新分配一个新context也就是传入*ps指向另外一个非NULL指针行为不太直观。最稳妥的方式是始终让*ps初始为NULL交给函数自己去处理。这是FFmpeg官方示例的推荐模式。多线程环境下使用avformat_open_input每个线程必须拥有自己独立的AVFormatContext不能共享。不同线程同时对同一个AVFormatContext调用读取接口FFmpeg的默认编译是不保证线程安全的。如果确实需要多线程读取同一个输入一般做法是外层加锁或者用av_read_frame返回后再分发处理。说完这些如果你正准备用avformat_open_input做开发我最后给一个实际操作上的建议在你项目里封装一层“打开输入”的包装函数统一处理选项字典、错误日志、超时中断、关闭释放。这样一来后续再遇到协议切换、格式扩展或者特殊服务器适配时只需要改包装函数里的字典配置不用动业务代码。这个习惯帮我在多个项目里省了大量时间希望对你也有用。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →