You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 07:01:09