1. 从一次“翻车”说起:直接把MP4喂给模型,为什么不行?
我应该从一次真实的踩坑经历讲起。我刚接触AI视觉项目那会儿,接的第一个活儿就是做一个“视频中的宠物检测”Demo,需要从监控视频里识别狗和猫。当时拿到一批MP4文件,第一反应就是:直接写个Python脚本,把MP4路径扔给YOLO模型,让它给我跑出检测框来。结果代码一跑,直接报了一堆看不懂的错,什么cv2.error、format not supported,然后程序就挂在那里,屏幕上一片红色。后来我才明白,我犯了一个很基础的错误——把“视频文件”和“图像数据”当成了一回事。
先说结论:AI检测程序本质上处理的是图像,不是视频。摄像头、硬盘里的MP4文件、网上下载的FLV、甚至是直播流,这些对模型来说都只是“数据来源”,而不是“可直接输入的内容”。绝大多数视觉模型(不管是YOLO、SSD还是更复杂的Transformer检测器),输入都要求是“一帧一帧的图像张量”,通常是H x W x C的数组,比如640 x 640 x 3,这个3就是RGB三个通道。
那MP4是什么?MP4是一种封装格式,它里面装的是经过压缩编码的码流,比如H.264、H.265。它和模型要的“RGB数组”之间,隔着一道巨大的鸿沟。你要做的,不是写个代码把MP4路径传给模型,而是要把MP4拆开、解压、还原成一帧帧裸图像,再做尺寸变换、颜色空间转换、归一化,最后才会变成一个模型能“看”的四维张量N x H x W x C。
这篇文章想聊的,就是这个完整的处理链路:从MP4文件到视觉算法输入,中间到底发生了什么,每一步为什么要做,常见的坑在哪里。适合刚开始做视频AI分析、想搞懂ffmpeg和OpenCV到底在干嘛、或者被“为什么海康的MP4播放不了”这类问题折磨过的朋友参考。搞懂了这条链路,你以后遇到什么mp4转m4s、m4s合成mp4、m3u8转mp4的问题,也都不会再觉得玄学了。
2. 容器不是数据:MP4的真实身份与视觉模型的标准输入
2.1 MP4是“箱子”,编码才是“货”
很多人分不清MP4和H.264的区别,这恰恰是整条链路里最核心的概念。MP4是一个容器格式,你可以把它理解成一个快递箱子:箱子外面写着“这是MP4”,打开之后里面装的是经过压缩的视频轨(比如H.264码流)和音频轨(比如AAC码流)。箱子本身不关心里面货物的具体形态,它只负责把视频轨和音频轨组合、打上时间戳、提供索引,让播放器知道第几秒该把哪些数据块丢给解码器。
而H.264是视频编码标准,它决定的是“货物”到底是怎么压缩的:怎么把连续帧之间的冗余信息去掉,怎么用运动估计和补偿来减少数据量,怎么用熵编码把语法元素压缩成比特流。同一个MP4文件,视频轨可能是H.264,可能是H.265/HEVC,可能是AV1,甚至可能是古老的MPEG-4 Part 2。播放器能不能放,取决于它有没有对应的解码器;而AI检测程序能不能直接处理,取决于它有没有把压缩码流解码成像素数据的能力。
这就是为什么你经常能看到“h.264和mp4的区别”这样的热搜词——因为太多人在网上搜这个问题了。简单粗暴地说:MP4是外壳,H.264是内核。一个外壳里面可以换不同的内核,同一个内核也可以装进不同的外壳(比如MP4、MKV、TS都是常见容器,里面都能装H.264码流)。AI检测程序要的是内核解压后的“原始像素”,不是外壳,更不是还压缩着的内核。
2.2 视觉模型的输入其实是“帧”而不是“视频”
再往深处看一步。无论你把什么视频喂给一个检测模型,模型本身根本没有“视频”的概念。它的卷积核是在感受野内滑动,它的注意力机制是在特征图内做矩阵运算,它的检测头是在“某一帧图像”上输出框、类别、置信度。所以,视觉算法的标准输入格式,几乎永远是以下两种之一:
第一种,单帧图像。形状为H x W x C的数组,H是高度,W是宽度,C是通道数(通常3,对应RGB)。数组元素类型一般是uint8(0~255),经过归一化后变成float32(0.0~1.0或-1.0~1.0)。
第二种,批量帧序列。形状为N x H x W x C的四维张量,N是批大小。对于视频理解类的任务,比如动作识别、时序检测,可能还会多一个时间维度,变成N x T x H x W x C或N x C x T x H x W。
无论哪种,它们的共同点是:数据已经是“解开压缩”的像素矩阵。而MP4里面的H.264码流不是这样的。H.264的码流里,绝大多数帧(P帧、B帧)根本不是完整图像,而是“参考帧的残差+运动矢量”。打个比方,H.264视频不是把每一张照片都完整存下来,而是存了一张底图,然后记录后面每张照片“和上一张相比哪里动了、差了多少”。你直接把这种“差值记录”给模型,模型当然看不懂。
2.3 程序报错之后,我到底该干什么?
当我们说“AI检测程序不能直接处理MP4”时,技术层面有两道明显的坎:
第一道坎,解码能力缺失。就算你装了OpenCV,它内置的ffmpeg后端如果没有编译进对应解码器(比如没有H.265解码器),你读一个HEVC编码的MP4,cv2.VideoCapture.read()会直接返回False或空帧。这不是代码写得不对,是你根本没有对应的“翻译官”。
第二道坎,语义鸿沟。就算视频能解码,解码出来的原始帧是YUV格式还是RGB格式?宽度高度是不是模型要求的尺寸?像素取值范围是否归一化过?通道顺序是不是R-G-B而不是B-G-R?这些细节全都对不上,模型推理结果就会莫名其妙,甚至完全跑不起来。
我当时第一次跑YOLO,看到报错以后第一个想法是“模型坏了”,后来才发现是视频那一侧的问题。搞明白这个以后,我开始把整个问题拆成了三段:解封装、解码、预处理,一段一段吃透,后面所有视频AI项目都顺了很多。
3. 从MP4到Tensor:完整链路里每一步都在干什么
3.1 链路全景:需要拆出来的四个环节
一个视频文件要变成能被模型推理的输入张量,至少要经过以下几个环节:读取与解封装、解码、颜色空间转换与缩放、张量化与归一化。这四步缺一不可,每一层都有对应的专业工具和参数。
先看一张“脑内地图”,我口述给你:
- MP4文件 → 用demuxer(解封装器)拆出H.264/H.265视频流(ES流)
- 视频流 → 用decoder(解码器)逐帧解出YUV像素帧
- YUV帧 → 用转换器转成RGB/RGB24,并按模型要求缩放尺寸
- RGB帧 → 转成numpy数组(
uint8,HxWx3),再做归一化变成float32 - 归一化数组 → 加batch维度,转成模型输入Tensor
这里面每一步都有操作系统的类比:解封装相当于从快递箱里取出货物,解码相当于把压缩文件解压成原始文件,颜色空间转换相当于把一种语言翻译成另一种语言,缩放相当于调整照片尺寸,归一化相当于把所有值按比例缩到一个标准范围里。每一层出问题,后面全白搭。
3.2 解封装:不要绕开时间戳和轨道信息
解封装是整个链路的第一步,目的就一个:从MP4容器的文件结构里,把视频轨数据块无损地拿取出来。
MP4内部是基于box/atom结构组织的。一个MP4文件里,通常有ftyp(文件类型)、moov(元数据)、mdat(媒体数据)这几种box。moov里面记录了每一条轨道的信息,包括编码格式、分辨率、帧率、时长,以及一个非常重要的东西——stbl(sample table),它相当于视频帧的索引表,告诉你每一帧的数据在mdat里的哪个位置、占多少字节、时间戳是多少。播放器或解码器要读取视频,必须靠这个索引去mdat里定位每一帧压缩数据。
这里有个巨坑:MP4文件里的帧在物理存储上的顺序,和播放顺序是不一样的。因为编码器为了提高压缩率,会重排帧的存储顺序,比如存的是I P B B P,播放顺序却是I B B P P。索引表里的stts、ctts就是用来换算时间戳的。如果解封装环节没有正确处理时间戳,后面按顺序取帧解码,就会看到画面时序错乱、人物动作怪异的视频。
所以,在做视频AI处理时,一定要选一个成熟的解封装实现,不要自己写解析器。OpenCV内部调用ffmpeg来解封装,PyAV直接封装了ffmpeg的libavformat,这些都是久经考验的。自己写解封装,你可能花两周时间,最后发现漏处理了某个box,解码出来的画面全是花屏。其实记住一句话就行:解封装交给ffmpeg全家桶,别自己造轮子。
3.3 解码:压缩域到像素域的唯一桥梁
解封装只是把压缩码流“取出来”,这时候数据依然不能看。接下来需要解码器。
解码的过程,本质上是做压缩的逆过程。以H.264为例,解码器会做的事大概有:解析码流里的NALU(网络抽象层单元)、重建残差数据(用反变换和反量化)、预测像素(帧内预测或帧间运动补偿)、最后加上去块滤波和样本自适应补偿(SAO,如果是HEVC),得到一帧完整的图像。
这里有一个对AI工程师很重要、但很多人不知道的点:解码器输出的像素格式,往往不是RGB,而是YUV。绝大多数视频编码标准在压缩时,为了充分利用人眼对亮度比色度更敏感的特性,会采用YUV 4:2:0采样——每四个亮度像素对应一组共享的色度像素。解码器解出来的是YUV420p,或者更高端的NV12、P010等格式。而视觉模型的输入几乎都是RGB,所以你在喂给模型之前,必须做一次颜色空间转换。
如果说这一步有什么实际操作建议,那就是:能用硬件解码就用硬件解码。解码这一步的计算量不小,尤其是高分辨率高帧率的视频。CPU软解4K H.264,一帧可能就要花几十毫秒,整个项目的推理吞吐量直接被拉垮。NVIDIA显卡上,用NVDEC硬件解码,同样一帧只需几毫秒,甚至下探到1毫秒以内。OpenCV的cv2.VideoCapture默认走ffmpeg软解,性能瓶颈严重;但如果用cv2.VideoCapture配合cv2.CAP_FFMPEG并指定硬件解码器(某些自定义构建支持),或者直接用PyAV并配置GPU解码,帧率可以提升好几倍以上。我实际测过,同样的1080p视频,CPU软解+模型推理时整体大概是12~15 FPS,切到NVDEC硬解后直接跑到45 FPS以上,体验完全不一样。
3.4 预处理:尺寸、通道顺序、归一化,一个都不能错
解码拿到YUV像素帧以后,你还需要做一系列“微调”,才能让数据符合模型的要求。这些操作看似琐碎,但每一样都能让模型输出从“正常”变成“离谱”。
第一步,颜色空间转换。将YUV转换为RGB。OpenCV里有个cv2.cvtColor(frame, cv2.COLOR_YUV2RGB_I420)之类的函数,PyAV也有对应的转换操作。这里最容易踩的坑是BGR/RGB顺序:OpenCV的默认通道顺序是BGR,很多深度学习框架读图时默认是RGB,如果你用OpenCV读帧后再直接塞给某个RGB模型,颜色通道就会错乱。你看到画面上猫的毛色变成蓝绿色调的,那大概率就是通道顺序弄反了。
第二步,缩放。模型输入一般是正方形,比如YOLO家族是640x640、416x416,分类模型可能要求224x224、256x256。视频帧则是16:9或4:3等宽高比,直接拉伸会破坏物体比例影响检测精度。正确的做法是:按比例缩放,然后把多余部分用固定值(比如灰色128)填充到正方形里,或者直接等比缩放后居中裁剪。这一步虽然不复杂,但如果不处理,检测框的位置坐标就会飘,置信度也会普遍偏低。
第三步,归一化与张量化。模型通常输入float32数组,取值在0~1之间,或者-1~1之间(取决于模型训练时的设定)。你需要把uint8的0~255像素值除以255再减均值除方差,或者简单地除以255做归一化。然后转成模型要求的Tensor格式,注意你的框架是NCHW(PyTorch)还是NHWC(TensorFlow),别把维度顺序搞反。
我做宠物检测模型的时候就踩过这个坑。模型训练时用的图片是已经缩放到640x640的RGB数据,结果推理管线里忘了把视频帧等比缩放直接拉伸,导致日常检测狗的时候框位置偏上,猫的时候框位置偏下,调了半天才发现是宽高比没保住。那一次之后我学乖了,任何预处理环节都写单元测试,用一张标准图片验证颜色空间、尺度、归一化是否正确,避免模型“玄学输出”。
4. 实操方案:三种从MP4到模型输入的落地方案
4.1 方案一:OpenCV读帧 + NumPy预处理(适合快速验证)
如果你只是想快速验证一个想法,比如“我手上有一段视频,里面有没有狗”,OpenCV是最快捷的路线。
import cv2 import numpy as np cap = cv2.VideoCapture("input.mp4") if not cap.isOpened(): print("视频打开失败,请检查文件路径或解码器") exit() # 模型输入尺寸假设为640x640 INPUT_SIZE = 640 while True: ret, frame = cap.read() if not ret: break # frame是BGR格式的uint8数组,形状为HxWx3 h, w = frame.shape[:2] # 按比例缩放并letterbox(填充灰边) scale = min(INPUT_SIZE / w, INPUT_SIZE / h) new_w, new_h = int(w * scale), int(h * scale) resized = cv2.resize(frame, (new_w, new_h), interpolation=cv2.INTER_LINEAR) canvas = np.full((INPUT_SIZE, INPUT_SIZE, 3), 114, dtype=np.uint8) offset_x, offset_y = (INPUT_SIZE - new_w) // 2, (INPUT_SIZE - new_h) // 2 canvas[offset_y:offset_y + new_h, offset_x:offset_x + new_w] = resized # BGR转RGB并归一化 rgb = cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB) input_tensor = rgb.astype(np.float32) / 255.0 # 这里省略送入模型推理的代码 # 注意:后续的坐标映射要考虑offset_x, offset_y和scale cap.release()这段代码看着很简单,但有几个细节很关键。第一,cap.read()读到的是BGR,不是RGB,转换顺序不能错。第二,letterbox填充的灰度值是114,这个值是从YOLO系列源码里沿用的习惯,用0会影响归一化后的数值分布(因为黑色背景在某些模型里会产生伪影)。第三,推理出来的检测框坐标是相对于640x640画布的,要还原到原始视频帧上,必须做一次逆变换,也就是减去offset_x、offset_y后除以scale。这一步漏掉了,框就永远画不对位置。
OpenCV方案的优点是代码量少、上手快,适合快速Demo。缺点也很明显:性能平庸,CPU软解很难跑满高帧率视频;错误信息不友好,遇到编码器不支持的视频就是打印一堆warning然后返回空帧。所以我一般建议:OpenCV只用来快速跑通流程,不要用到生产环境。
4.2 方案二:PyAV硬解 +像素转换(适合性能敏感场景)
如果你要做实时视频AI分析,或者处理4K、高帧率素材,绕不开硬件解码。PyAV是ffmpeg的Python绑定,能直接操作解码器、过滤器,用起来比OpenCV更底层,但性能上限也更高。
import av import numpy as np container = av.open("input.mp4") stream = container.streams.video[0] # 启用硬件解码(以NVIDIA NVDEC为例,需要ffmpeg带cuvid/nvdec支持) stream.codec_context.ffmpeg_hw_device_type = "cuda" # 解码后的帧会转成yuv420p格式 for frame in container.decode(stream): # PyAV的frame.to_ndarray()可以直接转RGB数组 img = frame.to_ndarray(format="rgb24") # 形状HxWx3, dtype=uint8 # 后续预处理同方案一...PyAV的好处在于:你能直接指定硬件解码器,并且能够利用解码器输出的帧信息做更多精细控制,比如拿到精确的时间戳、帧号,做视频抽帧和事件对齐。这对监控视频分析特别重要,比如你想知道“猫在视频的第几秒出现”,就不能只用OpenCV盲数帧数,因为视频帧率不恒定(VFR),没有精确时间戳根本没法对齐。
坑点提示:frame.to_ndarray(format="rgb24")默认会做颜色空间转换,但要看PyAV编译时是否带了对应解码器。如果你在Windows上用pip装PyAV,大概率只带了软解,GPU加速还得自己编译或找预编译包,这一块操作成本不小,不过值。
4.3 方案三:ffmpeg命令行 +管道(适合快速抽帧和调试)
还有一条很“作弊”的路:完全不写Python代码,直接用ffmpeg命令行把视频抽成帧图片或者裸流,再交给Python处理。
# 每0.5秒抽一帧,输出成jpg图片 ffmpeg -i input.mp4 -vf fps=2 -q:v 2 frames/frame_%04d.jpg # 输出成裸的RGB数据,通过管道直接送给Python程序 ffmpeg -i input.mp4 -f rawvideo -pix_fmt rgb24 pipe:1 | python process.pypipe:1这种用法很实用,Python端只要从stdin读字节流,按宽高乘三每帧切分就能还原出图像数组。这种方案特别适合那种“不想动环境,只想做一次性处理”的场景。缺点是你得手动管理子进程的生命周期,有点脏,但关键时刻能救命。
调试的时候我经常先用ffmpeg看视频的元信息:ffprobe input.mp4,看看编码格式、像素格式、分辨率、帧率。这一步基本能判断80%的“为什么程序跑不起来”问题。如果ffprobe都读不出编码信息,那说明文件本身有问题,而不是你的代码有问题。
5. 实际场景中的高频问题:解码失败与格式转换类的坑
5.1 为什么海康的MP4播放不了?
网上搜“为什么海康的MP4播放不了”,能搜出一堆帖子。这类问题的根源往往是:海康设备导出的视频虽然文件后缀是MP4(或DAV/MP4),但内部封装方式或编码格式不符合通用播放器的标准。
海康威视的NVR/摄像机常用H.264或H.265编码,但部分导出文件的封装里会带有私有扩展信息(比如专有的加密或元数据标记,还有一些老固件导出的是PS流封装却命名为MP4,或者把音视频轨放在不标准的位置),普通的播放器和AI程序不认这种“非标准MP4”。FFmpeg有时能猜出封装,但有时也猜不中,需要手动指定格式。
处理这类文件的建议是:先用ffprobe看真实格式,如果是PS流,改成.mpg或.ts后缀;如果带着私有扩展,先用ffmpeg -i input.mp4 -c copy output.mp4做一次“重封装”,只换包装不动编码,很多时候就能解决。如果还不行,就做成全量转码:ffmpeg -i input.mp4 -c:v libx264 -c:a aac output.mp4。虽然转大文件耗时,但兼容性最好,喂给AI检测程序也最省心。
5.2 m3u8/m4s转MP4之后,为什么AI检测还是报错?
你从网上下了个m3u8直播流,或者某个视频平台缓存下来的m4s文件,觉得很麻烦地转成了MP4,结果AI程序还是报错。这种情况也不少见,常见的“坑”有两个。
第一,m3u8装的是TS(MPEG-TS)格式的视频,转MP4时如果只改了后缀名(比如直接把.ts改成.mp4),那只是“换了个标签”,文件内部还是TS流结构,解码器会认错。正确的转换要用ffmpeg重新封装,例如:
ffmpeg -i input.ts -c copy -f mp4 output.mp4用-c copy是因为TS和MP4里的H.264码流格式是兼容的,不需要重编码,拷贝就行。但注意,TS里的H.264可能有未被MP4允许的NALU类型,或者有多个节目流,这种情况下ffmpeg会报错,你需要手动加参数过滤或者转码。
第二,m4s文件通常是从DASH流缓存下来的,可能是单个视频分片,也可能包含初始化段(init.m4s)和数据段(xxxx.m4s)两部分。你光把数据段改成MP4后缀,MP4没有moovbox索引,谁来都解不出。要用ffmpeg把init段和data段拼接后重新封装,例如:
ffmpeg -i init.m4s -i video.m4s -c copy -f mp4 output.mp4如果处理时你们看到“moov atom not found”或“Invalid data found when processing input”这类报错,基本就是这个原因:文件缺少头部索引信息,或者封装格式根本不是MP4。我常用的一招是用十六进制编辑器看文件头几个字节:MP4文件通常以ftyp开头,TS文件以0x47(G)开头,FLV以FLV开头。看一眼文件头,你就能判断是哪种容器,少走很多弯路。
5.3 嵌入式设备上的猫狗检测模型,该怎么处理视频帧?
现在边缘端AI很火,经常有人问:我要在树莓派、Jetson或者某个国产开发板上跑宠物检测,设备上收到的也是MP4视频流,是不是也一样要解封装解码?
是的,链路完全一样,只是“解码战士”变了。嵌入式设备上资源紧张,软解MP4常常直接卡成PPT。我的建议是:不要碰整段MP4解码,直接抽帧。如果是网络摄像头,直接拉RTSP流,用ffmpeg或GStreamer硬解;如果是本地MP4,就在PC端预处理成帧序列或直接转换编码格式,再拷到设备上做推理。有些开发板上带硬件NPU,但视频解码能力极弱,这时候强行让它解码MP4,你会看到CPU占用100%,推理帧率却只有个位数。
我最近在Jetson Nano上做过一个类似项目,做法是在PC端用ffmpeg把MP4里的关键帧(I帧)每隔几秒抽一张图,再把抽好的图批量喂给设备上的模型。因为宠物检测对时序不敏感——猫狗不是短暂闪现的目标——抽帧就够了,没必要追求每帧都检测,这样既省算力又有足够的检测频率。
6. 工程上的几条经验与建议
6.1 先问自己:视频处理链路,哪个环节是瓶颈?
在部署任何视频AI程序之前,先做一个简单的“瓶颈分析”。写一段读视频的代码,分别统计三个阶段的耗时:解封装+解码耗时、预处理耗时、模型推理耗时。你会发现自己以为的瓶颈和实际瓶颈可能完全不一样。
我排过很多次这类问题,一个常见的现象是:模型推理优化得再快,结果解码环节占了总端到端延时的70%以上,导致整个系统帧率上不去。这种情况下你该优化的是硬件解码或者抽帧策略,而不是花大力气去剪枝模型。学会用数据说话,别凭感觉优化。
6.2 关于帧的“时间维度”,很多AI新手会忽略
视频处理还有一个容易忽略的问题:视频帧率不一定恒定。VFR(可变帧率)视频在现代监控设备和手机录制中非常常见,比如你手机拍的慢动作片段,不同段落帧率不同。如果你写死“每隔10帧取一帧”,结果就是有些片段抽多了、有些抽少了,时间上分布不均。
正确做法是基于时间戳抽帧。用PyAV拿解码帧的frame.pts配合time_base计算时间,或者用ffmpeg的fps过滤器来均匀抽帧。处理监控视频的时候,我甚至会先把所有帧的输出时间戳记录下来,这样后面做事件检索、告警关联,都能直接对到秒级时间线。
6.3 最后的建议:把视频处理封装成独立服务,别和模型塞在一起
多项目以后,我养成了一个习惯——把“视频文件到模型输入张量”的转换,单独封装成一个服务(或者至少是一个独立的模块),对外提供统一的接口。比方说一个函数:load_frames(video_path, start_time=0, end_time=None, target_size=(640,640))-> Iterator[Dict]。这个函数内部管好解封装、硬解、颜色空间转换、letterbox、时间戳记录,调用方只需要拿帧、跑模型、拿结果。这样当你从MP4切换到RTSP流,从软解切换到硬解,从1280x720换到4K,只改这个内部模块就行,模型部分完全不受影响。
我不止一次看到同事在模型代码里直接写cap = cv2.VideoCapture(...),然后后面一堆代码都耦合了OpenCV的BGR格式,后面想换PyAV硬解,改得痛不欲生。架构上多花半小时做隔离,能省后面几天的调试时间,这笔账怎么算都划算。
7. 从“为什么不能处理”到“我把链路跑通了”
回到最开始的那个问题——为什么AI检测程序不能直接处理MP4?答案现在已经很清晰了:MP4是封装格式,不是像素数据格式;视觉模型的输入必须是一帧帧解码、预处理后的RGB张量。两者之间横着解封装、解码、颜色空间转换、缩放、归一化这一整套链路,不是简单改个接口就能跳过去的。
我的建议是,如果你第一次做视频AI项目,先别急着写模型推理代码。先亲手把一段MP4经过ffmpeg/PyAV解出帧、转成numpy数组、打印出形状和数据范围,再把它塞进一个最简单的分类模型里跑通。这一步跑通了,后面所有视频检测、识别、结构化分析的项目,你都具备了一个可靠的地基。搞明白这条链路之后,你再看到“为什么海康的MP4播放不了”“为什么m3u8转的MP4打不开”“为什么mp4转m4s之后视频花屏”这类问题,心里就有底了——绝大多数都是容器、编码、索引、解码器不对位造成的,你会很清楚该往哪个方向排查。
我后来在做一个工地安全帽检测项目时,又遇到了类似的事情。现场给的是H.265编码的高清MP4,第一版代码直接挂掉,当时我已经不再慌了。先用ffprobe看了一下编码信息,确认是HEVC + MP4容器,然后换用PyAV硬解,预处理里把letterbox参数填对,端到端测试一次通过。所以我想说,不要怕出问题,把这条链路吃透,你就能自己搞定它。