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

如何将C++ FFmpeg的AVFrame直接复制到C# WritableBitmap缓存

问题描述

原本通过FFmpeg的sws_scale将解码后的AVFrame缩放后传入C#的WritableBitmap缓存,显示完全正常。现在希望跳过缩放步骤,直接将AVFrame缓存复制到WritableBitmap缓存,但用memcpy实现后无显示效果,求正确的复制方式。


相关代码

原C++缩放实现及函数定义

int scale_decoded_video_frame(void* handle, void* scalerHandle, void* scaledBuffer, int scaledBufferStride) 

sws_scale(
    scalerContext->sws_context,
    srcData,
    context->frame->linesize,
    0, 
    scalerContext->source_height,
    reinterpret_cast<uint8_t**>(&scaledBuffer),
    &scaledBufferStride);

C#声明及调用代码

[DllImport(LibraryName, EntryPoint = "scale_decoded_video_frame", CallingConvention = CallingConvention.Cdecl)]
public static extern int ScaleDecodedVideoFrame(IntPtr handle, IntPtr scalerHandle, IntPtr scaledBuffer, int scaledBufferStride);
WriteableBitmap w = new WriteableBitmap(1920, 1080, 96, 96, PixelFormats.Pbgra32, null);
RenderOptions.SetBitmapScalingMode(w, BitmapScalingMode.NearestNeighbor);
w.Lock();

Int32Rect rect = new Int32Rect(0, 0, 1920, 1080);

resultCode = FFmpegVideoPInvoke.ScaleDecodedVideoFrame(decoderPtr, scalerHandle, w.BackBuffer, w.BackBufferStride);
if (resultCode == 0)
   w.AddDirtyRect(rect);

w.Unlock();

尝试的无效memcpy代码

memcpy(scaledBuffer, context->frame->data, AV_NUM_DATA_POINTERS * sizeof(uint8_t));
memcpy(&scaledBufferStride, context->frame->linesize, AV_NUM_DATA_POINTERS * sizeof(int));

问题原因

你写的memcpy存在核心错误:

  1. context->frame->data是指针数组,代码只复制了指针本身的字节,而非指针指向的实际像素数据
  2. WritableBitmap使用Pbgra32格式,而FFmpeg解码出的AVFrame通常是YUV格式(如YUV420P),格式不兼容直接复制必然无法显示
  3. 即使格式一致,AVFrame的linesize和WritableBitmap的BackBufferStride可能因内存对齐存在差异,不能直接整块复制

正确实现方式

跳过缩放的前提是AVFrame的分辨率、像素格式必须和WritableBitmap完全匹配,分两种情况处理:

情况1:AVFrame为YUV格式,需转BGRA32(无缩放)

用sws_scale仅做格式转换(因分辨率一致,无缩放逻辑):

  1. 先初始化格式转换上下文(示例以YUV420P转BGRA为例):
// 初始化时创建转换上下文
SwsContext* convertCtx = sws_getContext(
    srcWidth, srcHeight, AV_PIX_FMT_YUV420P,
    srcWidth, srcHeight, AV_PIX_FMT_BGRA,
    SWS_BILINEAR, NULL, NULL, NULL);
  1. 修改C++函数实现格式转换:
int scale_decoded_video_frame(void* handle, void* scalerHandle, void* scaledBuffer, int scaledBufferStride) {
    DecoderContext* context = reinterpret_cast<DecoderContext*>(handle);
    AVFrame* frame = context->frame;

    uint8_t* dstData[1] = { reinterpret_cast<uint8_t*>(scaledBuffer) };
    int dstLinesize[1] = { scaledBufferStride };

    // 仅做格式转换,无缩放
    int ret = sws_scale(
        convertCtx,
        frame->data,
        frame->linesize,
        0,
        frame->height,
        dstData,
        dstLinesize);

    return ret > 0 ? 0 : -1;
}

情况2:AVFrame已为BGRA32格式,直接逐行复制

若AVFrame像素格式为AV_PIX_FMT_BGRA且分辨率匹配,逐行复制数据(适配linesize差异):

int scale_decoded_video_frame(void* handle, void* scalerHandle, void* scaledBuffer, int scaledBufferStride) {
    DecoderContext* context = reinterpret_cast<DecoderContext*>(handle);
    AVFrame* frame = context->frame;

    // 逐行复制,处理linesize和BackBufferStride的差异
    for (int y = 0; y < frame->height; y++) {
        uint8_t* srcRow = frame->data[0] + y * frame->linesize[0];
        uint8_t* dstRow = reinterpret_cast<uint8_t*>(scaledBuffer) + y * scaledBufferStride;
        // 复制一行的像素数据(BGRA每个像素占4字节)
        memcpy(dstRow, srcRow, frame->width * 4);
    }

    return 0;
}

关键注意事项

  • 必须保证AVFrame的宽高与WritableBitmap完全一致,否则会显示错乱
  • Pbgra32对应FFmpeg的AV_PIX_FMT_BGRA格式,注意字节顺序为B、G、R、A
  • 禁止直接复制frame->data指针数组,必须复制指针指向的实际像素数据
  • 逐行复制时必须考虑linesize的内存对齐差异,避免越界或数据缺失

内容的提问来源于stack exchange,提问作者Spuriga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:01:18