2 using System.Collections.Generic;
\r
3 using System.Diagnostics;
\r
4 using System.Runtime.InteropServices;
\r
6 using System.Drawing;
\r
7 using System.Drawing.Imaging;
\r
8 using System.Threading;
\r
10 using SharpDX.Direct3D9;
\r
11 using SharpDX.Multimedia;
\r
12 using DirectShowLib;
\r
17 /// <para>DirectShowを用いたクリップ(動画+音声)を扱う。</para>
\r
18 /// <para>1つのクリップにつき1つの CDirectShow インスタンスを生成する。</para>
\r
19 /// <para>再生の開始や停止などの操作の他、任意の時点でスナップイメージを取得することができる。</para>
\r
21 public class CDirectShow : IDisposable
\r
25 public const uint WM_DSGRAPHNOTIFY = CWin32.WM_APP + 1;
\r
27 public enum Eグラフの状態 { 完全停止中, 再生のみ停止中, 再生中, 完全停止へ遷移中, 再生のみ停止へ遷移中, 再生へ遷移中, 未定 }
\r
28 public Eグラフの状態 eグラフの状態
\r
32 var status = Eグラフの状態.未定;
\r
34 if( this.MediaCtrl != null )
\r
37 int hr = this.MediaCtrl.GetState( 0, out fs ); // それなりに重たいので注意。
\r
39 if( hr == CWin32.E_FAIL )
\r
43 status = Eグラフの状態.未定;
\r
47 else if( hr == CWin32.VFW_S_STATE_INTERMEDIATE )
\r
53 case FilterState.Running:
\r
54 status = Eグラフの状態.再生へ遷移中;
\r
57 case FilterState.Paused:
\r
58 status = Eグラフの状態.再生のみ停止へ遷移中;
\r
61 case FilterState.Stopped:
\r
62 status = Eグラフの状態.完全停止へ遷移中;
\r
66 status = Eグラフの状態.未定;
\r
78 case FilterState.Running:
\r
79 status = Eグラフの状態.再生中;
\r
82 case FilterState.Paused:
\r
83 status = Eグラフの状態.再生のみ停止中;
\r
86 case FilterState.Stopped:
\r
87 status = Eグラフの状態.完全停止中;
\r
91 status = Eグラフの状態.未定;
\r
102 public bool bループ再生;
\r
114 public int nスキャンライン幅byte
\r
119 public int nデータサイズbyte
\r
136 public long n現在のグラフの再生位置ms
\r
140 if( this.MediaSeeking == null )
\r
144 int hr = this.MediaSeeking.GetCurrentPosition( out current );
\r
145 DsError.ThrowExceptionForHR( hr );
\r
146 return (long) ( current / ( 1000.0 * 10.0 ) );
\r
150 /// <para>無音:0~100:原音。set のみ。</para>
\r
160 if( this.BasicAudio == null )
\r
169 // リニア音量をデシベル音量に変換。
\r
175 n音量db = -10000; // 完全無音
\r
179 n音量db = (int) ( ( 20.0 * Math.Log10( ( (double) value ) / 100.0 ) ) * 100.0 );
\r
183 // デシベル音量でグラフの音量を変更。
\r
185 this.BasicAudio.put_Volume( n音量db );
\r
189 /// <para>左:-100~中央:0~100:右。set のみ。</para>
\r
195 if( this.BasicAudio == null )
\r
198 // リニア位置をデシベル位置に変換。
\r
200 int n位置 = Math.Min( Math.Max( value, -100 ), +100 );
\r
207 else if( n位置 == -100 )
\r
211 else if( n位置 == 100 )
\r
217 n位置db = (int) ( ( 20.0 * Math.Log10( ( (double) ( n位置 + 100 ) ) / 100.0 ) ) * 100.0 );
\r
221 n位置db = (int) ( ( -20.0 * Math.Log10( ( (double) ( 100 - n位置 ) ) / 100.0 ) ) * 100.0 );
\r
224 // デシベル位置でグラフの位置を変更。
\r
226 this.BasicAudio.put_Balance( n位置db );
\r
229 public IMediaControl MediaCtrl;
\r
230 public IMediaEventEx MediaEventEx;
\r
231 public IMediaSeeking MediaSeeking;
\r
232 public IBasicAudio BasicAudio;
\r
233 public IGraphBuilder graphBuilder;
\r
236 /// <para>CDirectShowインスタンスに固有のID。</para>
\r
237 /// <para>DirectShow イベントをウィンドウに発信する際、MessageID として "WM_APP+インスタンスID" を発信する。</para>
\r
238 /// <para>これにより、受け側でイベント発信インスタンスを特定することが可能になる。</para>
\r
240 public int nインスタンスID
\r
249 public CDirectShow()
\r
252 public CDirectShow( string fileName, IntPtr hWnd, bool bオーディオレンダラなし )
\r
258 this.b上下反転 = false;
\r
259 this.nスキャンライン幅byte = 0;
\r
260 this.nデータサイズbyte = 0;
\r
261 this.b音声のみ = false;
\r
262 this.graphBuilder = null;
\r
263 this.MediaCtrl = null;
\r
265 this.bループ再生 = false;
\r
268 // 静的リストに登録し、インスタンスIDを得る。
\r
270 CDirectShow.tインスタンスを登録する( this );
\r
275 if( CDirectShow.n並列度 == 0 ) // 算出がまだなら算出する。
\r
276 CDirectShow.n並列度 = Environment.ProcessorCount; // 並列度=CPU数とする。
\r
280 this.dgライン描画ARGB32 = new DGライン描画[ CDirectShow.n並列度 ];
\r
281 this.dgライン描画XRGB32 = new DGライン描画[ CDirectShow.n並列度 ];
\r
283 for( int i = 0; i < CDirectShow.n並列度; i++ )
\r
285 this.dgライン描画ARGB32[ i ] = new DGライン描画( this.tライン描画ARGB32 );
\r
286 this.dgライン描画XRGB32[ i ] = new DGライン描画( this.tライン描画XRGB32 );
\r
297 this.graphBuilder = (IGraphBuilder) new FilterGraph();
\r
300 this.rot = new DsROTEntry( graphBuilder );
\r
304 // QueryInterface。存在しなければ null。
\r
306 this.MediaCtrl = this.graphBuilder as IMediaControl;
\r
307 this.MediaEventEx = this.graphBuilder as IMediaEventEx;
\r
308 this.MediaSeeking = this.graphBuilder as IMediaSeeking;
\r
309 this.BasicAudio = this.graphBuilder as IBasicAudio;
\r
312 // IMemoryRenderer をグラフに挿入。
\r
314 AMMediaType mediaType = null;
\r
316 this.memoryRendererObject = new MemoryRenderer();
\r
317 this.memoryRenderer = (IMemoryRenderer) this.memoryRendererObject;
\r
318 var baseFilter = (IBaseFilter) this.memoryRendererObject;
\r
320 hr = this.graphBuilder.AddFilter( baseFilter, "MemoryRenderer" );
\r
321 DsError.ThrowExceptionForHR( hr );
\r
324 // fileName からグラフを自動生成。
\r
326 hr = this.graphBuilder.RenderFile( fileName, null ); // IMediaControl.RenderFile() は推奨されない
\r
327 DsError.ThrowExceptionForHR( hr );
\r
333 IBaseFilter videoRenderer;
\r
334 IPin videoInputPin;
\r
335 IBaseFilter audioRenderer;
\r
336 IPin audioInputPin;
\r
337 CDirectShow.SearchMMRenderers( this.graphBuilder, out videoRenderer, out videoInputPin, out audioRenderer, out audioInputPin );
\r
338 if ( videoRenderer == null && audioRenderer != null )
\r
344 C共通.tCOMオブジェクトを解放する(ref videoInputPin);
\r
345 C共通.tCOMオブジェクトを解放する(ref videoRenderer);
\r
346 C共通.tCOMオブジェクトを解放する(ref audioInputPin);
\r
347 C共通.tCOMオブジェクトを解放する(ref audioRenderer);
\r
358 this.memoryRenderer.GetWidth( out n );
\r
359 this.n幅px = (int) n;
\r
360 this.memoryRenderer.GetHeight( out n );
\r
361 this.n高さpx = (int) n;
\r
362 this.memoryRenderer.IsBottomUp( out m );
\r
363 this.b上下反転 = ( m != 0 );
\r
364 this.memoryRenderer.GetBufferSize( out n );
\r
365 this.nデータサイズbyte = (int) n;
\r
366 this.nスキャンライン幅byte = (int) this.nデータサイズbyte / this.n高さpx;
\r
367 // C共通.tCOMオブジェクトを解放する( ref baseFilter ); なんかキャスト元のオブジェクトまで解放されるので解放禁止。
\r
377 CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する( this.graphBuilder, out dummy1, out dummy2 );
\r
383 this.t再生準備開始(); // 1回以上 IMediaControl を呼び出してないと、IReferenceClock は取得できない。
\r
384 this.t遷移完了まで待って状態を取得する(); // 完全に Pause へ遷移するのを待つ。(環境依存)
\r
387 // イベント用ウィンドウハンドルを設定。
\r
389 this.MediaEventEx.SetNotifyWindow( hWnd, (int) WM_DSGRAPHNOTIFY, new IntPtr( this.nインスタンスID ) );
\r
392 catch( Exception e )
\r
394 C共通.t例外の詳細をログに出力する( e );
\r
404 public void t再生準備開始()
\r
406 if( this.MediaCtrl != null )
\r
408 int hr = this.MediaCtrl.Pause(); // 再生準備を開始する。ここでは準備が完了するまで待たない。
\r
409 DsError.ThrowExceptionForHR( hr );
\r
412 public void t再生開始()
\r
414 if( this.MediaCtrl != null && --this.n再生一時停止呼び出しの累積回数 <= 0 )
\r
416 //this.t遷移完了まで待って状態を取得する(); // 再生準備(だろう)がまだ完了してなければ、待つ。 → 意外と重い処理なので外部で判断して実行するよう変更する。(2011.8.7)
\r
418 int hr = this.MediaCtrl.Run(); // 再生開始。
\r
419 DsError.ThrowExceptionForHR( hr );
\r
421 this.n再生一時停止呼び出しの累積回数 = 0; // 一時停止回数はここでリセットされる。
\r
425 public void t再生一時停止()
\r
427 if( this.MediaCtrl != null && this.n再生一時停止呼び出しの累積回数 == 0 )
\r
429 int hr = this.MediaCtrl.Pause();
\r
430 DsError.ThrowExceptionForHR( hr );
\r
432 this.n再生一時停止呼び出しの累積回数++;
\r
435 public void t再生停止()
\r
437 if( this.MediaCtrl != null )
\r
439 int hr = this.MediaCtrl.Stop();
\r
440 DsError.ThrowExceptionForHR( hr );
\r
444 //this.t再生位置を変更する( 0.0 ); → より細かく制御するために、FDK外部で制御するように変更。(2011.8.7)
\r
447 this.n再生一時停止呼び出しの累積回数 = 0; // 停止すると、一時停止呼び出し累積回数はリセットされる。
\r
450 public void t再生位置を変更( double db再生位置ms )
\r
452 if( this.MediaSeeking == null )
\r
455 int hr = this.MediaSeeking.SetPositions(
\r
456 DsLong.FromInt64( (long) ( db再生位置ms * 1000.0 * 10.0 ) ),
\r
457 AMSeekingSeekingFlags.AbsolutePositioning,
\r
459 AMSeekingSeekingFlags.NoPositioning );
\r
461 DsError.ThrowExceptionForHR( hr );
\r
463 public void t最初から再生開始()
\r
465 this.t再生位置を変更( 0.0 );
\r
468 public Eグラフの状態 t遷移完了まで待って状態を取得する()
\r
470 var status = Eグラフの状態.未定;
\r
472 if( this.MediaCtrl != null )
\r
475 int hr = this.MediaCtrl.GetState( 1000, out fs ); // 遷移完了まで最大1000ms待つ。
\r
477 return this.eグラフの状態;
\r
479 public unsafe void t現時点における最新のスナップイメージをTextureに転写する( CTexture texture )
\r
483 #region [ 再生してないなら何もせず帰還。(一時停止中はOK。)]
\r
484 //-----------------
\r
487 //-----------------
\r
489 #region [ 音声のみなら何もしない。]
\r
490 //-----------------
\r
493 //-----------------
\r
496 DataRectangle dr = texture.texture.LockRectangle( 0, LockFlags.Discard );
\r
499 if( this.nスキャンライン幅byte == dr.Pitch )
\r
501 #region [ (A) ピッチが合うので、テクスチャに直接転送する。]
\r
502 //-----------------
\r
503 hr = this.memoryRenderer.GetCurrentBuffer( dr.DataPointer, this.nデータサイズbyte );
\r
504 DsError.ThrowExceptionForHR( hr );
\r
505 //-----------------
\r
510 this.b上下反転 = false; // こちらの方法では常に正常
\r
512 #region [ (B) ピッチが合わないので、メモリに転送してからテクスチャに転送する。]
\r
513 //-----------------
\r
515 #region [ IMemoryRenderer からバッファにイメージデータを読み込む。]
\r
516 //-----------------
\r
517 if( this.ip == IntPtr.Zero )
\r
518 this.ip = Marshal.AllocCoTaskMem( this.nデータサイズbyte );
\r
520 hr = this.memoryRenderer.GetCurrentBuffer( this.ip, this.nデータサイズbyte );
\r
521 DsError.ThrowExceptionForHR( hr );
\r
522 //-----------------
\r
525 #region [ テクスチャにスナップイメージを転送。]
\r
526 //-----------------
\r
527 bool bARGB32 = true;
\r
529 switch( texture.Format )
\r
531 case Format.A8R8G8B8:
\r
535 case Format.X8R8G8B8:
\r
540 return; // 未対応のフォーマットは無視。
\r
543 // スレッドプールを使って並列転送する準備。
\r
545 this.ptrSnap = (byte*) this.ip.ToPointer();
\r
546 var ptr = stackalloc UInt32*[ CDirectShow.n並列度 ]; // stackalloc(GC対象外、メソッド終了時に自動開放)は、スタック変数相手にしか使えない。
\r
547 ptr[ 0 ] = (UInt32*) dr.DataPointer.ToPointer(); // ↓
\r
548 for( int i = 1; i < CDirectShow.n並列度; i++ ) // スタック変数で確保、初期化して…
\r
549 ptr[ i ] = ptr[ i - 1 ] + this.n幅px; // ↓
\r
550 this.ptrTexture = ptr; // スタック変数をクラスメンバに渡す(これならOK)。
\r
553 // 並列度が1ならシングルスレッド、2以上ならマルチスレッドで転送する。
\r
554 // → CPUが1つの場合、わざわざスレッドプールのスレッドで処理するのは無駄。
\r
556 if( CDirectShow.n並列度 == 1 )
\r
559 this.tライン描画ARGB32( 0 );
\r
561 this.tライン描画XRGB32( 0 );
\r
567 var ar = new IAsyncResult[ CDirectShow.n並列度 ];
\r
568 for( int i = 0; i < CDirectShow.n並列度; i++ )
\r
570 ar[ i ] = ( bARGB32 ) ?
\r
571 this.dgライン描画ARGB32[ i ].BeginInvoke( i, null, null ) :
\r
572 this.dgライン描画XRGB32[ i ].BeginInvoke( i, null, null );
\r
578 for( int i = 0; i < CDirectShow.n並列度; i++ )
\r
581 this.dgライン描画ARGB32[ i ].EndInvoke( ar[ i ] );
\r
583 this.dgライン描画XRGB32[ i ].EndInvoke( ar[ i ] );
\r
587 this.ptrSnap = null;
\r
588 this.ptrTexture = null;
\r
589 //-----------------
\r
592 //-----------------
\r
598 texture.texture.UnlockRectangle( 0 );
\r
602 private IntPtr ip = IntPtr.Zero;
\r
604 public static void tグラフを解析しデバッグ出力する( IGraphBuilder graphBuilder )
\r
606 if( graphBuilder == null )
\r
608 Debug.WriteLine( "指定されたグラフが null です。" );
\r
614 IEnumFilters eFilters;
\r
615 hr = graphBuilder.EnumFilters( out eFilters );
\r
616 DsError.ThrowExceptionForHR( hr );
\r
618 var filters = new IBaseFilter[ 1 ];
\r
619 while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
\r
621 FilterInfo filterInfo;
\r
622 hr = filters[ 0 ].QueryFilterInfo( out filterInfo );
\r
623 DsError.ThrowExceptionForHR( hr );
\r
625 Debug.WriteLine( filterInfo.achName ); // フィルタ名表示。
\r
626 if( filterInfo.pGraph != null )
\r
627 C共通.tCOMオブジェクトを解放する( ref filterInfo.pGraph );
\r
631 hr = filters[ 0 ].EnumPins( out ePins );
\r
632 DsError.ThrowExceptionForHR( hr );
\r
634 var pins = new IPin[ 1 ];
\r
635 while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
\r
638 hr = pins[ 0 ].QueryPinInfo( out pinInfo );
\r
639 DsError.ThrowExceptionForHR( hr );
\r
641 Debug.Write( " " + pinInfo.name ); // ピン名表示。
\r
642 Debug.Write( ( pinInfo.dir == PinDirection.Input ) ? " ← " : " → " );
\r
645 hr = pins[ 0 ].ConnectedTo( out connectPin );
\r
646 if( hr != CWin32.S_OK )
\r
647 Debug.WriteLine( "(未接続)" );
\r
650 DsError.ThrowExceptionForHR( hr );
\r
652 PinInfo connectPinInfo;
\r
653 hr = connectPin.QueryPinInfo( out connectPinInfo );
\r
654 DsError.ThrowExceptionForHR( hr );
\r
656 FilterInfo connectFilterInfo;
\r
657 hr = connectPinInfo.filter.QueryFilterInfo( out connectFilterInfo );
\r
658 DsError.ThrowExceptionForHR( hr );
\r
660 Debug.Write( "[" + connectFilterInfo.achName + "]." ); // 接続先フィルタ名
\r
662 if( connectFilterInfo.pGraph != null )
\r
663 C共通.tCOMオブジェクトを解放する( ref connectFilterInfo.pGraph );
\r
666 Debug.WriteLine( connectPinInfo.name ); // 接続先ピン名
\r
667 if( connectPinInfo.filter != null )
\r
668 C共通.tCOMオブジェクトを解放する( ref connectPinInfo.filter );
\r
669 DsUtils.FreePinInfo( connectPinInfo );
\r
671 C共通.tCOMオブジェクトを解放する( ref connectPin );
\r
673 if( pinInfo.filter != null )
\r
674 C共通.tCOMオブジェクトを解放する( ref pinInfo.filter );
\r
675 DsUtils.FreePinInfo( pinInfo );
\r
677 C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
\r
680 C共通.tCOMオブジェクトを解放する( ref ePins );
\r
682 C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
\r
685 C共通.tCOMオブジェクトを解放する( ref eFilters );
\r
689 public static void tオーディオレンダラをNullレンダラに変えてフォーマットを取得する( IGraphBuilder graphBuilder, out WaveFormat wfx, out byte[] wfx拡張データ )
\r
693 IBaseFilter audioRenderer = null;
\r
694 IPin rendererInputPin = null;
\r
695 IPin rendererConnectedOutputPin = null;
\r
696 IBaseFilter nullRenderer = null;
\r
697 IPin nullRendererInputPin = null;
\r
699 wfx拡張データ = new byte[ 0 ];
\r
703 // audioRenderer を探す。
\r
705 audioRenderer = CDirectShow.tオーディオレンダラを探して返す( graphBuilder );
\r
706 if( audioRenderer == null )
\r
709 #region [ 音量ゼロで一度再生する。(オーディオレンダラの入力ピンMediaTypeが、接続時とは異なる「正しいもの」に変わる可能性があるため。)]
\r
710 //-----------------
\r
712 // ここに来た時点で、グラフのビデオレンダラは無効化(NullRendererへの置換や除去など)しておくこと。
\r
713 // さもないと、StopWhenReady() 時に一瞬だけ Activeウィンドウが表示されてしまう。
\r
715 var mediaCtrl = (IMediaControl) graphBuilder;
\r
716 var basicAudio = (IBasicAudio) graphBuilder;
\r
718 basicAudio.put_Volume( -10000 ); // 最小音量
\r
721 // グラフを再生してすぐ止める。(Paused → Stopped へ遷移する)
\r
723 mediaCtrl.StopWhenReady();
\r
726 // グラフが Stopped に遷移完了するまで待つ。(StopWhenReady() はグラフが Stopped になるのを待たずに帰ってくる。)
\r
728 FilterState fs = FilterState.Paused;
\r
729 hr = CWin32.S_FALSE;
\r
730 while( fs != FilterState.Stopped || hr != CWin32.S_OK )
\r
731 hr = mediaCtrl.GetState( 10, out fs );
\r
736 basicAudio.put_Volume( 0 ); // 最大音量
\r
741 //-----------------
\r
744 // audioRenderer の入力ピンを探す。
\r
746 rendererInputPin = t最初の入力ピンを探して返す( audioRenderer );
\r
747 if( rendererInputPin == null )
\r
751 // WAVEフォーマットを取得し、wfx 引数へ格納する。
\r
753 var type = new AMMediaType();
\r
754 hr = rendererInputPin.ConnectionMediaType( type );
\r
755 DsError.ThrowExceptionForHR( hr );
\r
758 #region [ type.formatPtr から wfx に、拡張領域を除くデータをコピーする。]
\r
759 //-----------------
\r
760 var wfxTemp = new WaveFormatEx(); // SlimDX.Multimedia.WaveFormat は Marshal.PtrToStructure() で使えないので、それが使える DirectShowLib.WaveFormatEx を介して取得する。(面倒…)
\r
761 Marshal.PtrToStructure( type.formatPtr, (object) wfxTemp );
\r
763 wfx = WaveFormat.CreateCustomFormat( (WaveFormatEncoding) wfxTemp.wFormatTag, wfxTemp.nSamplesPerSec, wfxTemp.nChannels, wfxTemp.nAvgBytesPerSec, wfxTemp.nBlockAlign, wfxTemp.wBitsPerSample );
\r
764 //-----------------
\r
766 #region [ 拡張領域が存在するならそれを wfx拡張データ に格納する。 ]
\r
767 //-----------------
\r
768 int nWaveFormatEx本体サイズ = 16 + 2; // sizeof( WAVEFORMAT ) + sizof( WAVEFORMATEX.cbSize )
\r
769 int nはみ出しサイズbyte = type.formatSize - nWaveFormatEx本体サイズ;
\r
771 if( nはみ出しサイズbyte > 0 )
\r
773 wfx拡張データ = new byte[ nはみ出しサイズbyte ];
\r
774 var hGC = GCHandle.Alloc( wfx拡張データ, GCHandleType.Pinned ); // 動くなよー
\r
777 byte* src = (byte*) type.formatPtr.ToPointer();
\r
778 byte* dst = (byte*) hGC.AddrOfPinnedObject().ToPointer();
\r
779 CWin32.CopyMemory( dst, src + nWaveFormatEx本体サイズ, (uint) nはみ出しサイズbyte );
\r
783 //-----------------
\r
789 DsUtils.FreeAMMediaType( type );
\r
793 // audioRenderer につながる出力ピンを探す。
\r
795 hr = rendererInputPin.ConnectedTo( out rendererConnectedOutputPin );
\r
796 DsError.ThrowExceptionForHR( hr );
\r
799 // audioRenderer をグラフから切断する。
\r
801 rendererInputPin.Disconnect();
\r
802 rendererConnectedOutputPin.Disconnect();
\r
805 // audioRenderer をグラフから除去する。
\r
807 hr = graphBuilder.RemoveFilter( audioRenderer );
\r
808 DsError.ThrowExceptionForHR( hr );
\r
811 // nullRenderer を作成し、グラフに追加する。
\r
813 nullRenderer = (IBaseFilter) new NullRenderer();
\r
814 hr = graphBuilder.AddFilter( nullRenderer, "Audio Null Renderer" );
\r
815 DsError.ThrowExceptionForHR( hr );
\r
818 // nullRenderer の入力ピンを探す。
\r
820 hr = nullRenderer.FindPin( "In", out nullRendererInputPin );
\r
821 DsError.ThrowExceptionForHR( hr );
\r
824 // nullRenderer をグラフに接続する。
\r
826 hr = rendererConnectedOutputPin.Connect( nullRendererInputPin, null );
\r
827 DsError.ThrowExceptionForHR( hr );
\r
831 C共通.tCOMオブジェクトを解放する( ref nullRendererInputPin );
\r
832 C共通.tCOMオブジェクトを解放する( ref nullRenderer );
\r
833 C共通.tCOMオブジェクトを解放する( ref rendererConnectedOutputPin );
\r
834 C共通.tCOMオブジェクトを解放する( ref rendererInputPin );
\r
835 C共通.tCOMオブジェクトを解放する( ref audioRenderer );
\r
838 public static void ConnectNullRendererFromSampleGrabber(IGraphBuilder graphBuilder, IBaseFilter sampleGrabber)
\r
841 IBaseFilter videoRenderer = null;
\r
842 IPin videoRendererInputPin = null;
\r
843 IBaseFilter audioRenderer = null;
\r
844 IPin audioRendererInputPin = null;
\r
845 IPin connectedOutputPin = null;
\r
846 IPin nullRendererInputPin = null;
\r
847 IPin grabberOutputPin = null;
\r
848 IPin grabberOutputConnectedPin = null;
\r
852 // videoRenderer を探す。
\r
853 CDirectShow.SearchMMRenderers(graphBuilder, out videoRenderer, out videoRendererInputPin, out audioRenderer, out audioRendererInputPin);
\r
854 if (videoRenderer != null && audioRendererInputPin != null)
\r
856 // 既存のレンダラにつながっているピン対を取得
\r
857 hr = videoRendererInputPin.ConnectedTo(out connectedOutputPin);
\r
858 DsError.ThrowExceptionForHR(hr);
\r
860 // それらを切断。前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。
\r
861 hr = videoRendererInputPin.Disconnect();
\r
862 DsError.ThrowExceptionForHR(hr);
\r
863 hr = connectedOutputPin.Disconnect();
\r
864 DsError.ThrowExceptionForHR(hr);
\r
866 // ビデオレンダラをグラフから除去し、ヌルレンダラを追加
\r
867 hr = graphBuilder.RemoveFilter(videoRenderer);
\r
868 DsError.ThrowExceptionForHR(hr);
\r
869 IBaseFilter nullRenderer = new NullRenderer() as IBaseFilter;
\r
870 hr = graphBuilder.AddFilter(nullRenderer, "Video Null Renderer");
\r
871 DsError.ThrowExceptionForHR(hr);
\r
873 // nullRenderer の入力ピンを探す。
\r
874 hr = nullRenderer.FindPin("In", out nullRendererInputPin);
\r
875 DsError.ThrowExceptionForHR(hr);
\r
876 hr = nullRendererInputPin.Disconnect();
\r
877 DsError.ThrowExceptionForHR(hr);
\r
879 // グラバの Out と Null Renderer の In を接続する。
\r
880 hr = sampleGrabber.FindPin("Out", out grabberOutputPin);
\r
881 DsError.ThrowExceptionForHR(hr);
\r
882 hr = grabberOutputPin.ConnectedTo(out grabberOutputConnectedPin);
\r
883 DsError.ThrowExceptionForHR(hr);
\r
884 hr = grabberOutputConnectedPin.Disconnect();
\r
885 DsError.ThrowExceptionForHR(hr);
\r
886 hr = grabberOutputPin.Disconnect();
\r
887 DsError.ThrowExceptionForHR(hr);
\r
888 hr = grabberOutputPin.Connect(nullRendererInputPin, null);
\r
889 DsError.ThrowExceptionForHR(hr);
\r
892 if( audioRenderer != null && audioRendererInputPin != null )
\r
894 C共通.tCOMオブジェクトを解放する(ref connectedOutputPin);
\r
896 // 既存のレンダラにつながっているピン対を取得
\r
897 hr = audioRendererInputPin.ConnectedTo(out connectedOutputPin);
\r
898 DsError.ThrowExceptionForHR(hr);
\r
900 // それらを切断。前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。
\r
901 hr = audioRendererInputPin.Disconnect();
\r
902 DsError.ThrowExceptionForHR(hr);
\r
903 hr = connectedOutputPin.Disconnect();
\r
904 DsError.ThrowExceptionForHR(hr);
\r
906 // ビデオレンダラをグラフから除去し、ヌルレンダラを追加
\r
907 hr = graphBuilder.RemoveFilter(audioRenderer);
\r
908 DsError.ThrowExceptionForHR(hr);
\r
909 IBaseFilter nullRenderer = new NullRenderer() as IBaseFilter;
\r
910 hr = graphBuilder.AddFilter(nullRenderer, "Audio Null Renderer");
\r
911 DsError.ThrowExceptionForHR(hr);
\r
913 C共通.tCOMオブジェクトを解放する(ref nullRendererInputPin);
\r
914 hr = nullRenderer.FindPin("In", out nullRendererInputPin);
\r
915 DsError.ThrowExceptionForHR(hr);
\r
916 hr = connectedOutputPin.Connect(nullRendererInputPin, null);
\r
917 DsError.ThrowExceptionForHR(hr);
\r
922 C共通.tCOMオブジェクトを解放する(ref connectedOutputPin);
\r
923 C共通.tCOMオブジェクトを解放する(ref videoRendererInputPin);
\r
924 C共通.tCOMオブジェクトを解放する(ref videoRenderer);
\r
925 C共通.tCOMオブジェクトを解放する(ref audioRenderer);
\r
926 C共通.tCOMオブジェクトを解放する(ref audioRendererInputPin);
\r
927 C共通.tCOMオブジェクトを解放する(ref nullRendererInputPin);
\r
928 C共通.tCOMオブジェクトを解放する(ref grabberOutputPin);
\r
929 C共通.tCOMオブジェクトを解放する(ref grabberOutputConnectedPin);
\r
933 private static IPin t最初の入力ピンを探して返す( IBaseFilter baseFilter )
\r
937 IPin firstInputPin = null;
\r
940 hr = baseFilter.EnumPins( out ePins );
\r
941 DsError.ThrowExceptionForHR( hr );
\r
944 var pins = new IPin[ 1 ];
\r
945 while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
\r
947 PinInfo pinfo = new PinInfo() { filter = null };
\r
950 hr = pins[ 0 ].QueryPinInfo( out pinfo );
\r
951 DsError.ThrowExceptionForHR( hr );
\r
953 if( pinfo.dir == PinDirection.Input )
\r
955 firstInputPin = pins[ 0 ];
\r
961 if( pinfo.filter != null )
\r
962 C共通.tCOMオブジェクトを解放する( ref pinfo.filter );
\r
963 DsUtils.FreePinInfo( pinfo );
\r
965 if( firstInputPin == null )
\r
966 C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
\r
972 C共通.tCOMオブジェクトを解放する( ref ePins );
\r
975 return firstInputPin;
\r
977 private static void SearchMMRenderers( IFilterGraph graph, out IBaseFilter videoRenderer, out IPin inputVPin, out IBaseFilter audioRenderer, out IPin inputAPin )
\r
980 string strVRフィルタ名 = null;
\r
981 string strVRピンID = null;
\r
982 string strARフィルタ名 = null;
\r
983 string strARピンID = null;
\r
985 // ビデオレンダラと入力ピンを探し、そのフィルタ名とピンIDを控える。
\r
987 IEnumFilters eFilters;
\r
988 hr = graph.EnumFilters( out eFilters );
\r
989 DsError.ThrowExceptionForHR( hr );
\r
992 var filters = new IBaseFilter[ 1 ];
\r
993 while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
\r
997 #region [ 出力ピンがない(レンダラである)ことを確認する。]
\r
998 //-----------------
\r
1000 bool b出力ピンがある = false;
\r
1002 hr = filters[ 0 ].EnumPins( out ePins );
\r
1003 DsError.ThrowExceptionForHR( hr );
\r
1006 var pins = new IPin[ 1 ];
\r
1007 while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
\r
1015 hr = pins[ 0 ].QueryDirection( out dir );
\r
1016 DsError.ThrowExceptionForHR( hr );
\r
1017 if( dir == PinDirection.Output )
\r
1022 C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
\r
1028 C共通.tCOMオブジェクトを解放する( ref ePins );
\r
1032 continue; // 次のフィルタへ
\r
1034 //-----------------
\r
1036 #region [ 接続中の入力ピンが MEDIATYPE_Video に対応していたら、フィルタ名とピンIDを取得する。]
\r
1037 //-----------------
\r
1038 hr = filters[ 0 ].EnumPins( out ePins );
\r
1039 DsError.ThrowExceptionForHR( hr );
\r
1042 var pins = new IPin[ 1 ];
\r
1043 while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
\r
1047 if( !string.IsNullOrEmpty( strVRフィルタ名 ) )
\r
1050 var mediaType = new AMMediaType();
\r
1052 #region [ 現在接続中の MediaType を取得。つながってなければ次のピンへ。]
\r
1053 //-----------------
\r
1054 hr = pins[ 0 ].ConnectionMediaType( mediaType );
\r
1055 if( hr == CWin32.VFW_E_NOT_CONNECTED )
\r
1056 continue; // つながってない
\r
1057 DsError.ThrowExceptionForHR( hr );
\r
1058 //-----------------
\r
1063 if( mediaType.majorType.Equals( MediaType.Video ) )
\r
1065 #region [ フィルタ名取得!]
\r
1066 //-----------------
\r
1067 FilterInfo filterInfo;
\r
1068 hr = filters[ 0 ].QueryFilterInfo( out filterInfo );
\r
1069 DsError.ThrowExceptionForHR( hr );
\r
1070 strVRフィルタ名 = filterInfo.achName;
\r
1071 C共通.tCOMオブジェクトを解放する( ref filterInfo.pGraph );
\r
1072 //-----------------
\r
1074 #region [ ピンID取得!]
\r
1075 //-----------------
\r
1076 hr = pins[ 0 ].QueryId( out strVRピンID );
\r
1077 DsError.ThrowExceptionForHR( hr );
\r
1078 //-----------------
\r
1081 else if( mediaType.majorType.Equals( MediaType.Audio ) )
\r
1083 FilterInfo filterInfo;
\r
1084 hr = filters[0].QueryFilterInfo(out filterInfo);
\r
1085 DsError.ThrowExceptionForHR(hr);
\r
1086 strARフィルタ名 = filterInfo.achName;
\r
1087 C共通.tCOMオブジェクトを解放する(ref filterInfo.pGraph);
\r
1088 hr = pins[0].QueryId(out strARピンID);
\r
1089 DsError.ThrowExceptionForHR(hr);
\r
1094 DsUtils.FreeAMMediaType( mediaType );
\r
1099 C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
\r
1105 C共通.tCOMオブジェクトを解放する( ref ePins );
\r
1108 //-----------------
\r
1113 C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
\r
1119 C共通.tCOMオブジェクトを解放する( ref eFilters );
\r
1123 // 改めてフィルタ名とピンIDからこれらのインターフェースを取得し、戻り値として返す。
\r
1125 videoRenderer = null;
\r
1127 audioRenderer = null;
\r
1130 if( !string.IsNullOrEmpty( strVRフィルタ名 ) )
\r
1132 hr = graph.FindFilterByName( strVRフィルタ名, out videoRenderer );
\r
1133 DsError.ThrowExceptionForHR( hr );
\r
1135 hr = videoRenderer.FindPin( strVRピンID, out inputVPin );
\r
1136 DsError.ThrowExceptionForHR( hr );
\r
1139 if( !string.IsNullOrEmpty( strARフィルタ名 ) )
\r
1141 hr = graph.FindFilterByName(strARフィルタ名, out audioRenderer);
\r
1142 DsError.ThrowExceptionForHR(hr);
\r
1144 hr = audioRenderer.FindPin(strARピンID, out inputAPin);
\r
1145 DsError.ThrowExceptionForHR(hr);
\r
1148 private static IBaseFilter tオーディオレンダラを探して返す( IFilterGraph graph )
\r
1151 IBaseFilter audioRenderer = null;
\r
1153 IEnumFilters eFilters;
\r
1154 hr = graph.EnumFilters( out eFilters );
\r
1155 DsError.ThrowExceptionForHR( hr );
\r
1158 var filters = new IBaseFilter[ 1 ];
\r
1159 while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
\r
1161 if( ( filters[ 0 ] as IAMAudioRendererStats ) != null )
\r
1163 audioRenderer = filters[ 0 ];
\r
1167 C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
\r
1172 C共通.tCOMオブジェクトを解放する( ref eFilters );
\r
1174 return audioRenderer;
\r
1178 #region [ 静的インスタンス管理 ]
\r
1179 //-----------------
\r
1180 public const int nインスタンスIDの最大数 = 100;
\r
1181 protected static Dictionary<int, CDirectShow> dicインスタンス = new Dictionary<int, CDirectShow>(); // <インスタンスID, そのIDを持つインスタンス>
\r
1183 public static CDirectShow tインスタンスを返す( int nインスタンスID )
\r
1185 if( CDirectShow.dicインスタンス.ContainsKey( nインスタンスID ) )
\r
1186 return CDirectShow.dicインスタンス[ nインスタンスID ];
\r
1190 protected static void tインスタンスを登録する( CDirectShow ds )
\r
1192 for( int i = 1; i < CDirectShow.nインスタンスIDの最大数; i++ )
\r
1194 if( !CDirectShow.dicインスタンス.ContainsKey( i ) ) // 空いている番号を使う。
\r
1197 CDirectShow.dicインスタンス.Add( i, ds );
\r
1202 protected static void tインスタンスを解放する( int nインスタンスID )
\r
1204 if( CDirectShow.dicインスタンス.ContainsKey( nインスタンスID ) )
\r
1205 CDirectShow.dicインスタンス.Remove( nインスタンスID );
\r
1207 //-----------------
\r
1210 #region [ Dispose-Finalize パターン実装 ]
\r
1211 //-----------------
\r
1212 public virtual void Dispose()
\r
1214 this.Dispose( true );
\r
1215 GC.SuppressFinalize( this ); // ちゃんと Dispose されたので、ファイナライズ不要であることを CLR に伝える。
\r
1217 protected virtual void Dispose( bool bManagedリソースも解放する )
\r
1219 if( bManagedリソースも解放する )
\r
1221 #region [ ROTから解放する。]
\r
1222 //-----------------
\r
1224 C共通.tDisposeする( ref this.rot );
\r
1226 //-----------------
\r
1229 CDirectShow.tインスタンスを解放する( this.nインスタンスID );
\r
1232 #region [ インターフェース参照をなくし、COMオブジェクトを解放する。 ]
\r
1233 //-----------------
\r
1234 if( this.ip != IntPtr.Zero )
\r
1236 Marshal.FreeCoTaskMem( this.ip );
\r
1237 this.ip = IntPtr.Zero;
\r
1240 if( this.MediaCtrl != null )
\r
1242 this.MediaCtrl.Stop();
\r
1243 this.MediaCtrl = null;
\r
1246 if( this.MediaEventEx != null )
\r
1248 this.MediaEventEx.SetNotifyWindow( IntPtr.Zero, 0, IntPtr.Zero );
\r
1249 this.MediaEventEx = null;
\r
1252 if( this.MediaSeeking != null )
\r
1253 this.MediaSeeking = null;
\r
1255 if( this.BasicAudio != null )
\r
1256 this.BasicAudio = null;
\r
1258 C共通.tCOMオブジェクトを解放する( ref this.nullRenderer );
\r
1259 C共通.tCOMオブジェクトを解放する( ref this.memoryRenderer );
\r
1260 C共通.tCOMオブジェクトを解放する( ref this.memoryRendererObject );
\r
1261 C共通.tCOMオブジェクトを解放する( ref this.graphBuilder );
\r
1262 //-----------------
\r
1265 C共通.t完全なガベージコレクションを実施する();
\r
1269 // ファイナライザが呼ばれたということは、Dispose() されなかったということ。
\r
1270 // この場合、Managed リソースは先にファイナライズされている可能性があるので、Unmamaed リソースのみを解放する。
\r
1272 this.Dispose( false );
\r
1274 //-----------------
\r
1277 #region [ protected ]
\r
1278 //-----------------
\r
1279 protected MemoryRenderer memoryRendererObject = null;
\r
1280 protected IMemoryRenderer memoryRenderer = null;
\r
1281 protected IBaseFilter nullRenderer = null;
\r
1282 protected int n再生一時停止呼び出しの累積回数 = 0;
\r
1283 //-----------------
\r
1286 #region [ private ]
\r
1287 //-----------------
\r
1288 private int _n音量 = 100;
\r
1290 private DsROTEntry rot = null;
\r
1293 // 可能な数のスレッドを使用して画像を転送する。大きい画像ほど有効。多すぎるとプール内のスレッドが空くまで待たされるので注意。
\r
1294 private static int n並列度 = 0; // 0 の場合、最初の生成時に並列度を決定する。
\r
1296 private DGライン描画[] dgライン描画ARGB32;
\r
1297 private DGライン描画[] dgライン描画XRGB32;
\r
1298 private unsafe delegate void DGライン描画( int n );
\r
1299 private unsafe byte* ptrSnap = null;
\r
1300 private unsafe UInt32** ptrTexture = null;
\r
1302 private unsafe void tライン描画XRGB32( int n )
\r
1304 // Snap は RGB32、Textureは X8R8G8B8
\r
1306 UInt32* ptrTexture = this.ptrTexture[ n ];
\r
1307 for( int y = n; y < this.n高さpx; y += CDirectShow.n並列度 )
\r
1309 byte* ptrPixel = ptrSnap + ( ( ( this.n高さpx - y ) - 1 ) * this.nスキャンライン幅byte );
\r
1311 // アルファ無視なので一括コピー。CopyMemory() は自前でループ展開するよりも速い。
\r
1312 CWin32.CopyMemory( (void*) ptrTexture, (void*) ptrPixel, (uint) ( this.n幅px * 4 ) );
\r
1314 ptrTexture += this.n幅px * CDirectShow.n並列度;
\r
1317 private unsafe void tライン描画ARGB32( int n )
\r
1319 // Snap は RGB32、Textureは A8R8G8B8
\r
1321 UInt32* ptrTexture = this.ptrTexture[ n ];
\r
1322 for( int y = n; y < this.n高さpx; y += CDirectShow.n並列度 )
\r
1324 UInt32* ptrPixel = (UInt32*) ( ptrSnap + ( ( ( this.n高さpx - y ) - 1 ) * this.nスキャンライン幅byte ) );
\r
1326 //for( int x = 0; x < this.n幅px; x++ )
\r
1327 // *( ptrTexture + x ) = 0xFF000000 | *ptrPixel++;
\r
1328 // ↓ループ展開により高速化。160fps の曲が 200fps まで上がった。
\r
1330 if( this.n幅px == 0 ) goto LEXIT;
\r
1331 UInt32* pt = ptrTexture;
\r
1332 UInt32 nAlpha = 0xFF000000;
\r
1333 int d = ( this.n幅px % 32 );
\r
1337 case 1: goto L031;
\r
1338 case 2: goto L030;
\r
1339 case 3: goto L029;
\r
1340 case 4: goto L028;
\r
1341 case 5: goto L027;
\r
1342 case 6: goto L026;
\r
1343 case 7: goto L025;
\r
1344 case 8: goto L024;
\r
1345 case 9: goto L023;
\r
1346 case 10: goto L022;
\r
1347 case 11: goto L021;
\r
1348 case 12: goto L020;
\r
1349 case 13: goto L019;
\r
1350 case 14: goto L018;
\r
1351 case 15: goto L017;
\r
1352 case 16: goto L016;
\r
1353 case 17: goto L015;
\r
1354 case 18: goto L014;
\r
1355 case 19: goto L013;
\r
1356 case 20: goto L012;
\r
1357 case 21: goto L011;
\r
1358 case 22: goto L010;
\r
1359 case 23: goto L009;
\r
1360 case 24: goto L008;
\r
1361 case 25: goto L007;
\r
1362 case 26: goto L006;
\r
1363 case 27: goto L005;
\r
1364 case 28: goto L004;
\r
1365 case 29: goto L003;
\r
1366 case 30: goto L002;
\r
1367 case 31: goto L001;
\r
1370 L000: *pt++ = nAlpha | *ptrPixel++;
\r
1371 L001: *pt++ = nAlpha | *ptrPixel++;
\r
1372 L002: *pt++ = nAlpha | *ptrPixel++;
\r
1373 L003: *pt++ = nAlpha | *ptrPixel++;
\r
1374 L004: *pt++ = nAlpha | *ptrPixel++;
\r
1375 L005: *pt++ = nAlpha | *ptrPixel++;
\r
1376 L006: *pt++ = nAlpha | *ptrPixel++;
\r
1377 L007: *pt++ = nAlpha | *ptrPixel++;
\r
1378 L008: *pt++ = nAlpha | *ptrPixel++;
\r
1379 L009: *pt++ = nAlpha | *ptrPixel++;
\r
1380 L010: *pt++ = nAlpha | *ptrPixel++;
\r
1381 L011: *pt++ = nAlpha | *ptrPixel++;
\r
1382 L012: *pt++ = nAlpha | *ptrPixel++;
\r
1383 L013: *pt++ = nAlpha | *ptrPixel++;
\r
1384 L014: *pt++ = nAlpha | *ptrPixel++;
\r
1385 L015: *pt++ = nAlpha | *ptrPixel++;
\r
1386 L016: *pt++ = nAlpha | *ptrPixel++;
\r
1387 L017: *pt++ = nAlpha | *ptrPixel++;
\r
1388 L018: *pt++ = nAlpha | *ptrPixel++;
\r
1389 L019: *pt++ = nAlpha | *ptrPixel++;
\r
1390 L020: *pt++ = nAlpha | *ptrPixel++;
\r
1391 L021: *pt++ = nAlpha | *ptrPixel++;
\r
1392 L022: *pt++ = nAlpha | *ptrPixel++;
\r
1393 L023: *pt++ = nAlpha | *ptrPixel++;
\r
1394 L024: *pt++ = nAlpha | *ptrPixel++;
\r
1395 L025: *pt++ = nAlpha | *ptrPixel++;
\r
1396 L026: *pt++ = nAlpha | *ptrPixel++;
\r
1397 L027: *pt++ = nAlpha | *ptrPixel++;
\r
1398 L028: *pt++ = nAlpha | *ptrPixel++;
\r
1399 L029: *pt++ = nAlpha | *ptrPixel++;
\r
1400 L030: *pt++ = nAlpha | *ptrPixel++;
\r
1401 L031: *pt++ = nAlpha | *ptrPixel++;
\r
1402 if( ( pt - ptrTexture ) < this.n幅px ) goto L000;
\r
1405 ptrTexture += this.n幅px * CDirectShow.n並列度;
\r
1408 //-----------------
\r