ホーム >プログラム >Delphi 6 ローテクTips

DirectShowを利用して再生・その3


前回は再生、一時停止、停止ボタンとシークバーをつけました。

今回は連続再生を行います。
ビデオの表示は別ウィンドウのままです。


イベント取得

連続再生を行うには今再生しているメディアが終わりに達したら次のファイルを開いて再生を始めるということをします。
そのためには再生が終了したことを知る必要があります。
タイマーを使って逐一再生位置を取得してメディアの長さと一致したら終わりに達したと判断するという方法でも良いのですがもっとスマートな方法があります。

 終了イベントの取得です。

メディアが終わりに達したら知らせてくれるという便利な機能です。
その終了イベント取得するにはIMediaEventExインターフェースを使います。

const
  //イベントを受け取るためのユニークなメッセージ番号
  WM_GRAPH_NOTIFY = (WM_APP +1);

type
  TForm1 = class(TForm)
  ...

  private
    { Private 宣言 }
    F_GraphBuilder  : IGraphBuilder;
    F_MediaEventEx  : IMediaEventEx;

    //イベント通知のためのメッセージ番号を受け取れるようにする
    procedure WMGraphNotify(var Msg: TMessage); message WM_GRAPH_NOTIFY;
  ...

procedure TForm1.FormCreate(Sender: TObject);
begin
  //グラフ作成
  CoCreateInstance(
    CLSID_FilterGraph,
    nil,
    CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder,
    F_GraphBuilder
  );

  //イベント通知ウィンドウをセット
  F_GraphBuilder.QueryInterface(IMediaEventEx, F_MediaEventEx);
  F_MediaEventEx.SetNotifyWindow(Self.Handle, WM_GRAPH_NOTIFY, 0);

  ...

procedure TForm1.WMGraphNotify(var Msg: TMessage);
//イベント処理
var
  li_EventCode : Longint;
  li_Param1    : Longint;
  li_Param2    : Longint;
begin
  if (F_MediaEventEx = nil) then begin
    Exit;
  end;

  // イベントを全て取得
  while (Succeeded(F_MediaEventEx.GetEvent(li_EventCode, li_Param1, li_Param2, 0))) do begin
    case (li_EventCode) of
      EC_COMPLETE //再生が終わりに来た
      :begin
        //連続再生のための処理をここに書く
      end;
    end;
    F_MediaEventEx.FreeEventParams(li_EventCode, li_Param1, li_Param2);
  end;
end;

IMediaEventExインターフェースはフォームのprivate部でF_MediaEventExとして宣言しておきます。
イベントはSetNotifyWindowメソッドで指定したウィンドウで受け取ることができます。
SetNotifyWindowは第一引数にメッセージを受け取るウィンドウハンドル、第二引数にメッセージの番号を指定します。
第三引数は特に必要がなければ(大抵は必要ないと思います)0で構いません。
メッセージの番号は他のメッセージと区別できるようにしなければなりません。
そこでWM_GRAPH_NOTIFYという識別子をグローバルで宣言して値にWM_APP +1を割り当てておきます。
メッセージを受け取るウィンドウ(今回はForm1)はメッセージハンドラでこのWM_GRAPH_NOTIFYを受け取れるようにしておきます。

このWM_GRAPH_NOTIFYという識別子はソースコードで区別がつけば良いものなのでエラーにならなければ何でも構いません。
値も必ずWM_APP +1である必要はなく他のメッセージ番号とかぶらなければ(WM_APP+1〜WM_APP+16383の間であれば)何でもかまいません。

イベント処理ではGetEventメソッドでイベントを取得します。
GetEventの第一引数にはイベントの種類を表す値がセットされます。
第二引数と第三引数にはイベント種類に応じた追加情報を表す値がセットされます。
そして第一引数の値をチェックしてEC_COMPLETEならメディアの終了に達したということになります。
ここに連続再生を行う処理を書けばOKとなります。

ソースコード

dplay_3.zip ソースコードと実行ファイル詰め合わせ。

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls,
  DirectShow9;

const
  //イベントを受け取るためのユニークなメッセージ番号
  WM_GRAPH_NOTIFY = (WM_APP +1);

type
  TForm1 = class(TForm)
    ComboBox1:      TComboBox;
    Button_Open:    TButton;
    Button_Delete:  TButton;
    Button_Clear:   TButton;
    Button_Play:    TButton;
    Button_Pause:   TButton;
    Button_Stop:    TButton;
    TrackBar1:      TTrackBar;
    Label_Time:     TLabel;
    Label_Size:     TLabel;
    CheckBox_Event: TCheckBox;
    Timer1:         TTimer;
    OpenDialog1:    TOpenDialog;
    procedure FormCreate (Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button_OpenClick  (Sender: TObject);
    procedure Button_DeleteClick(Sender: TObject);
    procedure Button_ClearClick (Sender: TObject);
    procedure Button_PlayClick  (Sender: TObject);
    procedure Button_PauseClick (Sender: TObject);
    procedure Button_StopClick  (Sender: TObject);
    procedure ComboBox1Select   (Sender: TObject);
    procedure TrackBar1Change   (Sender: TObject);
    procedure Timer1Timer       (Sender: TObject);
  private
    { Private 宣言 }
    F_GraphBuilder  : IGraphBuilder;
    F_MediaEventEx  : IMediaEventEx;
    F_MediaPosition : IMediaPosition;
    F_fDuration     : TRefTime;

    //イベント通知のためのメッセージ番号を受け取れるようにする
    procedure WMGraphNotify(var Msg: TMessage); message WM_GRAPH_NOTIFY;
    procedure F_Close;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation
uses
  ActiveX,
  debug_msg; //デバッグ出力用

{$R *.dfm}


procedure TForm1.FormCreate(Sender: TObject);
begin
  Label_Size.Caption := '';
  Label_Time.Caption := '';

  //グラフ作成
  CoCreateInstance(
    CLSID_FilterGraph,
    nil,
    CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder,
    F_GraphBuilder
  );

  //イベント通知ウィンドウをセット。
  F_GraphBuilder.QueryInterface(IMediaEventEx, F_MediaEventEx);
  F_MediaEventEx.SetNotifyWindow(Self.Handle, WM_GRAPH_NOTIFY, 0);

  //時間取得のため
  F_GraphBuilder.QueryInterface(IMediaPosition, F_MediaPosition);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  F_Close;
  F_MediaPosition := nil;
  F_MediaEventEx  := nil;
  F_GraphBuilder  := nil;
end;

procedure TForm1.Button_PlayClick(Sender: TObject);
//再生開始
var
  l_MediaControl : IMediaControl;
begin
  if (Assigned(F_GraphBuilder)) then begin
    F_GraphBuilder.QueryInterface(IMediaControl, l_MediaControl);
    l_MediaControl.Run;
    l_MediaControl := nil;

    Timer1.Enabled := True;
  end;
end;

procedure TForm1.Button_PauseClick(Sender: TObject);
//一時停止
var
  l_MediaControl : IMediaControl;
begin
  Timer1.Enabled := False;
  if (Assigned(F_GraphBuilder)) then begin
    F_GraphBuilder.QueryInterface(IMediaControl, l_MediaControl);
    l_MediaControl.Pause;
    l_MediaControl := nil;
  end;
end;

procedure TForm1.Button_StopClick(Sender: TObject);
//停止
var
  l_MediaControl : IMediaControl;
begin
  Timer1.Enabled := False;
  if (Assigned(F_GraphBuilder)) then begin
    F_GraphBuilder.QueryInterface(IMediaControl, l_MediaControl);
    l_MediaControl.Stop;

    //ただ停止させただけではPauseと変わらない
    //先頭に戻すにはIMediaPositionを使う
    F_MediaPosition.put_CurrentPosition(0);

    //移動させただけでは現在位置のフレームが描画されない。
    //現在位置のフレームを描画させるためにPauseあるいはStopWhenReadyを呼ぶ。
    l_MediaControl.StopWhenReady;
    l_MediaControl := nil;

    Timer1.Tag := 1;
    TrackBar1.Position := 0;
    Timer1.Tag := 0;
  end;
end;

//--- シークバー ---
procedure TForm1.Timer1Timer(Sender: TObject);
//現在位置表示
  function FloatToTime(fTime : TRefTime) : String;
  var
    li_Time, li_Hour, li_Min, li_Sec : Integer;
  begin
    li_Time := Trunc(fTime);
    li_Hour := li_Time div 60 div 60;
    li_Min  := li_Time div 60 mod 60;
    li_Sec  := li_Time mod 60;
    Result  := Format('%d:%.2d:%.2d', [li_Hour, li_Min, li_Sec]);
  end;
  function IsDragThumb: Boolean;
  //トラックバーのスライダーを触っているか。
  //実際はトラックバーがマウスキャプチャしているか。
  begin
    if not(BOOL(Hi(GetAsyncKeyState(VK_LBUTTON)))) then begin
      Result := False;
      Exit;
    end;
    Result := (GetCapture = TrackBar1.Handle);
  end;
var
  lf_Pos : TRefTime;
begin
  if (Assigned(F_GraphBuilder)) then begin
    Timer1.Tag := 1;

    F_MediaPosition.get_CurrentPosition(lf_Pos);
    Label_Time.Caption := Format('%s/%s', [FloatToTime(lf_Pos), FloatToTime(F_fDuration)]);

    if not(IsDragThumb) then begin
      //トラックバーのスライダーを触っていなかったらスライダーを現在位置に移動させる。
      TrackBar1.Position := Trunc(lf_Pos);
    end;

    Timer1.Tag := 0;
  end;
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
//トラックバー移動
var
  l_MediaPosition : IMediaPosition;
begin
  if (Timer1.Tag <> 0) then begin
    Exit;
  end;

  if (Assigned(F_GraphBuilder)) then begin
    if not(BOOL(Hi(GetAsyncKeyState(VK_LBUTTON)))) then begin
      F_GraphBuilder.QueryInterface(IMediaPosition, l_MediaPosition);
      l_MediaPosition.put_CurrentPosition(TrackBar1.Position);
      l_MediaPosition := nil;
    end;
  end;
end;
//------------------


procedure TForm1.WMGraphNotify(var Msg: TMessage);
//イベント処理
var
  li_EventCode   : Longint;
  li_Param1      : Longint;
  li_Param2      : Longint;
begin
  if (F_MediaEventEx = nil) then begin
Exit;
  end;

  // イベントを全て取得
  while (Succeeded(F_MediaEventEx.GetEvent(li_EventCode, li_Param1, li_Param2, 0))) do begin
    if (CheckBox_Event.Checked) then begin
      debug_msg.ShowEvent(li_EventCode, li_Param1, li_Param2);
    end;

    case (li_EventCode) of
      EC_COMPLETE
      :begin
        //再生が終わりに来た
        //連続再生
        if (ComboBox1.Items.Count = 0) then begin
          F_Close;
        end else begin
          if (ComboBox1.ItemIndex < ComboBox1.Items.Count -1) then begin
            ComboBox1.ItemIndex := ComboBox1.ItemIndex +1;
          end else begin
            ComboBox1.ItemIndex := 0;
          end;
          ComboBox1Select(nil);
        end;
      end;
    end;
    F_MediaEventEx.FreeEventParams(li_EventCode, li_Param1, li_Param2);
  end;
end;

procedure TForm1.F_Close;
var
  l_MediaControl : IMediaControl;
  l_EnumFilters  : IEnumFilters;
  l_BaseFilter   : IBaseFilter;
begin
  Timer1.Enabled     := False;
  Label_Size.Caption := '';
  Label_Time.Caption := '';

  if (F_GraphBuilder <> nil) then begin
    F_GraphBuilder.QueryInterface(IMediaControl, l_MediaControl);
    l_MediaControl.Stop;
    l_MediaControl := nil;

    //Sotpしただけではフィルタはまだ接続されたままなのでフィルタを列挙して削除する。
    //http://msdn.microsoft.com/ja-jp/library/cc973418.aspx
    F_GraphBuilder.EnumFilters(l_EnumFilters);
    while (l_EnumFilters.Next(1, l_BaseFilter, nil) = S_OK) do begin
      F_GraphBuilder.RemoveFilter(l_BaseFilter);
      l_BaseFilter := nil;
      l_EnumFilters.Reset;
    end;
    l_EnumFilters := nil;
  end;
end;

procedure TForm1.Button_OpenClick(Sender: TObject);
//開く
var
  li_Count : Integer;
begin
  if (OpenDialog1.Execute) then begin
    li_Count := ComboBox1.Items.Count;
    ComboBox1.Items.AddStrings(OpenDialog1.Files);
    ComboBox1.ItemIndex := li_Count;
    ComboBox1Select(nil); //再生開始
  end;
end;

procedure TForm1.Button_DeleteClick(Sender: TObject);
//リストから再生中のファイルを削除
var
  li_Index : Integer;
begin
  li_Index := ComboBox1.ItemIndex;
  F_Close;
  ComboBox1.Items.Delete(li_Index);
  ComboBox1.ItemIndex := li_Index;
  ComboBox1Select(nil);
end;

procedure TForm1.Button_ClearClick(Sender: TObject);
//リストクリア
begin
  ComboBox1.Items.Clear;
end;

procedure TForm1.ComboBox1Select(Sender: TObject);
//ファイル選択→再生
var
  ls_FileName  : String;
  l_BasicVideo : IBasicVideo;
  li_Width     : Integer;
  li_Height    : Integer;
begin
  F_Close;

  if (ComboBox1.Items.Count = 0) then begin
    Exit;
  end;

  if (ComboBox1.ItemIndex < 0) then begin
    ComboBox1.ItemIndex := 0;
  end;
  ls_FileName := ComboBox1.Items[ComboBox1.ItemIndex];

  //読み込み
  if not(Succeeded(F_GraphBuilder.RenderFile(POLESTR(WideString(ls_FileName)), nil))) then begin
    ShowMessage(ls_FileName + #13'は開けません');
    ComboBox1.Items.Delete(ComboBox1.ItemIndex);
    ComboBox1.Text := '';
    Exit;
  end;

  //タイトルバーにファイル名を表示
  Caption := ExtractFileName(ls_FileName);

  //ビデオのサイズを取得
  F_GraphBuilder.QueryInterface(IBasicVideo, l_BasicVideo);
  //li_Widthとli_Heightの初期化は必須。
  li_Width  := 0;
  li_Height := 0;
  l_BasicVideo.get_VideoWidth (li_Width);
  l_BasicVideo.get_VideoHeight(li_Height);
  l_BasicVideo := nil;
  if (li_Width > 0) and (li_Height > 0) then begin
    Label_Size.Caption := Format('%d×%d', [li_Width, li_Height]);
  end else begin
    Label_Size.Caption := '';
  end;

  //長さ取得
  F_fDuration := 0;
  F_MediaPosition.get_Duration(F_fDuration); //この後CurrentPositionが変わってしまうことがある
  TrackBar1.Max := Trunc(F_fDuration);

  //長さを取得した後CurrentPositionが終わりに移動してしまうことがあるので頭に戻す
  //F_MediaPosition.put_CurrentPosition(0);
  //Button_StopClickの中でやっている
  //再生開始
  Button_StopClick(nil);
  Button_PlayClick(nil);
end;


end.

デバッグ出力

イベントの確認用にデバッグ出力用のフォームを作ってみました。
Form1にMemoを置いて書き込んでも良いのですが、別フォームにして分けた方がコードも分離されて楽なので分けました。

今回はイベントの出力にしか使っていませんが、ソースコード中の気になる所にデバッグ用の出力コードを書けば動かしながら値を確認できて便利です。

unit debug_msg;

interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

procedure ShowDbg(AValue : array of Variant); overload;
procedure ShowDbg(sValue : String); overload;

procedure ShowSuc(hRes : HResult; sSuc, sFail : String);

//イベント
procedure ShowEvent(iEventCode, iParam1, iParam2: Longint);



type
  TForm_Debug = class(TForm)
    ListBox1:     TListBox;
    Button_Clear: TButton;
    procedure Button_ClearClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form_Debug: TForm_Debug;

implementation
uses
  ActiveX,
  DirectShow9,
  WMF9;


{$R *.dfm}


procedure TForm_Debug.Button_ClearClick(Sender: TObject);
begin
  ListBox1.Items.Clear;
end;

procedure _DbgAdds(sStrs : array of String);
const
  lci_MAXLINE = 1000;
var
  lb_Create : Boolean;
  i         : Integer;
  l_Rect    : TRect;
  li_Page   : Integer;
  ls_Text   : WideString;
  l_Strings : TStrings;
  l_ListBox : TListBox;
begin
  if not(Assigned(Form2)) then begin
    Form_Debug := TForm_Debug.Create(Application);
  end else begin
    lb_Create := True;
    for i := 0 to Screen.FormCount-1 do begin
      if (Screen.Forms[i] is TForm_Debug) then begin
        lb_Create := False;
        Break;
      end;
    end;
    if (lb_Create) then begin
      Form_Debug := TForm_Debug.Create(Application);
    end;
  end;
  Form_Debug.Show;

  l_ListBox := Form_Debug.ListBox1;
  l_Strings := l_ListBox.Items;

  for i := 0 to High(sStrs) do begin
    if (i = 0) then begin
      ls_Text := sStrs[i];
    end else begin
      ls_Text := ls_Text + ' ' + sStrs[i];
    end;
  end;

  //追加
  l_Strings.Add(ls_Text);
  if (l_Strings.Count > lci_MAXLINE) then begin
    for i := 0 to l_Strings.Count - lci_MAXLINE do begin
      l_Strings.Delete(0);
    end;
  end;

  //最後の行を画面に表示
  with l_ListBox do begin
    l_Rect  := ItemRect(0);
    li_Page := Trunc(ClientHeight / (l_Rect.Bottom - l_Rect.Top));
    if (Items.Count <= li_Page) then begin
      TopIndex := 0;
    end else begin
      TopIndex := Count - li_Page;
    end;
  end;
end;


procedure ShowDbg(AValue : array of Variant);
var
  ls_Arg : array of String;
  i      : Integer;
begin
  SetLength(ls_Arg, High(AValue) +1);
  for i := 0 to High(AValue) do begin
    ls_Arg[i] := String(VarAsType(AValue[i], varOleStr));
  end;

  _DbgAdds(ls_Arg);
end;

procedure ShowDbg(sValue : String);
begin
  ShowDbg([sValue]);
end;

procedure ShowSuc(hRes : HResult; sSuc, sFail : String);
var
  ls_Msg : String;
begin
  if (Succeeded(hRes)) then begin
    ls_Msg := sSuc;
  end else begin
    ls_Msg := sFail;
  end;
  ShowDbg([ls_Msg, Format('0x%x', [hRes])]);
end;

procedure ShowEvent(iEventCode, iParam1, iParam2: Longint);
var
  ls_Msg  : String;
  ls_Msg1 : String;
  ls_Msg2 : String;
  li_Len  : Integer;
begin
  case (iEventCode) of
    EC_ACTIVATE: begin
      ls_Msg  := 'EC_ACTIVATE';
      ls_Msg1 := 'ビデオウィンドウがアクティブまたは非アクティブである。';
    end;
    EC_BUFFERING_DATA: begin
      ls_Msg  := 'EC_BUFFERING_DATA';
      ls_Msg1 := 'グラフがデータをバッファリングしている、またはデータのバッファリングを停止した。';
    end;
    EC_BUILT: begin
      ls_Msg  := 'EC_BUILT';
      ls_Msg1 := 'グラフを作成し終えたときにビデオコントロールによって送信される。アプリケーションには転送されない。';
    end;
    EC_CLOCK_CHANGED: begin
      ls_Msg  := 'EC_CLOCK_CHANGED';
      ls_Msg1 := '基準クロックが変更された。';
    end;
    EC_CLOCK_UNSET: begin
      ls_Msg  := 'EC_CLOCK_UNSET';
      ls_Msg1 := 'クロックプロバイダの接続が解除された。';
    end;
    EC_CODECAPI_EVENT: begin
      ls_Msg  := 'EC_CODECAPI_EVENT';
      ls_Msg1 := 'エンコーディングイベントを通知するためにエンコーダによって送信される。';
    end;
    EC_COMPLETE: begin
      ls_Msg  := 'EC_COMPLETE';
      ls_Msg1 := '特定のストリームからのすべてのデータをレンダリングし終えた。';
    end;
    EC_DEVICE_LOST: begin
      ls_Msg  := 'EC_DEVICE_LOST';
      ls_Msg1 := 'プラグアンドプレイデバイスが取り外された、または再び使用できるようになった。';
    end;
    EC_DISPLAY_CHANGED: begin
      ls_Msg  := 'EC_DISPLAY_CHANGED';
      ls_Msg1 := 'ディスプレイモードが変更された。';
    end;
    EC_END_OF_SEGMENT: begin
      ls_Msg  := 'EC_END_OF_SEGMENT';
      ls_Msg1 := 'セグメントの終わりに到達した。';
    end;
    EC_ERROR_STILLPLAYING: begin
      ls_Msg  := 'EC_ERROR_STILLPLAYING';
      ls_Msg1 := 'グラフを実行する非同期コマンドが失敗した。';
    end;
    EC_ERRORABORT: begin
      ls_Msg  := 'EC_ERRORABORT';
      ls_Msg1 := 'エラーのため処理が中止された。';
    end;
    EC_EXTDEVICE_MODE_CHANGE: begin
      ls_Msg  := 'EC_EXTDEVICE_MODE_CHANGE';
      ls_Msg1 := 'サポートされていない。';
    end;
    EC_FULLSCREEN_LOST: begin
      ls_Msg  := 'EC_FULLSCREEN_LOST';
      ls_Msg1 := 'ビデオレンダラがフルスクリーンモードから切り替わろうとしている。';
    end;
    EC_GRAPH_CHANGED: begin
      ls_Msg  := 'EC_GRAPH_CHANGED';
      ls_Msg1 := 'フィルタグラフが変更された。';
    end;
    EC_LENGTH_CHANGED: begin
      ls_Msg  := 'EC_LENGTH_CHANGED';
      ls_Msg1 := 'ソースの長さが変更された。';
    end;
    EC_NEED_RESTART: begin
      ls_Msg  := 'EC_NEED_RESTART';
      ls_Msg1 := 'フィルタがグラフの再開を要求している。';
    end;
    EC_NOTIFY_WINDOW: begin
      ls_Msg  := 'EC_NOTIFY_WINDOW';
      ls_Msg1 := 'フィルタにビデオレンダラのウィンドウを通知する。';
    end;
    EC_OLE_EVENT: begin
      ls_Msg  := 'EC_OLE_EVENT';
      ls_Msg1 := 'フィルタがアプリケーションにテキスト文字列を渡している。';
    end;
    EC_OPENING_FILE: begin
      ls_Msg  := 'EC_OPENING_FILE';
      ls_Msg1 := 'グラフがファイルを開いている、またはファイルを開き終えた。';
    end;
    EC_PALETTE_CHANGED: begin
      ls_Msg  := 'EC_PALETTE_CHANGED';
      ls_Msg1 := 'ビデオパレットが変更された。';
    end;
    EC_PAUSED: begin
      ls_Msg  := 'EC_PAUSED';
      ls_Msg1 := 'ポーズ要求が完了した。';
    end;
    EC_QUALITY_CHANGE: begin
      ls_Msg  := 'EC_QUALITY_CHANGE';
      ls_Msg1 := '品質コントロールのため、グラフがサンプルを削除した。';
    end;
    EC_REPAINT: begin
      ls_Msg  := 'EC_REPAINT';
      ls_Msg1 := 'ビデオレンダラが再描画を要求している。';
    end;
    EC_SEGMENT_STARTED: begin
      ls_Msg  := 'EC_SEGMENT_STARTED';
      ls_Msg1 := '新しいセグメントが開始した。';
    end;
    EC_SHUTTING_DOWN: begin
      ls_Msg  := 'EC_SHUTTING_DOWN';
      ls_Msg1 := 'フィルタグラフが破棄前にシャットダウンしている。';
    end;
    EC_SNDDEV_IN_ERROR: begin
      ls_Msg  := 'EC_SNDDEV_IN_ERROR';
      ls_Msg1 := '入力ピンでオーディオデバイスエラーが発生した。';
    end;
    EC_SNDDEV_OUT_ERROR: begin
      ls_Msg  := 'EC_SNDDEV_OUT_ERROR';
      ls_Msg1 := '出力ピンでオーディオデバイスエラーが発生した。';
    end;
    EC_STARVATION: begin
      ls_Msg  := 'EC_STARVATION';
      ls_Msg1 := 'フィルタは十分なデータを受け取っていない。';
    end;
    EC_STATE_CHANGE: begin
      ls_Msg  := 'EC_STATE_CHANGE';
      ls_Msg1 := 'フィルタグラフが変更された。';
    end;
    EC_STEP_COMPLETE: begin
      ls_Msg  := 'EC_STEP_COMPLETE';
      ls_Msg1 := 'コマ送りを実行しているフィルタが、指定数のコマ送りを完了した。';
    end;
    EC_STREAM_CONTROL_STARTED: begin
      ls_Msg  := 'EC_STREAM_CONTROL_STARTED';
      ls_Msg1 := 'ストリーム制御開始コマンドが有効になった。';
    end;
    EC_STREAM_CONTROL_STOPPED: begin
      ls_Msg  := 'EC_STREAM_CONTROL_STOPPED';
      ls_Msg1 := 'ストリーム制御停止コマンドが有効になった。';
    end;
    EC_STREAM_ERROR_STILLPLAYING: begin
      ls_Msg  := 'EC_STREAM_ERROR_STILLPLAYING';
      ls_Msg1 := 'ストリーム中にエラーが発生した。ストリームは引き続き再生中である。';
    end;
    EC_STREAM_ERROR_STOPPED: begin
      ls_Msg  := 'EC_STREAM_ERROR_STOPPED';
      ls_Msg1 := 'エラーのためストリームが停止した。';
    end;
    EC_TIMECODE_AVAILABLE: begin
      ls_Msg  := 'EC_TIMECODE_AVAILABLE';
      ls_Msg1 := 'サポートされていない。';
    end;
    EC_UNBUILT: begin
      ls_Msg  := 'EC_UNBUILT';
      ls_Msg1 := 'グラフを破棄し終えたときにビデオコントロールによって送信される。アプリケーションには転送されない。';
    end;
    EC_USERABORT: begin
      ls_Msg  := 'EC_USERABORT';
      ls_Msg1 := 'ユーザーが再生を強制終了した。';
    end;
    EC_VIDEO_SIZE_CHANGED: begin
      ls_Msg  := 'EC_VIDEO_SIZE_CHANGED';
      ls_Msg1 := 'ネイティブビデオサイズが変更された。';
      ls_Msg2 := Format('%d×%d', [LoWord(iParam1), HiWord(iParam1)]);
    end;
    EC_VMR_RENDERDEVICE_SET: begin
      ls_Msg  := 'EC_VMR_RENDERDEVICE_SET';
      ls_Msg1 := 'VMRがレンダリングメカニズムを選択し終えた。';
      case iParam1 of
        VMR_RENDER_DEVICE_OVERLAY: ls_Msg2 := 'VMR はオーバーレイ サーフェイスにレンダリングする (VMR-7 のみ)。';
        VMR_RENDER_DEVICE_VIDMEM:  ls_Msg2 := 'VMR はビデオ メモリにレンダリングする。';
        VMR_RENDER_DEVICE_SYSMEM:  ls_Msg2 := 'VMR はシステム メモリにレンダリングする (VMR-7 のみ)。'
      end;
    end;
    EC_VMR_SURFACE_FLIPPED: begin
      ls_Msg  := 'EC_VMR_SURFACE_FLIPPED';
      ls_Msg1 := 'VMR-7のアロケータプレゼンタが、表示するサーフェイスのDirectDraw Flipメソッドを呼び出したときに送信される。';
    end;
    EC_VMR_RECONNECTION_FAILED: begin
      ls_Msg  := 'EC_VMR_RECONNECTION_FAILED';
      ls_Msg1 := 'アップストリームデコーダから動的フォーマット変更要求を受け入れることができなかった場合に、VMR-7およびVMR-9によって送信される。';
    end;
    EC_WINDOW_DESTROYED: begin
      ls_Msg  := 'EC_WINDOW_DESTROYED';
      ls_Msg1 := 'ビデオレンダラが破棄またはグラフから削除された。';
    end;
    EC_WMT_EVENT: begin
      ls_Msg  := 'EC_WMT_EVENT';
      ls_Msg1 := 'アプリケーションがASF Readerフィルタを使用して、DRM (digital rights management)によって保護されているASFファイルを再生する場合に、Windows Media Format SDKによって送信される。';
    end;
    EC_WMT_INDEX_EVENT: begin
      ls_Msg  := 'EC_WMT_INDEX_EVENT';
      ls_Msg1 := 'アプリケーションがASF Writerを使用して、Windows Media Videoファイルにインデックスを付ける場合に、Windows Media Format SDKによって送信される。';
      case iParam1 of
        Integer(WMT_STARTED):        ls_Msg2 := 'インデックス付けが開始された。';
        Integer(WMT_CLOSED):         ls_Msg2 := 'インデックス付けが完了した。';
        Integer(WMT_INDEX_PROGRESS): ls_Msg2 := 'インデックス付けが進行中である。lParam2 は進捗の度合いをパーセントで表す。';
      end;
    end;
    else begin
      ls_Msg  := '';
      ls_Msg1 := '';
      ls_Msg2 := '';
    end;
  end;

  li_Len := 2;
  ShowDbg(Format('%s (%2d, %d, %d)', [ls_Msg, iEventCode, iParam1, iParam2]));
  ShowDbg(Format('%*s %s',   [li_Len, ' ', ls_Msg1]));
  if (ls_Msg2 <> '') then begin
    ShowDbg(Format('%*s %s', [li_Len, ' ', ls_Msg2]));
  end;
end;


end.