如何使用LibAV逐帧输入编码帧并获取解码后帧?
使用LibAV处理第三方TCP返回的H264帧问题
需求说明
我正在集成一个第三方系统,通过发送TCP数据包从实时CCTV摄像头流中请求指定数量(比如5帧)的画面,系统会逐帧返回带封装数据的H264编码帧。需要用LibAV实现以下功能:
- 使用AVIOContext(或类似组件)逐帧输入编码帧
- 获取解码后的帧
- 将帧绘制到窗口(仅作上下文说明,与核心问题无关)
此前用GStreamer实现过相同功能,对应的管道是:
AppSrc -> H264Parser -> H264Decoder -> FrameGrabber
当前编写的C#代码
using System.Runtime.InteropServices; using FFmpeg.AutoGen; namespace AvIoCtxExperiment; public static unsafe class Program { public static void Main() { ffmpeg.avdevice_register_all(); Console.WriteLine($"FFmpeg v: {ffmpeg.av_version_info()}"); // Generous buffer size for I frames (~43KB) const int bufferSize = 50 * 1024; var buff = (byte*)ffmpeg.av_malloc(bufferSize); if (buff == null) throw new Exception("Buffer is null"); // A mock frame provider. Frames are stored in separate files and this reads & returns them one-by-one. var frameProvider = new FrameProvider(@"D:\Frames-1", 700); var gch = GCHandle.Alloc(frameProvider); var avioCtx = ffmpeg.avio_alloc_context( buffer: buff, buffer_size: bufferSize, write_flag: 0, opaque: (void*)GCHandle.ToIntPtr(gch), read_packet: new avio_alloc_context_read_packet(ReadFunc), write_packet: null, seek: null); var formatContext = ffmpeg.avformat_alloc_context(); formatContext->pb = avioCtx; formatContext->flags |= ffmpeg.AVFMT_FLAG_CUSTOM_IO; var openResult = ffmpeg.avformat_open_input(&formatContext, null, null, null); if (openResult < 0) throw new Exception("Open Input Failed"); if (ffmpeg.avformat_find_stream_info(formatContext, null) < 0) throw new Exception("Find StreamInfo Failed"); AVPacket packet; while (ffmpeg.av_read_frame(formatContext, &packet) >= 0) { Console.WriteLine($"GRAB: {packet.buf->size}"); ffmpeg.av_packet_unref(&packet); } } private static int ReadFunc(void* opaque, byte* buf, int bufSize) { var frameProvider = (FrameProvider?)GCHandle.FromIntPtr((IntPtr)opaque).Target; if (frameProvider == null) { return 0; } byte[] managedBuffer = new byte[bufSize]; var fBuff = frameProvider.NextFrame(); if (fBuff == null) { return ffmpeg.AVERROR_EOF; } int bytesRead = fBuff.Length; fBuff.CopyTo(managedBuffer, 0); if (bytesRead > 0) { Marshal.Copy(managedBuffer, 0, (IntPtr)buf, bytesRead); Console.WriteLine($"READ size: {fBuff.Length}"); return bytesRead; } return ffmpeg.AVERROR_EOF; } }
困惑点
用GStreamer的AppSrc时,我可以输入任意大小的帧,且由我主动向管道输入帧,GStreamer会通过信号通知是否需要更多数据;但LibAV需要提前指定缓冲区大小,且是LibAV自行调用read_packet委托来获取数据,这部分逻辑不太清楚怎么适配我的场景。
恳请提供帮助,感谢。
附:我正在编写C#应用,但C语言示例代码也可以,我可以自行适配。
内容的提问来源于stack exchange,提问作者Alvan Rahimli
相关产品推荐
相关产品推荐

