c# - SinkWriter.WriteSample() 失败并返回 E_INVALIDARG

标签 c# sharpdx ms-media-foundation

我正在使用 MediaFoundation使用 SharpDX 对来自 desktop duplication frames 的视频文件进行编码.

我正在创建纹理并捕获屏幕。然后将此纹理传递给 MFCreateVideoSampleFromSurface,这被称为一次

/* creating the texture */
new Texture2D(/* device */,
  new Texture2DDescription {
  CpuAccessFlags = CpuAccessFlags.Read,
  BindFlags = BindFlags.None,
  Format = Format.B8G8R8A8_UNorm,
  Width = /* width */,
  Height = /* height */,
  OptionFlags = ResourceOptionFlags.None,
  MipLevels = 1,
  ArraySize = 1,
  SampleDescription = { Count = 1, Quality = 0 },
  Usage = ResourceUsage.Staging
})

/* creating the sample */
Evr.CreateVideoSampleFromSurface(SourceTexture, out /* the sample */);

样本和纹理初始化后,我将调用 IDXGIOutputDuplication::AcquireFrame(),将相关区域从屏幕复制到我事先使用 ID3D11DeviceContext::CopySubresourceRegion 创建的纹理(),调整采样时间并调用 ISinkWriter::WriteSample(),失败并返回 ERROR_INVALID_PARAMETER

这些是我传递给 SinkWriter 的属性:

using (var attrs = new MediaAttributes()) {
  attrs.Set(TranscodeAttributeKeys.TranscodeContainertype, /* container type GUID */);
  attrs.Set(SinkWriterAttributeKeys.ReadwriteEnableHardwareTransforms, 1);
  attrs.Set(SinkWriterAttributeKeys.LowLatency, true);

  if (SourceTexture != null) {
    // create and bind a DXGI device manager
    this.dxgiManager = new DXGIDeviceManager();
    this.dxgiManager.ResetDevice(SourceTexture.Device);

    attrs.Set(SinkWriterAttributeKeys.D3DManager, this.dxgiManager);
  }

  this.byteStream = new ByteStream(/* ... */);
  this.sinkWriter = MediaFactory.CreateSinkWriterFromURL(null, this.byteStream.NativePointer, attrs);

  /* ... */
}

这就是我初始化输入媒体类型的方式:

using (var inMediaType = new MediaType()) {
  inMediaType.Set(MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video);
  inMediaType.Set(MediaTypeAttributeKeys.Subtype, VideoFormatGuids.Rgb32);
  inMediaType.Set(MediaTypeAttributeKeys.InterlaceMode, (int) VideoInterlaceMode.Progressive);
  inMediaType.Set(MediaTypeAttributeKeys.FrameSize,
    ((long) frameSize.Width << 32) | (uint) frameSize.Height);
  inMediaType.Set(MediaTypeAttributeKeys.FrameRate, ((long) FrameRate << 32) | 1);
  inMediaType.Set(MediaTypeAttributeKeys.PixelAspectRatio, 1);

  this.sinkWriter.SetInputMediaType(this.streamIdx, inMediaType, null);
  this.sinkWriter.BeginWriting();
}

mftrace 显示这个 ( full log ):

6532,C24 17:03:29.01758 CMFSinkWriterDetours::WriteSample @000000001ED45D50 Stream Index 0x0, Sample @000000001ED46B30, Time 0ms, Duration 0ms, Buffers 1, Size 0B,
6532,C24 17:03:29.01759 CMFSinkWriterDetours::WriteSample @000000001ED45D50 failed hr=0x80070057 ERROR_INVALID_PARAMETER

(注意:尽管由于不相关的错误,这里的持续时间为 0 毫秒,但我也尝试使用不同的值手动指定样本持续时间,但同样失败并出现相同的错误)

如果这在某种程度上是相关的,尝试对具有小屏幕区域(600x500 等)的视频进行编码似乎有 1/3 的时间完美无缺。在这种情况下尝试编码 1920x1080 帧在任何情况下都没有成功,这对我来说没有任何意义(我没有传递任何已处理的资源,也没有读取空闲的内存)

我也尝试过手动设置缓冲区和样本(使用 MFCreateDXGISurfaceBuffer),结果是一样的。

This是 MediaFoundation 编码的完整源文件,this包括我如何创建纹理和从屏幕上捕获,以防我无意中遗漏了相关代码。

提前致谢。

最佳答案

事实证明,MFCreateVideoSampleFromSurface 没有正确设置创建的缓冲区的长度。我解决这个问题的方法如下:

  1. 调用 MFCreateDXGISurfaceBuffer,创建一个支持 IMF2DBuffer 接口(interface)的媒体缓冲区。
  2. 查询IMF2DBuffer接口(interface)并将新创建的表面缓冲区的长度设置为IMF2DBuffer连续长度(另一种方式是将当前缓冲区长度设置为最大缓冲区长度,因此不需要查询 IMF2DBuffer 接口(interface),但实现此目的的规范方法是查询的连续长度IMF2DBuffer,所以我坚持这样做。)
  3. 使用空表面指针调用 MFCreateVideoSampleFromSurface(我发现这毫无意义),或者只是正常地创建一个 IMFSample
  4. 将表面缓冲区添加到新样本中并写入。

这就是我如何让它与 C#/SharpDX 一起工作:

this.sample?.Dispose();
this.buffer?.Dispose();

MediaFactory.CreateDXGISurfaceBuffer(SourceTexture.GetType().GUID,
  SourceTexture,
  0,
  new RawBool(false),
  out this.buffer);

this.sample = MediaFactory.CreateSample();
this.sample.SampleTime = time;

this.buffer.CurrentLength = this.buffer.QueryInterface<Buffer2D>().ContiguousLength;
this.sample.AddBuffer(this.buffer);

希望它能帮助任何使用 MediaFoundation 的人。我在互联网上到处查看,没有找到关于这个问题的有用信息,这是微不足道的,但违反直觉。

关于c# - SinkWriter.WriteSample() 失败并返回 E_INVALIDARG,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47930340/

相关文章:

C# | SharpDX.X输入法 |如何检测 Controller 上的按钮是否被按下一次并且没有被按下

c# - 从 C++ 到 C# 的 DirectX 结构数组的困难编码

c++ - 媒体基础 H264 解码器无法正常工作

c++ - 每个应用程序实例超过一个 Audio Session

c# - 什么是 Type.GUID,它与 Type.Equals() 有什么关系?

c# - Ninject基类和继承类绑定(bind)到同一个实例

C# 类构造函数前置条件

directx - 使用结构化缓冲区的 Direct3D 10 硬件实例

c# - 如何通过 C# 向 Linux 系统发出命令?

c# - 使用带有 C# 的 MediaTranscoder 将 PCM 音频转码为 MP3