DSPACKコンポーネントライブラリを使用してDelphi6で記述されたDirectShowプッシュソースビデオフィルターがあります。フィルタを使用しているSkypeクライアントが5.x以降でない限り、Skype通話中にフィルタは正常に実行されます。5.xでは、Skypeクライアントがハングするまで非常に遅くなり、データ実行防止の警告や一般的なMicrosoftの「このプログラムがクラッシュしました」ダイアログボックスなど、さまざまな悪いクラッシュが発生します。すぐにクラッシュする場合もあれば、通話開始から約30秒以上後にクラッシュする場合もあります。
次のコンテキストでは、エラーなしでビデオフィルターを実行することもできます。
- Skypeで使用するビデオデバイスを選択するときに表示されるビデオフィルタープレビューウィンドウでSkype5.xを使用します(通話ではなく、[ビデオオプションの選択]ダイアログページで)。
- Skype 4.xクライアント(通話中および通話中に完全に実行されます)
- グラフ編集
- DSPACKTVideoWindowインスタンス
- ウェブカメラフィードを利用する他のプログラム
私はWebでいくつかの調査を行い、Skype5.xとクラッシュに関するかなりの数の苦情を見つけました。私が読んだスレッドは、5.7ベータ版のダウンロードを提案しました。私はそれを試しましたが、役に立ちませんでした。動作は少し良くなりますが、それでもクラッシュします。
必要最低限のテストとして、FillBuffer()メソッドを変更して、通常Skypeに中継する外部ビデオストリームの代わりに、起動時にロードする静的ビットマップを配信するようにしました。それでもクラッシュします。また、FastMM4を設定してプッシュソースフィルターDLLを実行し、FillBuffer()呼び出しとメディアサンプルをダウンストリームピンに配信する呼び出しごとにフルメモリスキャンを実行しようとしました。エラーは一切ありません。
Skypeは明らかに他のWebカメラドライバーと連携するか、Webで大きな抗議が発生する可能性があるため、フィルターが気に入らないことを何をしている可能性がありますか?
更新:さらにテストしたところ、奇妙なことに遭遇しました。元々、私のフィルターでのGetMediaType()呼び出しには4つの形式がありました。私はそれを1つの形式にノックダウンしました。圧縮がBI_RGBに設定された24ビットです。これは、外部で受信してからSkypeに渡すものだからです。ログイン後にDirectShowフィルタースキャンを実行すると、Skypeからすぐに障害が発生し始め、GetStreamCaps()の呼び出し中に障害が発生しました。Skypeにはデバッグ防止コードがあるため、すべての行の後にGetStreamCaps()呼び出しにトレースメッセージを入念に追加し、そのメディア形式変数に最初にアクセスしようとしたときに発生することを発見しました(以下を参照)。)。SkypeがDirectShowフィルターに渡すメモリ領域にアクセスできないようです。以前の4つに対して提供するメディア形式が1つしかないために、障害がより早く発生する理由は不明です。
これは私の側のまったくの推測ですが、Skypeと私のフィルターの間で何らかの奇妙なメモリエリアアクセス権限エラーが発生している可能性はありますか?Skypeが、通話を開始する前にデータ実行防止エラーを報告することがあり、他の一般的なクラッシュもあるという事実から、何かエキゾチックなことが起こっているのではないかと思います。コードブロックとしてマークされた領域に書き込もうとすると、DEPエラーが発生します。Skypeが私に渡すポインターが、私が書き込めない奇妙な場所や保護された場所を指しているようです。
要約すると、SkypeがGetStreamCaps()を呼び出すときにDirectShowフィルターにアクセスするたびに、通話を開始する前に、またはビデオデバイスの選択画面にアクセスする前に、エラーが100%発生するようになりました。関連するコードスニペットは次のとおりです。
function TBCPushPinDesktop.GetStreamCaps(iIndex: Integer; out ppmt: PAMMediaType; out pSCC): HResult;
var
pvi:PVIDEOINFOHEADER;
begin
ppmt := CreateMediaType(@Fmt);
pvi:=PVIDEOINFOHEADER(ppmt.pbFormat);
// Error occurs at THIS statement, the first attempt to write to the memory area
// provided by Skype.
pvi.bmiHeader.biCompression := BI_RGB;
.... SNIP ....
end;
更新2:コードに問題がありますが、何がわかりません。Graph Editは、SkypeのようにGetStreamCaps()を呼び出しません。さらにいくつかのトレースステートメントを追加しましたが、上記のコードでは、DSPACK CreateMediaType()呼び出しによって返されるメディアタイプオブジェクトにNIL pbFormatフィールドがあるため、高速障害を説明していることがわかりました。適切に構成されたpbFormatフィールドを取得するために私が何をする必要があるかを誰かが知っている場合は、私に知らせてください。以下は、CreateMediaType()操作を実行するDSPACKのコードです。
// this also comes in useful when using the IEnumMediaTypes interface so
// that you can copy a media type, you can do nearly the same by creating
// a CMediaType object but as soon as it goes out of scope the destructor
// will delete the memory it allocated (this takes a copy of the memory)
function CreateMediaType(pSrc: PAMMediaType): PAMMediaType;
var pMediaType: PAMMediaType;
begin
ASSERT(pSrc<>nil);
// Allocate a block of memory for the media type
pMediaType := CoTaskMemAlloc(sizeof(TAMMediaType));
if (pMediaType = nil) then
begin
result := nil;
exit;
end;
// Copy the variable length format block
CopyMediaType(pMediaType,pSrc);
result := pMediaType;
end;
//----------------------------------------------------------------------------
// Copies a task-allocated AM_MEDIA_TYPE structure.
//----------------------------------------------------------------------------
procedure CopyMediaType(pmtTarget: PAMMediaType; pmtSource: PAMMediaType);
begin
// We'll leak if we copy onto one that already exists - there's one
// case we can check like that - copying to itself.
ASSERT(pmtSource <> pmtTarget);
//pmtTarget^ := pmtSource^;
move(pmtSource^, pmtTarget^, SizeOf(TAMMediaType));
if (pmtSource.cbFormat <> 0) then
begin
ASSERT(pmtSource.pbFormat <> nil);
pmtTarget.pbFormat := CoTaskMemAlloc(pmtSource.cbFormat);
if (pmtTarget.pbFormat = nil) then
pmtTarget.cbFormat := 0
else
CopyMemory(pmtTarget.pbFormat, pmtSource.pbFormat, pmtTarget.cbFormat);
end;
if (pmtTarget.pUnk <> nil) then pmtTarget.pUnk._AddRef;
end;