简体   繁体   中英

How to create a C# wrapper for custom DirectShow h264 encoder

I am trying to create some basic C# wrapper for a h264 encoder. I am using Directshow.NET and some custom H264 encoder. The encoder directshow filter is a part of video processing project http://sourceforge.net/projects/videoprocessing/ The filter class (H264EncoderFilter) is inheriting an ISettingsInterface:

//For Smart pointers
DEFINE_GUID( IID_ISettingsInterface, /* 388EEF20-40CC-4752-A0FF-66AA5C4AF8FA */
        0x388eef20, 
        0x40cc, 
        0x4752, 
        0xa0, 0xff, 0x66, 0xaa, 0x5c, 0x4a, 0xf8, 0xfa
        );

#undef  INTERFACE
#define INTERFACE   ISettingsInterface
DECLARE_INTERFACE_( ISettingsInterface, IUnknown )
{
// *** methods ***
/// Method to retrieve parameters named type. The result will be stored in value and the length of the result in length
STDMETHOD(GetParameter)( const char* type, int buffersize, char* value, int* length ) = 0;
/// Method to set parameter named type to value
STDMETHOD(SetParameter)( const char* type, const char* value) = 0;
/// Method to retrieve ALL parameters in szResult. nSize should contain the size of the buffer passed in
STDMETHOD(GetParameterSettings)(char* szResult, int nSize) = 0;

};

I created a wrapper for the filter itself (in Uuids.cs of Directshow.NET lib, for the record):

[ComImport, Guid("28D61FDF-2646-422D-834C-EFFF45884A36")]
public class H264Encoder
{ 
}

With it, I can instantiate the filter class in C# and also I am able to cast the filter on IBaseFilter interface, so I guess this wrapper works.

Next, I wanted to create a wrapper for the before mentioned ISettingsInterface (I added the code below to AxCore.cs):

[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
Guid("388EEF20-40CC-4752-A0FF-66AA5C4AF8FA"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISettingsInterface
{
    [PreserveSig]
    int GetParameter(
        [MarshalAs(UnmanagedType.LPStr)] String type,
        [MarshalAs(UnmanagedType.I4)] int buffersize,
        [In, Out, MarshalAs(UnmanagedType.LPStr)] String value,
        [In, Out, MarshalAs(UnmanagedType.I4)] ref int length
        );

    [PreserveSig]
    int SetParameter(
        [MarshalAs(UnmanagedType.LPStr)] String type,
        [MarshalAs(UnmanagedType.LPStr)] String value
        );

    [PreserveSig]
    int GetParameterSettings(
        [MarshalAs(UnmanagedType.LPStr)] ref String szResult,
        [In] int nSize
        );
}

This brings us to my problem. When I am trying to use the interface, not everything works. When I use SetParameter function, it seems to behave allright (Hresult returned is 0), but something wrong happens when I use GetParameter. Please take a look on testing code and it's console output:

object enc = new H264Encoder();
        ISettingsInterface enc_settings = enc as ISettingsInterface;
        String szParamValue= "initinitinitinit";

        unsafe //Write address od szParamValue
        {
            fixed (char* wsk = szParamValue)
            {
                IntPtr ptr = (IntPtr)wsk;
                Console.WriteLine("adres: " + ptr.ToInt64());
            }
        }

        int nLength=0;
        int hr = enc_settings.SetParameter("quality", "15"); //set quality to some arbitrary value
        hr = enc_settings.GetParameter("quality", 16, ref szParamValue, ref nLength);

        Console.WriteLine("szParamValue: " + szParamValue);
        Console.WriteLine("nLength: " + nLength);
        Console.WriteLine("HRESULT: " + hr);
        Console.WriteLine(DsError.GetErrorText(hr));
        Marshal.ReleaseComObject(enc_settings);
        Marshal.ReleaseComObject(enc);

        unsafe //Write address od szParamValue
        {
            fixed (char* wsk = szParamValue)
            {
                IntPtr ptr = (IntPtr)wsk;
                Console.WriteLine("adres: " + ptr.ToInt64());
            }
        }

Console output: http://img707.imageshack.us/img707/3667/consolevd.png

Observations:

  • szParamValue should be a string containg "15", because it was set that way by SetParameter. Instead, it's a mess.
  • nLength is the length of string contained in SetParameter, which is correct because length of the expected "15" is 2. It changes to 3 when quality set to for example "151"
  • szParamValue isn't always such a mess, sometimes it's an empty string or some XML code... And, what's more important, AccessViolationException is thrown in line with GetParameter call. The exception details attached below.
  • Address od szParamValue in memory changes, as you can see in the console output.

Exception details:

System.AccessViolationException was unhandled Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt. Source=DirectShowLib-2005 StackTrace: at DirectShowLib.ISettingsInterface.GetParameter(String type, Int32 buffersize, String& value, Int32& length) at ConsoleApplication4.Program.Main(String[] args) in F:\\Documents\\Visual Studio 2010\\Projects\\ConsoleApplication4\\ConsoleApplication4\\Program.cs:line 79 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException:

Questions: First one is obvious- what am I doing wrong? ;) Second, how does .NET marshaler or anything know if I wrote the interface correctly? How does it know which function to call from the original c++ interface? It doesn't recognize them by names (I tried to incorporate a typo- SetParam instead of SetParameter and it worked) but it failed when I swapped the function order in the interface.

PS I can attach any code you want (or you can download also download it) because videoprocessing project is open source, as directshow.net. The code created by me is all here.

Thank you in advance.

EDIT: SetParameter indeed works, as I created a filter graph camera -> h264 -> decoder -> renderer and played only with SetParameter("quality", "..."); and an expected and clearly visible reaction was present.

you are using the wrong type for marshalling the value (3rd) parameter in GetParameter. instead of string use StringBuilder. Like below

[PreserveSig]
int GetParameter(
    [MarshalAs(UnmanagedType.LPStr)] String type,
    [MarshalAs(UnmanagedType.I4)] int buffersize,
    [In, Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder value,
    [In, Out, MarshalAs(UnmanagedType.I4)] ref int length);

An example of using it would then be

StringBuilder sb = new StringBuilder();
int len = int.MinValue;
((ISettingsInterface)enc).GetParameter("quality", 0, sb, ref len);
string value = sb.ToString();

Not sure what the buffersize parameter does, but i can set it to 0 and the method still returns the expected values

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM