public CAviDS(string filename, double playSpeed) { builder = new FilterGraph() as IGraphBuilder; grabber = new SampleGrabber() as ISampleGrabber; mediaType = new AMMediaType(); mediaType.majorType = MediaType.Video; mediaType.subType = MediaSubType.RGB32; mediaType.formatType = FormatType.VideoInfo; DsError.ThrowExceptionForHR(grabber.SetMediaType(mediaType)); DsError.ThrowExceptionForHR(builder.AddFilter(grabber as IBaseFilter, "Sample Grabber(DTXMania)")); DsError.ThrowExceptionForHR(builder.RenderFile(filename, null)); CDirectShow.ConnectNullRendererFromSampleGrabber(builder, grabber as IBaseFilter); if (builder is IVideoWindow videoWindow) { videoWindow.put_AutoShow(OABool.False); } DsError.ThrowExceptionForHR(grabber.GetConnectedMediaType(mediaType)); videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader)); nWidth = videoInfo.BmiHeader.Width; nHeight = videoInfo.BmiHeader.Height; seeker = builder as IMediaSeeking; DsError.ThrowExceptionForHR(seeker.GetDuration(out nMediaLength)); DsError.ThrowExceptionForHR(seeker.SetRate(playSpeed / 20.0)); control = builder as IMediaControl; filter = builder as IMediaFilter; grabber.SetBufferSamples(BufferThem: true); Run(); Pause(); bPlaying = false; bPause = false; }
protected virtual void Dispose(bool bManagedリソースも解放する) { if (bManagedリソースも解放する) { #region [ ROTから解放する。] //----------------- #if DEBUG C共通.tDisposeする(ref this.rot); #endif //----------------- #endregion CDirectShow.tインスタンスを解放する(this.nインスタンスID); } #region [ インターフェース参照をなくし、COMオブジェクトを解放する。 ] //----------------- if (this.ip != IntPtr.Zero) { Marshal.FreeCoTaskMem(this.ip); this.ip = IntPtr.Zero; } if (this.MediaCtrl != null) { this.MediaCtrl.Stop(); this.MediaCtrl = null; } if (this.MediaEventEx != null) { this.MediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero); this.MediaEventEx = null; } if (this.MediaSeeking != null) { this.MediaSeeking = null; } if (this.BasicAudio != null) { this.BasicAudio = null; } CCommon.tReleaseComObject(ref this.nullRenderer); CCommon.tReleaseComObject(ref this.memoryRenderer); CCommon.tReleaseComObject(ref this.memoryRendererObject); CCommon.tReleaseComObject(ref this.graphBuilder); //----------------- #endregion CCommon.tRunGarbageCollector(); }
protected static void tインスタンスを登録する(CDirectShow ds) { for (int i = 1; i < CDirectShow.nインスタンスIDの最大数; i++) { if (!CDirectShow.dicインスタンス.ContainsKey(i)) // 空いている番号を使う。 { ds.nインスタンスID = i; CDirectShow.dicインスタンス.Add(i, ds); break; } } }
public static void tビデオレンダラをグラフから除去する(IGraphBuilder graphBuilder) { int hr = 0; IBaseFilter videoRenderer = null; IPin renderInputPin = null; IPin connectedOutputPin = null; try { // videoRenderer を探す。 CDirectShow.tビデオレンダラとその入力ピンを探して返す(graphBuilder, out videoRenderer, out renderInputPin); if (videoRenderer == null || renderInputPin == null) { return; // なかった } #region [ renderInputPin へ接続している前段の出力ピン connectedOutputPin を探す。 ] //----------------- renderInputPin.ConnectedTo(out connectedOutputPin); //----------------- #endregion if (connectedOutputPin == null) { return; // なかった } // 前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。 renderInputPin.Disconnect(); connectedOutputPin.Disconnect(); // ビデオレンダラをグラフから除去。 graphBuilder.RemoveFilter(videoRenderer); } finally { CCommon.tReleaseComObject(ref connectedOutputPin); CCommon.tReleaseComObject(ref renderInputPin); CCommon.tReleaseComObject(ref videoRenderer); } }
/// <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 CAviDS(string filename, double playSpeed) { int hr = 0x0; builder = (IGraphBuilder) new FilterGraph(); #region [Sample Grabber] { grabber = new SampleGrabber() as ISampleGrabber; mediaType = new AMMediaType(); mediaType.majorType = MediaType.Video; mediaType.subType = MediaSubType.RGB32; mediaType.formatType = FormatType.VideoInfo; hr = grabber.SetMediaType(mediaType); DsError.ThrowExceptionForHR(hr); hr = builder.AddFilter((IBaseFilter)grabber, "Sample Grabber"); DsError.ThrowExceptionForHR(hr); } #endregion hr = builder.RenderFile(filename, null); DsError.ThrowExceptionForHR(hr); // Null レンダラに接続しないとウィンドウが表示される。 // また、レンダリングを行わないため処理速度を向上できる。 CDirectShow.ConnectNullRendererFromSampleGrabber(builder, grabber as IBaseFilter); CDirectShow.tグラフを解析しデバッグ出力する(builder); IVideoWindow videoWindow = builder as IVideoWindow; if (videoWindow != null) { videoWindow.put_AutoShow(OABool.False); } #region [Video Info] { hr = grabber.GetConnectedMediaType(mediaType); DsError.ThrowExceptionForHR(hr); videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader)); nWidth = videoInfo.BmiHeader.Width; nHeight = videoInfo.BmiHeader.Height; } #endregion #region [ Seeker ] { seeker = builder as IMediaSeeking; hr = seeker.GetDuration(out nMediaLength); DsError.ThrowExceptionForHR(hr); hr = seeker.SetRate(playSpeed / 20); DsError.ThrowExceptionForHR(hr); } #endregion #region [Control] { control = builder as IMediaControl; } #endregion #region [Filter] { filter = builder as IMediaFilter; } #endregion grabber.SetBufferSamples(true); this.Run(); this.Pause(); bPlaying = false; bPause = false; // 外見えには演奏停止している。PAUSE中として外に見せないこと。 }
public static void tオーディオレンダラをNullレンダラに変えてフォーマットを取得する(IGraphBuilder graphBuilder, out WaveFormat wfx, out byte[] wfx拡張データ) { int hr = 0; IBaseFilter audioRenderer = null; IPin rendererInputPin = null; IPin rendererConnectedOutputPin = null; IBaseFilter nullRenderer = null; IPin nullRendererInputPin = null; wfx = null; wfx拡張データ = new byte[0]; try { // audioRenderer を探す。 audioRenderer = CDirectShow.tオーディオレンダラを探して返す(graphBuilder); if (audioRenderer == null) { return; // なかった } #region [ 音量ゼロで一度再生する。(オーディオレンダラの入力ピンMediaTypeが、接続時とは異なる「正しいもの」に変わる可能性があるため。)] //----------------- { // ここに来た時点で、グラフのビデオレンダラは無効化(NullRendererへの置換や除去など)しておくこと。 // さもないと、StopWhenReady() 時に一瞬だけ Activeウィンドウが表示されてしまう。 var mediaCtrl = (IMediaControl)graphBuilder; var basicAudio = (IBasicAudio)graphBuilder; basicAudio.put_Volume(-10000); // 最小音量 // グラフを再生してすぐ止める。(Paused → Stopped へ遷移する) mediaCtrl.StopWhenReady(); // グラフが Stopped に遷移完了するまで待つ。(StopWhenReady() はグラフが Stopped になるのを待たずに帰ってくる。) FilterState fs = FilterState.Paused; hr = CWin32.S_FALSE; while (fs != FilterState.Stopped || hr != CWin32.S_OK) { hr = mediaCtrl.GetState(10, out fs); } // 終了処理。 basicAudio.put_Volume(0); // 最大音量 basicAudio = null; mediaCtrl = null; } //----------------- #endregion // audioRenderer の入力ピンを探す。 rendererInputPin = t最初の入力ピンを探して返す(audioRenderer); if (rendererInputPin == null) { return; } // WAVEフォーマットを取得し、wfx 引数へ格納する。 var type = new AMMediaType(); hr = rendererInputPin.ConnectionMediaType(type); DsError.ThrowExceptionForHR(hr); try { wfx = new WaveFormat(); #region [ type.formatPtr から wfx に、拡張領域を除くデータをコピーする。] //----------------- var wfxTemp = new WaveFormatEx(); // SharpDX.Multimedia.WaveFormat は Marshal.PtrToStructure() で使えないので、それが使える DirectShowLib.WaveFormatEx を介して取得する。(面倒…) Marshal.PtrToStructure(type.formatPtr, (object)wfxTemp); wfx = WaveFormat.CreateCustomFormat((WaveFormatEncoding)wfxTemp.wFormatTag, wfxTemp.nSamplesPerSec, wfxTemp.nChannels, wfxTemp.nAvgBytesPerSec, wfxTemp.nBlockAlign, wfxTemp.wBitsPerSample); //----------------- #endregion #region [ 拡張領域が存在するならそれを wfx拡張データ に格納する。 ] //----------------- int nWaveFormatEx本体サイズ = 16 + 2; // sizeof( WAVEFORMAT ) + sizof( WAVEFORMATEX.cbSize ) int nはみ出しサイズbyte = type.formatSize - nWaveFormatEx本体サイズ; if (nはみ出しサイズbyte > 0) { wfx拡張データ = new byte[nはみ出しサイズbyte]; var hGC = GCHandle.Alloc(wfx拡張データ, GCHandleType.Pinned); // 動くなよー unsafe { byte *src = (byte *)type.formatPtr.ToPointer(); byte *dst = (byte *)hGC.AddrOfPinnedObject().ToPointer(); CWin32.CopyMemory(dst, src + nWaveFormatEx本体サイズ, (uint)nはみ出しサイズbyte); } hGC.Free(); } //----------------- #endregion } finally { if (type != null) { DsUtils.FreeAMMediaType(type); } } // audioRenderer につながる出力ピンを探す。 hr = rendererInputPin.ConnectedTo(out rendererConnectedOutputPin); DsError.ThrowExceptionForHR(hr); // audioRenderer をグラフから切断する。 rendererInputPin.Disconnect(); rendererConnectedOutputPin.Disconnect(); // audioRenderer をグラフから除去する。 hr = graphBuilder.RemoveFilter(audioRenderer); DsError.ThrowExceptionForHR(hr); // nullRenderer を作成し、グラフに追加する。 nullRenderer = (IBaseFilter) new NullRenderer(); hr = graphBuilder.AddFilter(nullRenderer, "Audio Null Renderer"); DsError.ThrowExceptionForHR(hr); // nullRenderer の入力ピンを探す。 hr = nullRenderer.FindPin("In", out nullRendererInputPin); DsError.ThrowExceptionForHR(hr); // nullRenderer をグラフに接続する。 hr = rendererConnectedOutputPin.Connect(nullRendererInputPin, null); DsError.ThrowExceptionForHR(hr); } finally { CCommon.tReleaseComObject(ref nullRendererInputPin); CCommon.tReleaseComObject(ref nullRenderer); CCommon.tReleaseComObject(ref rendererConnectedOutputPin); CCommon.tReleaseComObject(ref rendererInputPin); CCommon.tReleaseComObject(ref audioRenderer); } }
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 { } }