DeckLinkAPI采集后FFmpeg录制视频帧错位问题求助
你遇到的画面错位问题,核心是FFmpeg接收的帧数据格式/时序与参数预期不匹配,但因为单帧保存为图片正常,说明原始帧数据本身没问题,问题出在数据传递给FFmpeg的环节。以下是具体排查方向和解决方法:
1. DeckLink帧的行字节对齐填充导致数据冗余
DeckLink采集的视频帧会做内存对齐优化,比如1920x1080 BGRA格式,理论每行字节数是1920*4=7680,但实际通过videoFrame.GetRowBytes()获取的数值可能因硬件对齐规则(比如16/32字节对齐)更大。如果直接把带填充字节的整帧数据传给FFmpeg,FFmpeg会按1920*4的行字节解析,导致每行末尾的填充字节被当成下一行的开头像素,累积后出现画面左移、右侧内容错位到左侧的现象。
解决方法:
在ConvertVideoFrameToByteArray中逐行拷贝有效像素数据,跳过每行末尾的填充字节:
byte[] ConvertVideoFrameToByteArray(IDeckLinkVideoInputFrame videoFrame) { int width = videoFrame.GetWidth(); int height = videoFrame.GetHeight(); int rowBytes = videoFrame.GetRowBytes(); int validRowBytes = width * 4; // BGRA每个像素占4字节 byte[] frameData = new byte[validRowBytes * height]; IntPtr bufferPtr = videoFrame.GetBytes(); for (int y = 0; y < height; y++) { // 仅拷贝当前行的有效像素部分,跳过对齐填充字节 Marshal.Copy(new IntPtr(bufferPtr.ToInt64() + y * rowBytes), frameData, y * validRowBytes, validRowBytes); } return frameData; }
2. 帧率参数与实际采集帧率不匹配
你在FFmpeg参数里硬写了-r 30,但DeckLink实际采集的帧率可能是29.97fps、59.94fps等非整数帧率,当FFmpeg的帧率预期与实际输入帧速率不一致时,会导致帧时序错乱,表现为画面错位或跳帧。
解决方法:
从DeckLink的采集模式中获取实际帧率,动态生成FFmpeg参数:
// 假设已获取当前采集的displayMode对象 IDeckLinkDisplayMode displayMode = ...; double actualFrameRate = displayMode.GetFrameRate(); string args = $"\\.\\ffmpeg.exe -y -f rawvideo -pix_fmt bgra -s 1920x1080 -r {actualFrameRate:F2} -i - -c:v libx264 -preset ultrafast -pix_fmt yuv420p -crf 23 \"{outputFilePath}\"";
3. 轮询写入导致的帧数据竞态问题
你用死循环轮询CurrentFrame的方式写入流,可能出现同一帧被重复写入或者新帧覆盖旧帧时数据未完全读取的情况,导致帧数据拼接错误。
解决方法:
改成事件驱动的写入方式,在VideoFrameArrived事件触发时异步写入FFmpeg流,确保每帧只处理一次:
// 初始化时注册事件 VideoFrameArrived += OnVideoFrameArrived; private async void OnVideoFrameArrived(object sender, DeckLinkDeviceInputVideoFrameEventArgs e) { if (!isRecording || ffmpegInputStream == null) return; byte[] frameData = ConvertVideoFrameToByteArray(e.VideoFrame); await ffmpegInputStream.WriteAsync(frameData, 0, frameData.Length); GC.RemoveMemoryPressure(e.VideoFrame.GetRowBytes() * e.VideoFrame.GetHeight()); }
同时移除原来的死循环轮询方法,避免资源浪费和竞态问题。
4. FFmpeg RAW格式参数的细节补充
可以在FFmpeg参数中添加-vsync passthrough,确保帧时序严格匹配输入,避免FFmpeg自动调整帧率导致的错位:
string args = $"\\.\\ffmpeg.exe -y -f rawvideo -pix_fmt bgra -s 1920x1080 -r {actualFrameRate:F2} -vsync passthrough -i - -c:v libx264 -preset ultrafast -pix_fmt yuv420p -crf 23 \"{outputFilePath}\"";
内容的提问来源于stack exchange,提问作者massivemoisture

