1

h264 エンコーダー用の基本的な C# ラッパーを作成しようとしています。Directshow.NET といくつかのカスタム H264 エンコーダーを使用しています。エンコーダーの 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;

};

フィルター自体のラッパーを作成しました (記録のために、Directshow.NET lib の Uuids.cs 内):

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

これにより、C# でフィルター クラスをインスタンス化でき、IBaseFilter インターフェイスでフィルターをキャストできるので、このラッパーが機能すると思います。

次に、前述の 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
        );
}

これは私の問題につながります。インターフェイスを使用しようとすると、すべてが機能するわけではありません。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());
            }
        }

コンソール出力: http://img707.imageshack.us/img707/3667/consolevd.png

所見:

  • szParamValue は、SetParameter によってそのように設定されているため、"15" を含む文字列である必要があります。代わりに、それは混乱です。
  • nLength は SetParameter に含まれる文字列の長さです。予想される "15" の長さは 2 であるため、これは正しいです。たとえば、品質が "151" に設定されると、3 に変わります。
  • szParamValue は常に混乱しているとは限りません。空の文字列や XML コードの場合もあります。さらに重要なのは、GetParameter 呼び出しに沿って AccessViolationException がスローされることです。例外の詳細は以下に添付されています。
  • コンソール出力でわかるように、メモリ変更のアドレス od szParamValue。

例外の詳細:

System.AccessViolationException は処理されませんでした Message=保護されたメモリを読み書きしようとしました。これは多くの場合、他のメモリが破損していることを示しています。Source=DirectShowLib-2005 StackTrace: 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:Microsoft.VisualStudio.HostingProcess の System.AppDomain.ExecuteAssembly(String assemblyFile、Evidence assemblySecurity、String[] args) の System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) の 79 行目。 System.Threading の System.Threading.ThreadHelper.ThreadStart_Context(オブジェクト状態) で HostProc.RunUsersAssembly()。

質問: 最初のものは明白です - 私は何を間違っていますか? ;) 次に、.NET マーシャラーなどは、私がインターフェイスを正しく記述したかどうかをどのように認識しますか? 元の C++ インターフェイスからどの関数を呼び出すかをどのように知るのでしょうか? それらを名前で認識しません(SetParameterの代わりにタイプミスSetParamを組み込んでみましたが、機能しました)が、インターフェイスで関数の順序を入れ替えると失敗しました。

PS videoprocessing プロジェクトは directshow.net のようにオープン ソースであるため、任意のコードを添付できます (または、ダウンロードしてダウンロードすることもできます)。私が作成したコードはすべてここにあります。

前もって感謝します。

編集: SetParameter は実際に機能します。フィルター グラフ カメラ -> h264 -> デコーダー -> レンダラーを作成し、SetParameter("quality", "..."); でのみ再生したためです。そして、予想された明確に目に見える反応がありました。

4

1 に答える 1

0

GetParameter で値 (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 に設定してもメソッドは期待値を返します

于 2015-02-20T22:32:47.373 に答える