/// <summary> /// <para>指定された動画ファイルから音声のみをエンコードし、WAVファイルイメージを作成して返す。</para> /// </summary> public static void t変換(string fileName, out byte[] wavFileImage) { int hr = 0; IGraphBuilder graphBuilder = null; try { graphBuilder = (IGraphBuilder) new FilterGraph(); #region [ オーディオ用サンプルグラバの作成と追加。] //----------------- ISampleGrabber sampleGrabber = null; try { sampleGrabber = (ISampleGrabber) new SampleGrabber(); // サンプルグラバのメディアタイプの設定。 var mediaType = new AMMediaType() { majorType = MediaType.Audio, subType = MediaSubType.PCM, formatType = FormatType.WaveEx, }; try { hr = sampleGrabber.SetMediaType(mediaType); DsError.ThrowExceptionForHR(hr); } finally { if (mediaType != null) { DsUtils.FreeAMMediaType(mediaType); } } // サンプルグラバのバッファリングを有効にする。 hr = sampleGrabber.SetBufferSamples(true); DsError.ThrowExceptionForHR(hr); // サンプルグラバにコールバックを追加する。 sampleGrabberProc = new CSampleGrabberCallBack(); hr = sampleGrabber.SetCallback(sampleGrabberProc, 1); // 1:コールバックの BufferCB() メソッドの方を呼び出す。 // サンプルグラバをグラフに追加する。 hr = graphBuilder.AddFilter((IBaseFilter)sampleGrabber, "SampleGrabber for Audio/PCM"); DsError.ThrowExceptionForHR(hr); } finally { C共通.tCOMオブジェクトを解放する(ref sampleGrabber); } //----------------- #endregion var e = new DirectShowLib.DsROTEntry(graphBuilder); // fileName からグラフを自動生成。 hr = graphBuilder.RenderFile(fileName, null); // IMediaControl.RenderFile() は推奨されない DsError.ThrowExceptionForHR(hr); // ビデオレンダラを除去。 // オーディオレンダラをNullに変えるより前に実行すること。 // (CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する() の中で一度再生するので、 // そのときにActiveウィンドウが表示されてしまうため。) // chnmr0 : ウィンドウを表示しないだけなら IVideoWindow で put_AutoShow した。 IVideoWindow vw = graphBuilder as IVideoWindow; vw.put_AutoShow(OABool.False); // オーディオレンダラを NullRenderer に置換。 WaveFormat wfx; byte[] wfx拡張領域; CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する(graphBuilder, out wfx, out wfx拡張領域); // 基準クロックを NULL(最高速)に設定する。 IMediaFilter mediaFilter = graphBuilder as IMediaFilter; mediaFilter.SetSyncSource(null); mediaFilter = null; // メモリストリームにデコードデータを出力する。 sampleGrabberProc.MemoryStream = new MemoryStream(); // CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する() で一度再生しているので、ストリームをクリアする。 var ms = sampleGrabberProc.MemoryStream; var bw = new BinaryWriter(ms); bw.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }); // 'RIFF' bw.Write((UInt32)0); // ファイルサイズ - 8 [byte];今は不明なので後で上書きする。 bw.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }); // 'WAVE' bw.Write(new byte[] { 0x66, 0x6D, 0x74, 0x20 }); // 'fmt ' bw.Write((UInt32)(16 + ((wfx拡張領域.Length > 0) ? (2 /*sizeof(WAVEFORMATEX.cbSize)*/ + wfx拡張領域.Length) : 0))); // fmtチャンクのサイズ[byte] bw.Write((UInt16)wfx.Encoding); // フォーマットID(リニアPCMなら1) bw.Write((UInt16)wfx.Channels); // チャンネル数 bw.Write((UInt32)wfx.SampleRate); // サンプリングレート bw.Write((UInt32)wfx.AverageBytesPerSecond); // データ速度 bw.Write((UInt16)wfx.BlockAlign); // ブロックサイズ bw.Write((UInt16)wfx.BitsPerSample); // サンプルあたりのビット数 if (wfx拡張領域.Length > 0) { bw.Write((UInt16)wfx拡張領域.Length); // 拡張領域のサイズ[byte] bw.Write(wfx拡張領域); // 拡張データ } bw.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }); // 'data' int nDATAチャンクサイズ位置 = (int)ms.Position; bw.Write((UInt32)0); // dataチャンクのサイズ[byte];今は不明なので後で上書きする。 #region [ 再生を開始し、終了を待つ。- 再生中、sampleGrabberProc.MemoryStream に PCM データが蓄積されていく。] //----------------- IMediaControl mediaControl = graphBuilder as IMediaControl; mediaControl.Run(); // 再生開始 IMediaEvent mediaEvent = graphBuilder as IMediaEvent; EventCode eventCode; hr = mediaEvent.WaitForCompletion(-1, out eventCode); DsError.ThrowExceptionForHR(hr); if (eventCode != EventCode.Complete) { throw new Exception("再生待ちに失敗しました。"); } mediaControl.Stop(); mediaEvent = null; mediaControl = null; //----------------- #endregion bw.Seek(4, SeekOrigin.Begin); bw.Write((UInt32)ms.Length - 8); // ファイルサイズ - 8 [byte] bw.Seek(nDATAチャンクサイズ位置, SeekOrigin.Begin); bw.Write((UInt32)ms.Length - (nDATAチャンクサイズ位置 + 4)); // dataチャンクサイズ [byte] // 出力その2を作成。 wavFileImage = ms.ToArray(); // 終了処理。 bw.Close(); sampleGrabberProc.Dispose(); // ms.Close() } finally { C共通.tCOMオブジェクトを解放する(ref graphBuilder); } }
public CDirectShow(string fileName, IntPtr hWnd, bool bオーディオレンダラなし) { // 初期化。 this.n幅px = 0; this.n高さpx = 0; this.b上下反転 = false; this.nスキャンライン幅byte = 0; this.nデータサイズbyte = 0; this.b音声のみ = false; this.graphBuilder = null; this.MediaCtrl = null; this.b再生中 = false; this.bループ再生 = false; // 静的リストに登録し、インスタンスIDを得る。 CDirectShow.tインスタンスを登録する(this); // 並列処理準備。 if (CDirectShow.n並列度 == 0) // 算出がまだなら算出する。 { CDirectShow.n並列度 = Environment.ProcessorCount; // 並列度=CPU数とする。 } unsafe { this.dgライン描画ARGB32 = new DGライン描画[CDirectShow.n並列度]; this.dgライン描画XRGB32 = new DGライン描画[CDirectShow.n並列度]; for (int i = 0; i < CDirectShow.n並列度; i++) { this.dgライン描画ARGB32[i] = new DGライン描画(this.tライン描画ARGB32); this.dgライン描画XRGB32[i] = new DGライン描画(this.tライン描画XRGB32); } } try { int hr = 0; // グラフビルダを生成。 this.graphBuilder = (IGraphBuilder) new FilterGraph(); #if DEBUG // ROT への登録。 this.rot = new DsROTEntry(graphBuilder); #endif // QueryInterface。存在しなければ null。 this.MediaCtrl = this.graphBuilder as IMediaControl; this.MediaEventEx = this.graphBuilder as IMediaEventEx; this.MediaSeeking = this.graphBuilder as IMediaSeeking; this.BasicAudio = this.graphBuilder as IBasicAudio; // IMemoryRenderer をグラフに挿入。 AMMediaType mediaType = null; this.memoryRendererObject = new MemoryRenderer(); this.memoryRenderer = (IMemoryRenderer)this.memoryRendererObject; var baseFilter = (IBaseFilter)this.memoryRendererObject; hr = this.graphBuilder.AddFilter(baseFilter, "MemoryRenderer"); DsError.ThrowExceptionForHR(hr); // fileName からグラフを自動生成。 hr = this.graphBuilder.RenderFile(fileName, null); // IMediaControl.RenderFile() は推奨されない DsError.ThrowExceptionForHR(hr); // 音声のみ? { IBaseFilter videoRenderer; IPin videoInputPin; CDirectShow.tビデオレンダラとその入力ピンを探して返す(this.graphBuilder, out videoRenderer, out videoInputPin); if (videoRenderer == null) { this.b音声のみ = true; } else { CCommon.tReleaseComObject(ref videoInputPin); CCommon.tReleaseComObject(ref videoRenderer); } } // イメージ情報を取得。 if (!this.b音声のみ) { long n; int m; this.memoryRenderer.GetWidth(out n); this.n幅px = (int)n; this.memoryRenderer.GetHeight(out n); this.n高さpx = (int)n; this.memoryRenderer.IsBottomUp(out m); this.b上下反転 = (m != 0); this.memoryRenderer.GetBufferSize(out n); this.nデータサイズbyte = (int)n; this.nスキャンライン幅byte = (int)this.nデータサイズbyte / this.n高さpx; // CCommon.tReleaseComObject( ref baseFilter ); なんかキャスト元のオブジェクトまで解放されるので解放禁止。 } // グラフを修正する。 if (bオーディオレンダラなし) { WaveFormat dummy1; byte[] dummy2; CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する(this.graphBuilder, out dummy1, out dummy2); } // その他の処理。 this.t再生準備開始(); // 1回以上 IMediaControl を呼び出してないと、IReferenceClock は取得できない。 this.t遷移完了まで待って状態を取得する(); // 完全に Pause へ遷移するのを待つ。(環境依存) // イベント用ウィンドウハンドルを設定。 this.MediaEventEx.SetNotifyWindow(hWnd, (int)WM_DSGRAPHNOTIFY, new IntPtr(this.nインスタンスID)); } #if !DEBUG catch (Exception e) { CCommon.t例外の詳細をログに出力する(e); this.Dispose(); throw; // 例外発出。 } #endif finally { } }