ffmpeg d3d11va 加速 访问帧 出现帧混乱 flush

使用ffmpeg d3d11va时出现帧时序混乱问题。ffmpeg用d3d11va时frame数据存放有特点,按常规取图方式耗时。分析发现D3d有命令缓冲机制,猜测多线程操作导致pipeline stall。在copysubresourceregion后加入flush命令解决问题,且copysubresouce有两帧延时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用ffmpeg d3d11va的时候出现帧的时序上混乱。
通过ffmpeg 源码可知,ffmpeg 用d3d11va 时 出来的frame 中的data[0]存放的是decode texture数组(默认大小为20),data[1] 存放下标(可以发现,貌似下标总是一个随机的值,没调试进去,所以没有发现在哪里修改下标,不过通过下表访问的数据是没错的)。decode texture是在显存上的一个纹理,但是如果按demo中的方式去取图像,他需要先创建一个staging 类型的 texture ,再将decode texture拷贝到staging类型的 texture,再将staging 类型的texture中的内容map出来,这是极为消耗时间的 。我的操作是直接decode texture放到我的default类型的render texture中。开始的时候出现问题帧时序上的问题。但是如果调用ffmpeg demo中的原始的代码,却没有出现这个问题。

查了一通,发现这里

大概意思就是: D3d中有一个内部的命令缓冲(command list)机制,这个机制能减少用户态和内核态的切换。很多函数执行完后,会暂时将操作放到command list中,后续command list会高效的去执行。有以下几个操作能让command list立马执行完。

猜测 因为ffmpeg d3d11va 中解码时在内部有开启子线程。多线程操作导致了在 copysubresource 时 ,产生了pipeline stall。  
于是在copysubresourceregion后加入flush 命令,果然就没有这个问题了。下面的lock是必须加上的。

D3D11_BOX box; box.back = 1, box.front = 0, box.left = 0, box.right = descT.Width, box.top = 0, box.bottom = descT.Height;
ctx1->lock(ctx1->lock_ctx);
dctx->CopySubresourceRegion(frame->m_d3DTextureFfmpeg, 0, 0, 0, 0, (ID3D11Texture2D*)m_frame->data[0], currentFrameIndex,&box); //may be we can use the buffer in the frame directly
dctx->Flush(); //清空缓冲命令 //https://docs.microsoft.com/zh-cn/windows/desktop/api/d3d11/nf-d3d11-id3d11devicecontext-flush
ctx1->unlock(ctx1->lock_ctx);

值得注意的是copysubresouce 大概是有两帧的延时:

### FFMPEG与D3D11VA及D3D11的结合使用 在现代多媒体应用开发中,利用硬件加速技术显著提高视频解码性能是一种常见需求。FFmpeg 提供了对 D3D11VA 的支持,允许开发者通过 Direct3D 11 实现高效的硬件解码。 #### 配置环境 要使 FFmpeg 支持 D3D11VA 和 D3D11 进行硬件加速,需确保以下条件已满足: - 安装 Microsoft Visual Studio 并启用 C++ 开发工具链。 - 系统安装 DirectX SDK 或 Windows SDK,提供必要的头文件和库文件。 - 使用支持 DXVA2/DXVAHEVC 的 GPU 设备及其驱动程序。 #### 初始化 D3D11 Device 和 Swap Chain 以下是初始化 `ID3D11Device` 和 `IDXGISwapChain2` 的代码示例: ```cpp #include <d3d11.h> #include <dxgi1_2.h> // 创建 D3D11 设备和上下文 ID3D11Device* pD3DDevice; ID3D11DeviceContext* pD3DDeviceContext; D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; D3D11CreateDevice( nullptr, // 默认适配器 D3D_DRIVER_TYPE_HARDWARE, nullptr, // 软件模块句柄 (NULL 表示不指定) 0, // 不设置标志位 featureLevels, // 特性级别列表 _countof(featureLevels), // 列表长度 D3D11_SDK_VERSION, // SDK 版本号 &pD3DDevice, // 输出设备指针 nullptr, // 返回实际特性级别 &pD3DDeviceContext // 输出设备上下文指针 ); // 设置交换链描述符 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = width; // 缓冲区宽度 swapChainDesc.Height = height; // 缓冲区高度 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // 像素格式 swapChainDesc.SampleDesc.Count = 1; // 多采样数量 swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; swapChainDesc.BufferCount = 2; // 双缓冲模式 swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; IDXGIFactory2* pFactory; CreateDXGIFactory1(IID_PPV_ARGS(&pFactory)); IDXGISwapChain1* pSwapChain1; pFactory->CreateSwapChainForHwnd(pD3DDevice, hwnd, &swapChainDesc, nullptr, nullptr, reinterpret_cast<IDXGISwapChain1**>(&pSwapChain1)); ``` 此部分代码用于创建 Direct3D 11 设备以及与其关联的交换链对象[^1]。 #### 启用 FFmpeg 的 D3D11VA 支持 在 FFmpeg 中配置 D3D11VA 加速涉及以下几个方面: 1. **注册硬件设备上下文** 在 FFmpeg 中,可以通过 `av_hwdevice_ctx_create()` 注册一个硬件设备上下文实例。对于 D3D11VA,需要传递指向 `ID3D11Device` 的指针给 FFmpeg。 ```c AVBufferRef* hw_device_ctx = NULL; int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_D3D11VA, NULL, NULL, 0); if (ret < 0) { fprintf(stderr, "Failed to create hardware device context\n"); exit(1); } ``` 2. **分配解码上下文** 将硬件设备上下文附加到解码器实例上。 ```c AVCodecContext* codecCtx = avcodec_alloc_context3(codec); codecCtx->get_format = [](AVCodecContext* ctx, const enum AVPixelFormat* pix_fmts) -> enum AVPixelFormat { while (*pix_fmts != AV_PIX_FMT_NONE) { if (*pix_fmts == AV_PIX_FMT_D3D11VA_VLD) { return *pix_fmts; } pix_fmts++; } return AV_PIX_FMT_YUV420P; // 如果无法使用硬件加速,则回退至软件解码 }; codecCtx->hw_frames_ctx = av_buffer_ref(hw_device_ctx); // 绑定硬件上下文 ``` 3. **处理解码后的数据** 当前版本的 FFmpeg 对于 D3D11VA 解码会返回存储在显存中的纹理资源。如果直接访问这些纹理可能会遇到时序问题(如乱序播放),因此建议采用如下方法解决: - 使用 staging texture 映射显存内容; - 或者将 decode texture 直接绑定为目标渲染目标。 下面展示了一个简单的例子来说明如何获取并显示解码结果: ```cpp ID3D11Texture2D* decodedTex = (ID3D11Texture2D*)frame->data[0]; UINT index = frame->data[1]; // 渲染逻辑... m_pD3D11VideoContext->VideoProcessBlt(m_pOutputView, &outputRect, m_pD3D11VideoProcessor, 1, &inputFrame); ``` 需要注意的是,在某些情况下可能仍然会出现顺序错误的现象[^3]^。此时可尝试调整 FFmpeg 参数或增加额外同步机制加以规避。 --- ### 总结 综上所述,借助 FFmpeg 结合 D3D11VA 技术可以有效提升基于 Windows 平台下的视频流实时解码效率[^2]^。然而具体实施过程中还需注意兼容性和稳定性等问题,并根据实际情况灵活调整方案设计。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值