如何将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存在核心错误:
context->frame->data是指针数组,代码只复制了指针本身的字节,而非指针指向的实际像素数据- WritableBitmap使用
Pbgra32格式,而FFmpeg解码出的AVFrame通常是YUV格式(如YUV420P),格式不兼容直接复制必然无法显示 - 即使格式一致,AVFrame的
linesize和WritableBitmap的BackBufferStride可能因内存对齐存在差异,不能直接整块复制
正确实现方式
跳过缩放的前提是AVFrame的分辨率、像素格式必须和WritableBitmap完全匹配,分两种情况处理:
情况1:AVFrame为YUV格式,需转BGRA32(无缩放)
用sws_scale仅做格式转换(因分辨率一致,无缩放逻辑):
- 先初始化格式转换上下文(示例以YUV420P转BGRA为例):
// 初始化时创建转换上下文 SwsContext* convertCtx = sws_getContext( srcWidth, srcHeight, AV_PIX_FMT_YUV420P, srcWidth, srcHeight, AV_PIX_FMT_BGRA, SWS_BILINEAR, NULL, NULL, NULL);
- 修改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
相关产品推荐
相关产品推荐

