使用媒体基础架构对DirectX surfaces进行编码

5
我正在尝试使用MediaFoundation API来对视频进行编码,但是我在将样本推送到SinkWriter时遇到了问题。
通过Desktop Duplication API获取要编码的帧。最终我得到了一个包含桌面图像的ID3D11Texture2D。
我试图创建一个包含此表面的IMFVideoSample,然后将该视频样本推送到SinkWriter。
我已经尝试了不同的方法:
1. 我调用了MFCreateVideoSampleFromSurface(texture, &pSample),其中texture是ID3D11Texture2D,填写了SampleTime和SampleDuration,然后将创建的样本传递给SinkWriter。 SinkWriter返回E_INVALIDARG。
2. 我尝试通过将第一个参数设置为nullptr并自己使用MFCreateDXGISurfaceBuffer创建缓冲区来创建样本,然后将生成的缓冲区传递到Sample中。 也无法正常工作。
3. 我阅读了MediaFoundation文档,但未找到有关如何从DirectX纹理创建样本的详细信息。
我已经没有可以尝试的事情了。是否有使用过这个API并且可以想出我应该检查的问题或者调试方式的人呢?

你确定吗? 查看 MFCreateDXGISurfaceBuffer 的文档,它说明返回一个指向 IMFMediaBuffer 的指针。 - Tiago Magalhães
1
从由DXGI表面支持的样本/缓冲区中获取数据的最佳方法似乎是使用MFGetService。 另一个线程指出这两个API应该能够处理此任务:https://dev59.com/DG3Xa4cB1Zd3GeqPha1B。 我无法在任何地方找到有关使用这两种方法的最佳示例。 我们将尝试采用另一种方法,这很遗憾,因为DesktopDuplication + MediaFoundation似乎是我们问题的最佳解决方案。 - Tiago Magalhães
1
你使用DesktopDuplication + MediaFoundation的想法很有趣。在我的项目CaptureManager SDK中,我在'CaptureProject'网站上使用了DirectX9 + MediaFoundation来捕获实时屏幕视频,但它从桌面管理器复制图像(在任务管理器程序和桌面管理器之间相互占用15%)。我认为使用DesktopDuplication可能更有效。 - Evgeny Pereguda
1
@Tiago Magalhães,你找到解决问题的方法了吗?我也遇到了同样的困境,因为我找不到相关的资源。 - iamrameshkumar
抱歉Ram,我没有解决这个问题,我们最终采用了一个与整体问题非常不同的解决方案。 就像你所说,资源非常有限,而且时间也不够。 但是如果你想到了什么,我很乐意听取你的建议,这是我打算在未来某一天回头解决的问题。 - Tiago Magalhães
显示剩余2条评论
2个回答

2
首先,您应该学习使用mftrace工具。很可能,它会立即告诉您问题所在。
但是我的猜测是,以下问题可能会出现:
1. 可能需要除SampleTime / SampleDuration之外的其他属性。 2. 可能SinkWriter需要一种可以在CPU上读取的纹理。要解决这个问题,当一个帧可用时,创建一个相同格式+大小的暂存纹理,调用CopyResource将桌面复制到暂存纹理,然后将该暂存纹理传递给MF。 3. 即使您使用硬件编码器,以便CPU永远不会尝试读取纹理数据,我认为直接将桌面纹理传递给MF也不是一个好主意。
当您为样本设置D3D纹理时,不会将任何数据复制到任何地方,样本仅保留纹理。
MF异步工作,如果拓扑节点想要,它可能会缓冲多个样本。
DD同步提供数据,您只能在AcquireNextFrame和ReleaseFrame调用之间访问纹理。

我尝试不要在问题中涉及太多细节,但是在将桌面纹理传递给MF之前,我会通过获取桌面纹理描述符并创建一个新的纹理来复制桌面纹理,然后再使用CopyResource。但是非常感谢mftrace指针,我会查看一下。 - Tiago Magalhães
我使用了mfctrace并发现发送到接收器的样本被报告为大小为0B,因此它们的创建出了问题。 - Tiago Magalhães
@Soonts,我甚至尝试创建了一个暂存纹理,并调用了CopyResource,但输出没有任何影响。仅得到一个绿屏。 https://dev59.com/quk5XIcBkEYKwwoY49kS - iamrameshkumar

0
如果有人需要帮助的话,它的工作原理是通过调用MF.CreateDXGISurfaceBuffer(surface, 0, false, &dstBuffer)来实现的,但是并没有明确指定,您还需要调用dstBuffer.SetCurrentLength(length)来设置表面缓冲区的正确长度。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接