SinkWriter.WriteSample() 失败 E_INVALIDARG
SinkWriter.WriteSample() fails with E_INVALIDARG
我正在使用 MediaFoundation with SharpDX in order to encode video files from 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 is the full source file for MediaFoundation encoding and this 包括我如何创建纹理并从屏幕上捕获,以防我无意中遗漏了相关代码。
提前致谢。
原来是MFCreateVideoSampleFromSurface
没有正确设置创建缓冲区的长度。我解决这个问题的方法如下:
- 调用
MFCreateDXGISurfaceBuffer
,创建一个支持IMF2DBuffer
接口的媒体缓冲区。
- 查询
IMF2DBuffer
接口,将新建surface buffer的长度设置为IMF2DBuffer
的连续长度(另一种方式是将当前缓冲区长度设置为最大缓冲区长度,因此不需要查询 IMF2DBuffer
接口,但是 canonical 实现此目的的方法是查询IMF2DBuffer
,所以我坚持这一点。)
- 使用空表面指针调用
MFCreateVideoSampleFromSurface
——我发现这毫无意义——或者只是正常地创建一个 IMFSample
。
- 将表面缓冲区添加到新样本中并写入。
这就是我让它与 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 的人。我在网上到处找,没有找到关于这个问题的有用信息,这是微不足道的,但违反直觉。
我正在使用 MediaFoundation with SharpDX in order to encode video files from 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 is the full source file for MediaFoundation encoding and this 包括我如何创建纹理并从屏幕上捕获,以防我无意中遗漏了相关代码。
提前致谢。
原来是MFCreateVideoSampleFromSurface
没有正确设置创建缓冲区的长度。我解决这个问题的方法如下:
- 调用
MFCreateDXGISurfaceBuffer
,创建一个支持IMF2DBuffer
接口的媒体缓冲区。 - 查询
IMF2DBuffer
接口,将新建surface buffer的长度设置为IMF2DBuffer
的连续长度(另一种方式是将当前缓冲区长度设置为最大缓冲区长度,因此不需要查询IMF2DBuffer
接口,但是 canonical 实现此目的的方法是查询IMF2DBuffer
,所以我坚持这一点。) - 使用空表面指针调用
MFCreateVideoSampleFromSurface
——我发现这毫无意义——或者只是正常地创建一个IMFSample
。 - 将表面缓冲区添加到新样本中并写入。
这就是我让它与 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 的人。我在网上到处找,没有找到关于这个问题的有用信息,这是微不足道的,但违反直觉。