简体   繁体   English

如何为自定义DirectShow h264编码器创建C#包装器

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

I am trying to create some basic C# wrapper for a h264 encoder. 我正在尝试为h264编码器创建一些基本的C#包装器。 I am using Directshow.NET and some custom H264 encoder. 我正在使用Directshow.NET和一些自定义H264编码器。 The encoder directshow filter is a part of video processing project http://sourceforge.net/projects/videoprocessing/ The filter class (H264EncoderFilter) is inheriting an ISettingsInterface: 编码器DirectShow过滤器是视频处理项目http://sourceforge.net/projects/videoprocessing/的一部分。过滤器类(H264EncoderFilter)继承了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): 我为过滤器本身创建了一个包装器(在记录的Directshow.NET lib的Uuids.cs中):

[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. 有了它,我可以在C#中实例化过滤器类,也可以将过滤器强制转换为IBaseFilter接口,因此我猜想此包装器是有效的。

Next, I wanted to create a wrapper for the before mentioned ISettingsInterface (I added the code below to AxCore.cs): 接下来,我想为前面提到的ISettingsInterface创建一个包装器(我在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. 当我使用SetParameter函数时,它似乎表现良好(返回的Hresult为0),但是当我使用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 控制台输出: http : //img707.imageshack.us/img707/3667/consolevd.png

Observations: 观察结果:

  • szParamValue should be a string containg "15", because it was set that way by SetParameter. szParamValue应该是包含“ 15”的字符串,因为它是通过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" nLength是SetParameter中包含的字符串的长度,这是正确的,因为期望的“ 15”的长度为2。当质量设置为例如“ 151”时,它将变为3。
  • 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. szParamValue并不总是那么混乱,有时它是一个空字符串或一些XML代码...而且,更重要的是,AccessViolationException与GetParameter调用一起抛出。 The exception details attached below. 异常详细信息如下。
  • Address od szParamValue in memory changes, as you can see in the console output. 如您在控制台输出中所见,内存中的od szParamValue地址更改。

Exception details: 异常详细信息:

System.AccessViolationException was unhandled Message=Attempted to read or write protected memory. 未处理System.AccessViolationException Message =尝试读取或写入受保护的内存。 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: Source = DirectShowLib-2005 StackTrace:位于DirectShowLib.ISettingsInterface.GetParameter(字符串类型,Int32缓冲区大小,字符串和值,整数32和长度),位于F:\\ Documents \\ Visual Studio 2010 \\ Projects \\中的ConsoleApplication4.Program.Main(String [] args)中。 ConsoleApplication4 \\ ConsoleApplication4 \\ Program.cs:System.AppDomain._nExecuteAssembly(装配体,String [] args)位于System.AppDomain.ExecuteAssembly(字符串assemblyFile,证据AssemblySecurity,String [] args)位于Microsoft.VisualStudio.HostingProcess。 System.Threading.ThreadHelper.ThreadStart_Context(对象状态)处于System.Threading.ExecutionContext.Run(System.Threading.ThreadHelper.ThreadStart()处的HostProc.RunUsersAssembly()状态)

Questions: First one is obvious- what am I doing wrong? 问题:第一个很明显-我在做什么错? ;) Second, how does .NET marshaler or anything know if I wrote the interface correctly? ;)其次,.NET封送处理程序或任何其他内容如何知道我是否正确编写了接口? How does it know which function to call from the original c++ interface? 它如何知道从原始c ++接口调用哪个函数? 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. 它不能按名称识别它们(我试图用一个错字SetParam代替SetParameter并起作用),但是当我在接口中交换功能顺序时它失败了。

PS I can attach any code you want (or you can download also download it) because videoprocessing project is open source, as directshow.net. PS我可以附加您想要的任何代码(或者也可以下载也可以下载它),因为videoprocessing项目是开源的,如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", "..."); 编辑:SetParameter确实有效,因为我创建了一个滤镜摄影机-> h264->解码器->渲染器,并且仅使用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. 您使用错误的类型来整理GetParameter中的value(3rd)参数。 instead of string use StringBuilder. 代替字符串使用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 不知道buffersize参数是做什么的,但是我可以将其设置为0,并且该方法仍返回预期值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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