sharpdx / SharpDX

SharpDX GitHub Repository
http://sharpdx.org
MIT License
1.69k stars 641 forks source link

Windows Media Foundation IDirectXVideoProcessor #955

Open grill2010 opened 6 years ago

grill2010 commented 6 years ago

I try to build a IDirectXVideoProcessor object with Direct3D9. I'm not a DirectX expert so maybe I'm doing something wrong but I'm not able to generate a SharpDX.MediaFoundation.DirectX.VideoProcessor. It always fails with this error message:

{"HRESULT: [0x80070057], Module: [General], ApiCode: [E_INVALIDARG/Invalid Arguments], Message: Falscher Parameter.\r\n"}

I try to build the IDirectXVideoProcessor by using the VideoProcessorService. Another strange thing is when I call

videoProcessorService.GetVideoProcessorDeviceGuids(ref videoDesc, out count, guid);

the variable count is 5 but in the guid array there is just one GUID (the initial array size was 5). You can find my example code below:

using (Direct3D d3d = new Direct3D())
{
    using (var renderForm = new System.Windows.Forms.Form())
    {
        SharpDX.Direct3D9.Device device;
        //using (device = new Device(d3d, 0, SharpDX.Direct3D9.DeviceType.NullReference, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1, DeviceWindowHandle = renderForm.Handle, PresentFlags = PresentFlags.Video }))
        using (device = new Device(d3d, 0, SharpDX.Direct3D9.DeviceType.Hardware, renderForm.Handle, CreateFlags.HardwareVertexProcessing, new SharpDX.Direct3D9.PresentParameters(1, 1) /*new SharpDX.Direct3D9.PresentParameters(this.ClientSize.Width, this.ClientSize.Height, Format.A8R8G8B8, 1) { BackBufferWidth = 1, BackBufferHeight = 1, DeviceWindowHandle = renderForm.Handle, PresentFlags = PresentFlags.None, FullScreenRefreshRateInHz = 60, SwapEffect = SwapEffect.Copy, AutoDepthStencilFormat = Format.A8R8G8B8, EnableAutoDepthStencil= false }*/))
        {

            using (VideoProcessorService videoProcessorService = new VideoProcessorService(device))
            {

                int count = 5;
                Guid[] guid = new Guid[10];
                // https://msdn.microsoft.com/de-de/library/windows/desktop/ms695370(v=vs.85).aspx
                VideoDesc videoDesc = new VideoDesc()
                {
                    Format = SharpDX.Direct3D9.Format.A8R8G8B8,
                    InputSampleFreq = new Frequency()
                    {
                        Denominator = 1,
                        Numerator = 60
                    },
                    OutputFrameFreq = new Frequency()
                    {
                        Denominator = 1,
                        Numerator = 60
                    },
                    SampleFormat = new ExtendedFormat()
                    {
                        NominalRange = (int)NominalRange.Range16_235,
                        SampleFormat = (int)SampleFormat.ProgressiveFrame,
                        VideoChromaSubsampling = (int)VideoChromaSubSampling.SamplingVerticallyAlignedChromaPlanes,
                        VideoLighting = (int)VideoLighting.Dim,
                        VideoPrimaries = (int)VideoPrimaries.Bt709,
                        VideoTransferFunction = (int)VideoTransferFunction.Func709,
                        VideoTransferMatrix = (int)VideoTransferMatrix.Bt709,
                        Value = 0
                    },
                    SampleWidth = 640,
                    SampleHeight = 480,
                    UABProtectionLevel = 0
                };

                // On my PC count is 5 but only 1 guid is present in the array???
                videoProcessorService.GetVideoProcessorDeviceGuids(ref videoDesc, out count, guid);

                if (count > 0)
                {
                    try
                    {
                        SharpDX.MediaFoundation.DirectX.VideoProcessor videoProcessor;
                        // always fails
                        videoProcessorService.CreateVideoProcessor(guid[0], ref videoDesc, SharpDX.Direct3D9.Format.A8R8G8B8, 0, out videoProcessor);
                    }
                    catch (Exception ex)
                    {
                    }
                }
            }
        }
    }
}

I seems in regard to the error message that some parameters are invalid or not correct but I can not figure out what is wrong. I could not even find one example which uses the SharpDX.MediaFoundation.DirectX.VideoProcessor, or maybe I just missed it.

Anyway, any help in regard to this topic would be highly appreciated. Many thanks.

xoofx commented 6 years ago

Many functions in MediaFoundation requires manually handling the marshalling of arguments. Typically for https://msdn.microsoft.com/en-us/library/windows/desktop/ms695370(v=vs.85).aspx you can see that the output argument guid is actually an array, but the DirectX headers doesn't help us to catch this automatically. So it requires to fix in via implementing correctly the marshalling in SharpDX.

  1. Modify the mapping.xml file, make the CreateVideoProcessor internal
  2. Modify the argument guid to be a IntPtr (void) instead of a guid
  3. Expose a proper C# method with an array parameters and call the internal method correctly

For an example, you have 1 and 2:

https://github.com/sharpdx/SharpDX/blob/0793b4e84f40ddca260df9ef5e467ebdb3414726/Source/SharpDX.DXGI/Mapping.xml#L258-L260

And for 3:

https://github.com/sharpdx/SharpDX/blob/0793b4e84f40ddca260df9ef5e467ebdb3414726/Source/SharpDX.DXGI/SwapChain1.cs#L82-L100

(The above example doesn't have an array, so it is a bit different, you just have to fixed the passed array and pass it to CreateVideoProcessor directly).

PR Welcome.

grill2010 commented 6 years ago

Many thanks for your quick reply. I will check it out and hopefully I can fix the error with IDirectXVideoProcessor too.

grill2010 commented 6 years ago

I looked into the code and I can finally build the SharpDX project. It seems that the GetVideoProcessorDeviceGuids method is located in a hand generated Interfaces class located in SharpDX.MediaFoundation.DirectX. So do I still need to modify any Mapping.xml if it is manually generated? And if yes, am I right to insert this in the Mapping.xml located in SharpDX.MediaFoundation.DirectX? I have now a few additional questions, regarding to your suggestions.

1. Modify the mapping.xml file, make the CreateVideoProcessor internal -> You've meant GetVideoProcessorDeviceGuids right? Or why do I need to make the CreateVideoProcessor internal?

3. Expose a proper C# method with an array parameters and call the internal method correctly -> I'm not quite sure if I've understood this correctly. I know that the SwapChain1 in your example is a partial class which enhance the auto generated interface methods. So in my case does this mean I have to generate a partial class VideoProcessorService with a method which acts as a layer between the real native call in the auto generated (or hand made) Interfaces class, which should handle the marshaling of the array to a pointer and vice versa?

I'm sorry about the many questions but I just want to make sure that I'm on the right way. Thank you for your help.

xoofx commented 6 years ago

So do I still need to modify any Mapping.xml if it is manually generated? And if yes, am I right to insert this in the Mapping.xml located in SharpDX.MediaFoundation.DirectX?

yes and yes.

-> You've meant GetVideoProcessorDeviceGuids right? Or why do I need to make the CreateVideoProcessor internal?

Yep sorry

-> I'm not quite sure if I've understood this correctly. I know that the SwapChain1 in your example is a partial class which enhance the auto generated interface methods. So in my case does this mean I have to generate a partial class VideoProcessorService with a method which acts as a layer between the real native call in the auto generated (or hand made) Interfaces class, which should handle the marshaling of the array to a pointer and vice versa?

Yes, but marshaling in this case should be just a fixed statement. The partial class already exists actually.

grill2010 commented 6 years ago

Okay, and when I've modified the Mapping.xml located in the SharpDX.MediaFoundation.DirectX it will automatically create some code in some Interfaces class right, where is this new code located? And whats the case with the Interfaces class in the Hand.Generated because it seems it has exactly the function GetVideoProcessorDeviceGuids in it which I need to modify and when I compile the whole SharpDX project it doesn't seem to change anything in this class?

Sorry again, I know that these are very basic questions and you have for sure some other thing to do but I really have to understand the functionality before I try to modify something.

xoofx commented 6 years ago

Okay, and when I've modified the Mapping.xml located in the SharpDX.MediaFoundation.DirectX it will automatically create some code in some Interfaces class right, where is this new code located? And whats the case with the Interfaces class in the Hand.Generated because it seems it has exactly the function GetVideoProcessorDeviceGuids in it which I need to modify and when I compile the whole SharpDX project it doesn't seem to change anything in this class?

Ah, forgot completely that these interfaces are no longer automatically generated from C++, so changing the mapping XML file will not do anything. You will have to update the hand generated file manually. The reason these files are no longer going through C++ autogen code is that many direct3d9 files were removed in recent SDKs, and it was starting to be very complicate to work with different SDKs, so I decided to freeze all the old/legacy wrapper.

So, if you will have to modify the generated code manually.

grill2010 commented 6 years ago

Ah okay, that make sense. I will see how far I will get. Many thanks for your quick reply and for your help.