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

本文关键字:创建 包装 编码器 h264 自定义 DirectShow | 更新日期: 2023-09-27 18:14:03

我正在尝试为h264编码器创建一些基本的c#包装器。我正在使用Directshow。. NET和一些自定义的H264编码器。编码器directshow过滤器是视频处理项目http://sourceforge.net/projects/videoprocessing/的一部分过滤器类(H264EncoderFilter)继承了一个isetingsinterface:

//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的uuid .cs中)。. NET库,记录):

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

有了它,我可以在c#中实例化过滤器类,也可以在IBaseFilter接口上转换过滤器,所以我猜这个包装器是有效的。

接下来,我想为前面提到的isetingsinterface创建一个包装器(我将下面的代码添加到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
        );
}
这就引出了我的问题。当我尝试使用这个界面时,并不是一切都能正常工作。当我使用SetParameter函数时,它似乎表现得很好(返回的结果是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());
            }
        }

控制台输出:http://img707.imageshack.us/img707/3667/consolevd.png

观察:

  • szParamValue应该是一个包含"15"的字符串,因为它是设置的通过SetParameter。相反,它是一团乱。
  • nLength是SetParameter中包含的字符串的长度,这是正确的,因为预期的"15"的长度是2。当质量设置为例如"151"时,它将变为3
  • szParamValue并不总是如此混乱,有时它是一个空字符串或一些XML代码…更重要的是,AccessViolationException是与GetParameter调用一起抛出的。例外细节附在下面。
  • 地址id szParamValue在内存中发生变化,您可以在控制台输出中看到。

异常细节:

系统。未处理AccessViolationException消息=试图读写受保护的内存。这通常表明其他内存已损坏。源= directshowlib - 2005加:在DirectShowLib.ISettingsInterface。GetParameter(字符串类型,Int32 buffersize,字符串&价值,Int32&长度)在ConsoleApplication4.Program。Main(String[] args) in F:'Documents'Visual Studio 2010'Projects'ConsoleApplication4'ConsoleApplication4'Program.cs:line 79在System.AppDomain。_nExecuteAssembly(Assembly Assembly, String[] args)在System.AppDomain。ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly ()在System.Threading.ThreadHelper。ThreadStart_Context(对象状态)在System.Threading.ExecutionContext。运行(ExecutionContext, ExecutionContext, ContextCallback, callback, Object state)在System.Threading.ThreadHelper.ThreadStart ()InnerException:

问题:第一个很明显——我做错了什么?,)其次,.NET编组器或其他程序如何知道我是否正确编写了接口?它如何知道从原始c++接口调用哪个函数?它不能通过名称识别它们(我试图合并一个错字- SetParam而不是SetParameter并且它工作),但当我交换接口中的函数顺序时,它失败了。

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

提前谢谢你。

编辑:SetParameter确实有效,因为我创建了一个过滤器图形相机-> h264 ->解码器->渲染器,只使用SetParameter("质量","…");出现了预期的、明显可见的反应。

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

您使用了错误的类型来封送GetParameter中的value (3rd)参数。使用StringBuilder代替string。像下面的

[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,该方法仍然返回预期的值