SinkWriter.WriteSample()因E_INVALIDARG而失败

时间:2017-12-21 17:59:12

标签: 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()创建的纹理,调整采样时间并调用{{1 }},与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(); } 正在显示此内容(full log):

mftrace

(注意:虽然这里的持续时间是0ms,但是由于一个不相关的错误,我也尝试用不同的值手动指定采样持续时间,这也失败并出现相同的错误)

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

我还尝试手动设置缓冲区和样本(使用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 ),结果是一样的。

This是MediaFoundation编码的完整源文件,this包括我如何创建纹理并从屏幕捕获,如果我无意中省略了相关代码。

提前致谢。

1 个答案:

答案 0 :(得分:3)

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

  1. 调用MFCreateDXGISurfaceBuffer,创建支持IMF2DBuffer接口的媒体缓冲区。
  2. 查询IMF2DBuffer接口,并将新创建的表面缓冲区的长度设置为IMF2DBuffer连续长度(另一种方式是设置当前缓冲区长度到最大缓冲区长度,因此不需要查询IMF2DBuffer接口,但规范方法来实现这一点是通过查询IMF2DBuffer的连续长度,所以我坚持下去。)
  3. 使用空面指针调用MFCreateVideoSampleFromSurface - 我发现这是无意义的 - 或者只是正常创建IMFSample
  4. 将表面缓冲区添加到新样本并写入。
  5. 这就是我使用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。我在互联网上无处不在,发现没有关于这个问题的有用信息,这是微不足道的,但是反直觉。