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

如何为自定义DirectShow h264编码器创建C#包装器,c#,marshalling,directshow,video-processing,directshow.net,C#,Marshalling,Directshow,Video Processing,Directshow.net,我正在尝试为h264编码器创建一些基本的C#包装器。 我正在使用Directshow.NET和一些自定义H264编码器。 编码器directshow过滤器是视频处理项目的一部分 筛选器类(H264EncoderFilter)正在继承ISettingsInterface: //For Smart pointers DEFINE_GUID( IID_ISettingsInterface, /* 388EEF20-40CC-4752-A0FF-66AA5C4AF8FA */ 0x388

我正在尝试为h264编码器创建一些基本的C#包装器。 我正在使用Directshow.NET和一些自定义H264编码器。 编码器directshow过滤器是视频处理项目的一部分 筛选器类(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;

};
我为过滤器本身创建了一个包装器(在Directshow.NET lib的Uuids.cs中,用于记录):

有了它,我可以在C#中实例化filter类,还可以在IBaseFilter接口上强制转换过滤器,所以我猜这个包装器可以工作

接下来,我想为前面提到的ISettingsInterface创建一个包装器(我将下面的代码添加到AxCore.cs中):

这就引出了我的问题。当我尝试使用这个界面时,并不是所有的东西都能工作。 当我使用SetParameter函数时,它的行为似乎正常(Hresult返回值为0),但当我使用GetParameter时,出现了一些错误。请查看测试代码及其控制台输出:

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());
            }
        }
控制台输出:

意见:

  • szParamValue应该是一个包含“15”的字符串,因为它已设置 通过SetParameter可以实现这种方式。相反,这是一片混乱
  • nLength是SetParameter中包含的字符串长度,这是正确的,因为预期的“15”的长度为2。当质量设置为例如“151”时,它将更改为3
  • szParamValue并不总是如此混乱,有时它是一个空字符串或一些XML代码。。。更重要的是,AccessViolationException与GetParameter调用一起抛出。例外情况详情见下文
  • 正如您在控制台输出中看到的,内存中的地址和值会发生变化
例外情况详情:

System.AccessViolationException未处理 消息=尝试读取或写入受保护内存。这通常表示其他内存已损坏。 Source=DirectShowLib-2005 堆栈跟踪: 在DirectShowLib.ISettingsInterface.GetParameter(字符串类型、Int32 buffersize、字符串和值、Int32和长度) 位于F:\Documents\Visual Studio 2010\Projects\ConsoleApplication4\ConsoleApplication4\Program.Main中的ConsoleApplication4.Program(字符串[]args):第79行 位于System.AppDomain.\u nexecutestAssembly(程序集,字符串[]args) 位于System.AppDomain.ExecuteAssembly(字符串汇编文件、证据汇编安全性、字符串[]args) 在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()上 位于System.Threading.ThreadHelper.ThreadStart\u上下文(对象状态) 在System.Threading.ExecutionContext.Run(ExecutionContext ExecutionContext,ContextCallback回调,对象状态) 位于System.Threading.ThreadHelper.ThreadStart()处 内部异常:

问题:第一个很明显——我做错了什么其次,.NET封送处理程序或任何东西如何知道我是否正确编写了接口?它如何知道从原来的C++接口调用哪个函数?它无法通过名称识别它们(我试图合并一个typo-SetParam而不是SetParameter,它成功了),但在我交换接口中的函数顺序时失败了

PS我可以附加任何你们想要的代码(或者你们也可以下载),因为视频处理项目是开源的,就像directshow.net一样。我创建的代码都在这里

先谢谢你

编辑:
SetParameter确实可以工作,因为我创建了一个过滤图摄影机->h264->解码器->渲染器,并且只使用SetParameter(“质量”和“…”)播放;出现了一个预期的、清晰可见的反应。

您在整理GetParameter中的value(第3个)参数时使用了错误的类型。使用StringBuilder代替字符串。如下

[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);
使用它的一个例子是

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

不确定buffersize参数的作用,但我可以将其设置为0,并且该方法仍然返回预期值

找到解决方案了吗?
[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);
StringBuilder sb = new StringBuilder();
int len = int.MinValue;
((ISettingsInterface)enc).GetParameter("quality", 0, sb, ref len);
string value = sb.ToString();