1
0
mirror of synced 2024-12-28 23:33:19 +01:00
OpenTaiko/FDK19/コード/05.DirectShow/CDirectShow.cs
Mr-Ojii c6d1b8f443
Remove dependency on SlimDX ()
* Use the using directive for SlimDX namespace

* Remove dependency on SlimDX

* Remove dependency on SlimDX
2022-02-08 16:23:41 +01:00

1611 lines
49 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using SharpDX;
using SharpDX.Direct3D9;
using SharpDX.Multimedia;
using DirectShowLib;
namespace FDK
{
/// <summary>
/// <para>DirectShowを用いたクリップ動画音声を扱う。</para>
/// <para>1つのクリップにつき1つの CDirectShow インスタンスを生成する。</para>
/// <para>再生の開始や停止などの操作の他、任意の時点でスナップイメージを取得することができる。</para>
/// </summary>
public class CDirectShow : IDisposable
{
// プロパティ
public const uint WM_DSGRAPHNOTIFY = CWin32.WM_APP + 1;
public enum Eグラフの状態 { , , , , , , }
public Eグラフの状態 eグラフの状態
{
get
{
var status = Eグラフの状態.;
if( this.MediaCtrl != null )
{
FilterState fs;
int hr = this.MediaCtrl.GetState( 0, out fs ); // それなりに重たいので注意。
if( hr == CWin32.E_FAIL )
{
#region [ ]
//-----------------
status = Eグラフの状態.;
//-----------------
#endregion
}
else if( hr == CWin32.VFW_S_STATE_INTERMEDIATE )
{
#region [ ]
//-----------------
switch( fs )
{
case FilterState.Running:
status = Eグラフの状態.;
break;
case FilterState.Paused:
status = Eグラフの状態.;
break;
case FilterState.Stopped:
status = Eグラフの状態.;
break;
default:
status = Eグラフの状態.;
break;
}
//-----------------
#endregion
}
else
{
#region [ ]
//-----------------
switch( fs )
{
case FilterState.Running:
status = Eグラフの状態.;
break;
case FilterState.Paused:
status = Eグラフの状態.;
break;
case FilterState.Stopped:
status = Eグラフの状態.;
break;
default:
status = Eグラフの状態.;
break;
}
//-----------------
#endregion
}
}
return status;
}
}
public bool b再生中;
public bool bループ再生;
public int n幅px
{
get;
protected set;
}
public int n高さpx
{
get;
protected set;
}
public int nスキャンライン幅byte
{
get;
protected set;
}
public int nデータサイズbyte
{
get;
protected set;
}
public bool b上下反転
{
get;
protected set;
}
public bool b音声のみ
{
get;
protected set;
}
public long n現在のグラフの再生位置ms
{
get
{
if( this.MediaSeeking == null )
return 0;
long current;
int hr = this.MediaSeeking.GetCurrentPosition( out current );
DsError.ThrowExceptionForHR( hr );
return (long) ( current / ( 1000.0 * 10.0 ) );
}
}
/// <summary>
/// <para>無音:0100:原音。set のみ。</para>
/// </summary>
public int n音量
{
get
{
return this._n音量;
}
set
{
if( this.BasicAudio == null )
return;
// 値を保存。
this._n音量 = value;
// リニア音量をデシベル音量に変換。
int n音量db = 0;
if( value == 0 )
{
n音量db = -10000; // 完全無音
}
else
{
n音量db = (int) ( ( 20.0 * Math.Log10( ( (double) value ) / 100.0 ) ) * 100.0 );
}
// デシベル音量でグラフの音量を変更。
this.BasicAudio.put_Volume( n音量db );
}
}
/// <summary>
/// <para>左:-100中央:0100:右。set のみ。</para>
/// </summary>
public int n位置
{
set
{
if( this.BasicAudio == null )
return;
// リニア位置をデシベル位置に変換。
int n位置 = Math.Min( Math.Max( value, -100 ), +100 );
int n位置db = 0;
if( n位置 == 0 )
{
n位置db = 0;
}
else if( n位置 == -100 )
{
n位置db = -10000;
}
else if( n位置 == 100 )
{
n位置db = +10000;
}
else if( n位置 < 0 )
{
n位置db = (int) ( ( 20.0 * Math.Log10( ( (double) ( n位置 + 100 ) ) / 100.0 ) ) * 100.0 );
}
else
{
n位置db = (int) ( ( -20.0 * Math.Log10( ( (double) ( 100 - n位置 ) ) / 100.0 ) ) * 100.0 );
}
// デシベル位置でグラフの位置を変更。
this.BasicAudio.put_Balance( n位置db );
}
}
public IMediaControl MediaCtrl;
public IMediaEventEx MediaEventEx;
public IMediaSeeking MediaSeeking;
public IBasicAudio BasicAudio;
public IGraphBuilder graphBuilder;
/// <summary>
/// <para>CDirectShowインスタンスに固有のID。</para>
/// <para>DirectShow イベントをウィンドウに発信する際、MessageID として "WM_APP+インスタンスID" を発信する。</para>
/// <para>これにより、受け側でイベント発信インスタンスを特定することが可能になる。</para>
/// </summary>
public int nインスタンスID
{
get;
protected set;
}
// メソッド
public CDirectShow()
{
}
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;
IBaseFilter audioRenderer;
IPin audioInputPin;
#if MemoryRenderer
CDirectShow.tビデオレンダラとその入力ピンを探して返す( this.graphBuilder, out videoRenderer, out videoInputPin );
#else
CDirectShow.SearchMMRenderers( this.graphBuilder, out videoRenderer, out videoInputPin, out audioRenderer, out audioInputPin );
#endif
if( videoRenderer == null && audioRenderer != null )
this.b音声のみ = true;
else
{
C共通.tCOMオブジェクトを解放する( ref videoInputPin );
C共通.tCOMオブジェクトを解放する( ref videoRenderer );
C共通.tCOMオブジェクトを解放する( ref audioInputPin );
C共通.tCOMオブジェクトを解放する( ref audioRenderer );
}
}
// イメージ情報を取得。
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;
// C共通.tCOMオブジェクトを解放する( 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 )
{
C共通.t例外の詳細をログに出力する( e );
this.Dispose();
throw; // 例外発出。
}
#endif
finally
{
}
}
public void t再生準備開始()
{
if( this.MediaCtrl != null )
{
int hr = this.MediaCtrl.Pause(); // 再生準備を開始する。ここでは準備が完了するまで待たない。
DsError.ThrowExceptionForHR( hr );
}
}
public void t再生開始()
{
if( this.MediaCtrl != null && --this.n再生一時停止呼び出しの累積回数 <= 0 )
{
//this.t遷移完了まで待って状態を取得する(); // 再生準備(だろう)がまだ完了してなければ、待つ。 → 意外と重い処理なので外部で判断して実行するよう変更する。(2011.8.7)
int hr = this.MediaCtrl.Run(); // 再生開始。
DsError.ThrowExceptionForHR( hr );
this.n再生一時停止呼び出しの累積回数 = 0; // 一時停止回数はここでリセットされる。
this.b再生中 = true;
}
}
public void t再生一時停止()
{
if( this.MediaCtrl != null && this.n再生一時停止呼び出しの累積回数 == 0 )
{
int hr = this.MediaCtrl.Pause();
DsError.ThrowExceptionForHR( hr );
}
this.n再生一時停止呼び出しの累積回数++;
this.b再生中 = false;
}
public void t再生停止()
{
if( this.MediaCtrl != null )
{
int hr = this.MediaCtrl.Stop();
DsError.ThrowExceptionForHR( hr );
}
// 次への準備。
//this.t再生位置を変更する( 0.0 ); → より細かく制御するために、FDK外部で制御するように変更。(2011.8.7)
//this.t再生準備開始();
this.n再生一時停止呼び出しの累積回数 = 0; // 停止すると、一時停止呼び出し累積回数はリセットされる。
this.b再生中 = false;
}
public void t再生位置を変更( double db再生位置ms )
{
if( this.MediaSeeking == null )
return;
int hr = this.MediaSeeking.SetPositions(
DsLong.FromInt64( (long) ( db再生位置ms * 1000.0 * 10.0 ) ),
AMSeekingSeekingFlags.AbsolutePositioning,
null,
AMSeekingSeekingFlags.NoPositioning );
DsError.ThrowExceptionForHR( hr );
}
public void t最初から再生開始()
{
this.t再生位置を変更( 0.0 );
this.t再生開始();
}
public Eグラフの状態 t遷移完了まで待って状態を取得する()
{
var status = Eグラフの状態.;
if( this.MediaCtrl != null )
{
FilterState fs;
int hr = this.MediaCtrl.GetState( 1000, out fs ); // 遷移完了まで最大1000ms待つ。
}
return this.eグラフの状態;
}
public unsafe void t現時点における最新のスナップイメージをTextureに転写する( CTexture texture )
{
int hr;
#region [ OK]
//-----------------
if( !this.b再生中 )
return;
//-----------------
#endregion
#region [ ]
//-----------------
if( this.b音声のみ )
return;
//-----------------
#endregion
DataRectangle dr = texture.texture.LockRectangle( 0, LockFlags.Discard );
try
{
if( this.nスキャンライン幅byte == dr.Pitch )
{
#region [ (A) ]
//-----------------
hr = this.memoryRenderer.GetCurrentBuffer( dr.DataPointer, this.nデータサイズbyte );
DsError.ThrowExceptionForHR( hr );
//-----------------
#endregion
}
else
{
this.b上下反転 = false; // こちらの方法では常に正常
#region [ (B) ]
//-----------------
#region [ IMemoryRenderer ]
//-----------------
if( this.ip == IntPtr.Zero )
this.ip = Marshal.AllocCoTaskMem( this.nデータサイズbyte );
hr = this.memoryRenderer.GetCurrentBuffer( this.ip, this.nデータサイズbyte );
DsError.ThrowExceptionForHR( hr );
//-----------------
#endregion
#region [ ]
//-----------------
bool bARGB32 = true;
switch( texture.Format )
{
case Format.A8R8G8B8:
bARGB32 = true;
break;
case Format.X8R8G8B8:
bARGB32 = false;
break;
default:
return; // 未対応のフォーマットは無視。
}
// スレッドプールを使って並列転送する準備。
this.ptrSnap = (byte*) this.ip.ToPointer();
var ptr = stackalloc UInt32*[ CDirectShow.n並列度 ]; // stackallocGC対象外、メソッド終了時に自動開放は、スタック変数相手にしか使えない。
ptr[ 0 ] = (UInt32*) dr.DataPointer.ToPointer(); // ↓
for( int i = 1; i < CDirectShow.n並列度; i++ ) // スタック変数で確保、初期化して…
ptr[ i ] = ptr[ i - 1 ] + this.n幅px; // ↓
this.ptrTexture = ptr; // スタック変数をクラスメンバに渡すこれならOK
// 並列度が1ならシングルスレッド、2以上ならマルチスレッドで転送する。
// → CPUがつの場合、わざわざスレッドプールのスレッドで処理するのは無駄。
if( CDirectShow.n並列度 == 1 )
{
if( bARGB32 )
this.tライン描画ARGB32( 0 );
else
this.tライン描画XRGB32( 0 );
}
else
{
// 転送開始。
var ar = new IAsyncResult[ CDirectShow.n並列度 ];
for( int i = 0; i < CDirectShow.n並列度; i++ )
{
ar[ i ] = ( bARGB32 ) ?
this.dgライン描画ARGB32[ i ].BeginInvoke( i, null, null ) :
this.dgライン描画XRGB32[ i ].BeginInvoke( i, null, null );
}
// 転送完了待ち。
for( int i = 0; i < CDirectShow.n並列度; i++ )
{
if( bARGB32 )
this.dgライン描画ARGB32[ i ].EndInvoke( ar[ i ] );
else
this.dgライン描画XRGB32[ i ].EndInvoke( ar[ i ] );
}
}
this.ptrSnap = null;
this.ptrTexture = null;
//-----------------
#endregion
//-----------------
#endregion
}
}
finally
{
texture.texture.UnlockRectangle( 0 );
}
}
private IntPtr ip = IntPtr.Zero;
public static void tグラフを解析しデバッグ出力する( IGraphBuilder graphBuilder )
{
if( graphBuilder == null )
{
Debug.WriteLine( "指定されたグラフが null です。" );
return;
}
int hr = 0;
IEnumFilters eFilters;
hr = graphBuilder.EnumFilters( out eFilters );
DsError.ThrowExceptionForHR( hr );
{
var filters = new IBaseFilter[ 1 ];
while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
{
FilterInfo filterInfo;
hr = filters[ 0 ].QueryFilterInfo( out filterInfo );
DsError.ThrowExceptionForHR( hr );
{
Debug.WriteLine( filterInfo.achName ); // フィルタ名表示。
if( filterInfo.pGraph != null )
C共通.tCOMオブジェクトを解放する( ref filterInfo.pGraph );
}
IEnumPins ePins;
hr = filters[ 0 ].EnumPins( out ePins );
DsError.ThrowExceptionForHR( hr );
{
var pins = new IPin[ 1 ];
while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
{
PinInfo pinInfo;
hr = pins[ 0 ].QueryPinInfo( out pinInfo );
DsError.ThrowExceptionForHR( hr );
{
Debug.Write( " " + pinInfo.name ); // ピン名表示。
Debug.Write( ( pinInfo.dir == PinDirection.Input ) ? " ← " : " → " );
IPin connectPin;
hr = pins[ 0 ].ConnectedTo( out connectPin );
if( hr != CWin32.S_OK )
Debug.WriteLine( "(未接続)" );
else
{
DsError.ThrowExceptionForHR( hr );
PinInfo connectPinInfo;
hr = connectPin.QueryPinInfo( out connectPinInfo );
DsError.ThrowExceptionForHR( hr );
{
FilterInfo connectFilterInfo;
hr = connectPinInfo.filter.QueryFilterInfo( out connectFilterInfo );
DsError.ThrowExceptionForHR( hr );
{
Debug.Write( "[" + connectFilterInfo.achName + "]." ); // 接続先フィルタ名
if( connectFilterInfo.pGraph != null )
C共通.tCOMオブジェクトを解放する( ref connectFilterInfo.pGraph );
}
Debug.WriteLine( connectPinInfo.name ); // 接続先ピン名
if( connectPinInfo.filter != null )
C共通.tCOMオブジェクトを解放する( ref connectPinInfo.filter );
DsUtils.FreePinInfo( connectPinInfo );
}
C共通.tCOMオブジェクトを解放する( ref connectPin );
}
if( pinInfo.filter != null )
C共通.tCOMオブジェクトを解放する( ref pinInfo.filter );
DsUtils.FreePinInfo( pinInfo );
}
C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
}
}
C共通.tCOMオブジェクトを解放する( ref ePins );
C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
}
}
C共通.tCOMオブジェクトを解放する( ref eFilters );
Debug.Flush();
}
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(); // SlimDX.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
{
C共通.tCOMオブジェクトを解放する( ref nullRendererInputPin );
C共通.tCOMオブジェクトを解放する( ref nullRenderer );
C共通.tCOMオブジェクトを解放する( ref rendererConnectedOutputPin );
C共通.tCOMオブジェクトを解放する( ref rendererInputPin );
C共通.tCOMオブジェクトを解放する( ref audioRenderer );
}
}
public static void ConnectNullRendererFromSampleGrabber(IGraphBuilder graphBuilder, IBaseFilter sampleGrabber)
{
int hr = 0;
IBaseFilter videoRenderer = null;
IPin videoRendererInputPin = null;
IBaseFilter audioRenderer = null;
IPin audioRendererInputPin = null;
IPin connectedOutputPin = null;
IPin nullRendererInputPin = null;
IPin grabberOutputPin = null;
IPin grabberOutputConnectedPin = null;
try
{
// videoRenderer を探す。
CDirectShow.SearchMMRenderers(graphBuilder, out videoRenderer, out videoRendererInputPin, out audioRenderer, out audioRendererInputPin);
if (videoRenderer != null && videoRendererInputPin != null)
{
// 既存のレンダラにつながっているピン対を取得
hr = videoRendererInputPin.ConnectedTo(out connectedOutputPin);
DsError.ThrowExceptionForHR(hr);
// それらを切断。前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。
hr = videoRendererInputPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
hr = connectedOutputPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
// ビデオレンダラをグラフから除去し、ヌルレンダラを追加
hr = graphBuilder.RemoveFilter(videoRenderer);
DsError.ThrowExceptionForHR(hr);
IBaseFilter nullRenderer = new NullRenderer() as IBaseFilter;
hr = graphBuilder.AddFilter(nullRenderer, "Video Null Renderer");
DsError.ThrowExceptionForHR(hr);
// nullRenderer の入力ピンを探す。
hr = nullRenderer.FindPin("In", out nullRendererInputPin);
DsError.ThrowExceptionForHR(hr);
hr = nullRendererInputPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
// グラバの Out と Null Renderer の In を接続する。
hr = sampleGrabber.FindPin("Out", out grabberOutputPin);
DsError.ThrowExceptionForHR(hr);
hr = grabberOutputPin.ConnectedTo(out grabberOutputConnectedPin);
// grabberのoutに何もつながっていない場合(つまり、grabberのoutとrendererのinが直結している場合)は、
// grabberのoutと、別のフィルタのinの間の切断処理を行わない。
if (hr != CWin32.S_OK)
{
//Debug.WriteLine("grabber out: 未接続:");
}
else
{
hr = grabberOutputConnectedPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
hr = grabberOutputPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
}
hr = grabberOutputPin.Connect(nullRendererInputPin, null);
DsError.ThrowExceptionForHR(hr);
}
if ( audioRenderer != null && audioRendererInputPin != null )
{
C共通.tCOMオブジェクトを解放する(ref connectedOutputPin);
// 既存のレンダラにつながっているピン対を取得
hr = audioRendererInputPin.ConnectedTo(out connectedOutputPin);
DsError.ThrowExceptionForHR(hr);
// それらを切断。前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。
hr = audioRendererInputPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
hr = connectedOutputPin.Disconnect();
DsError.ThrowExceptionForHR(hr);
// ビデオレンダラをグラフから除去し、ヌルレンダラを追加
hr = graphBuilder.RemoveFilter(audioRenderer);
DsError.ThrowExceptionForHR(hr);
IBaseFilter nullRenderer = new NullRenderer() as IBaseFilter;
hr = graphBuilder.AddFilter(nullRenderer, "Audio Null Renderer");
DsError.ThrowExceptionForHR(hr);
C共通.tCOMオブジェクトを解放する(ref nullRendererInputPin);
hr = nullRenderer.FindPin("In", out nullRendererInputPin);
DsError.ThrowExceptionForHR(hr);
hr = connectedOutputPin.Connect(nullRendererInputPin, null);
DsError.ThrowExceptionForHR(hr);
}
}
finally
{
C共通.tCOMオブジェクトを解放する(ref connectedOutputPin);
C共通.tCOMオブジェクトを解放する(ref videoRendererInputPin);
C共通.tCOMオブジェクトを解放する(ref videoRenderer);
C共通.tCOMオブジェクトを解放する(ref audioRenderer);
C共通.tCOMオブジェクトを解放する(ref audioRendererInputPin);
C共通.tCOMオブジェクトを解放する(ref nullRendererInputPin);
C共通.tCOMオブジェクトを解放する(ref grabberOutputPin);
C共通.tCOMオブジェクトを解放する(ref grabberOutputConnectedPin);
}
}
private static void SearchMMRenderers( IFilterGraph graph, out IBaseFilter videoRenderer, out IPin inputVPin, out IBaseFilter audioRenderer, out IPin inputAPin )
{
int hr = 0;
string strVRフィルタ名 = null;
string strVRピンID = null;
string strARフィルタ名 = null;
string strARピンID = null;
// ビデオレンダラと入力ピンを探し、そのフィルタ名とピンIDを控える。
IEnumFilters eFilters;
hr = graph.EnumFilters( out eFilters );
DsError.ThrowExceptionForHR( hr );
try
{
var filters = new IBaseFilter[ 1 ];
while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
{
try
{
#region [ ]
//-----------------
IEnumPins ePins;
bool b出力ピンがある = false;
hr = filters[ 0 ].EnumPins( out ePins );
DsError.ThrowExceptionForHR( hr );
try
{
var pins = new IPin[ 1 ];
while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
{
try
{
if( b出力ピンがある )
continue;
PinDirection dir;
hr = pins[ 0 ].QueryDirection( out dir );
DsError.ThrowExceptionForHR( hr );
if( dir == PinDirection.Output )
b出力ピンがある = true;
}
finally
{
C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref ePins );
}
if( b出力ピンがある )
continue; // 次のフィルタへ
//-----------------
#endregion
#region [ MEDIATYPE_Video IDを取得する]
//-----------------
hr = filters[ 0 ].EnumPins( out ePins );
DsError.ThrowExceptionForHR( hr );
try
{
var pins = new IPin[ 1 ];
while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
{
try
{
if( !string.IsNullOrEmpty( strVRフィルタ名 ) )
continue;
var mediaType = new AMMediaType();
#region [ MediaType ]
//-----------------
hr = pins[ 0 ].ConnectionMediaType( mediaType );
if( hr == CWin32.VFW_E_NOT_CONNECTED )
continue; // つながってない
DsError.ThrowExceptionForHR( hr );
//-----------------
#endregion
try
{
if( mediaType.majorType.Equals( MediaType.Video ) )
{
#region [ ]
//-----------------
FilterInfo filterInfo;
hr = filters[ 0 ].QueryFilterInfo( out filterInfo );
DsError.ThrowExceptionForHR( hr );
strVRフィルタ名 = filterInfo.achName;
C共通.tCOMオブジェクトを解放する( ref filterInfo.pGraph );
//-----------------
#endregion
#region [ ID取得]
//-----------------
hr = pins[ 0 ].QueryId( out strVRピンID );
DsError.ThrowExceptionForHR( hr );
//-----------------
#endregion
}
else if( mediaType.majorType.Equals( MediaType.Audio ) )
{
FilterInfo filterInfo;
hr = filters[0].QueryFilterInfo(out filterInfo);
DsError.ThrowExceptionForHR(hr);
strARフィルタ名 = filterInfo.achName;
C共通.tCOMオブジェクトを解放する(ref filterInfo.pGraph);
hr = pins[0].QueryId(out strARピンID);
DsError.ThrowExceptionForHR(hr);
}
}
finally
{
DsUtils.FreeAMMediaType( mediaType );
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref ePins );
}
//-----------------
#endregion
}
finally
{
C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref eFilters );
}
// 改めてフィルタ名とピンIDからこれらのインターフェースを取得し、戻り値として返す。
videoRenderer = null;
inputVPin = null;
audioRenderer = null;
inputAPin = null;
if( !string.IsNullOrEmpty( strVRフィルタ名 ) )
{
hr = graph.FindFilterByName( strVRフィルタ名, out videoRenderer );
DsError.ThrowExceptionForHR( hr );
hr = videoRenderer.FindPin( strVRピンID, out inputVPin );
DsError.ThrowExceptionForHR( hr );
}
if( !string.IsNullOrEmpty( strARフィルタ名 ) )
{
hr = graph.FindFilterByName(strARフィルタ名, out audioRenderer);
DsError.ThrowExceptionForHR(hr);
hr = audioRenderer.FindPin(strARピンID, out inputAPin);
DsError.ThrowExceptionForHR(hr);
}
}
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
{
C共通.tCOMオブジェクトを解放する( ref connectedOutputPin );
C共通.tCOMオブジェクトを解放する( ref renderInputPin );
C共通.tCOMオブジェクトを解放する( ref videoRenderer );
}
}
private static IPin t最初の入力ピンを探して返す( IBaseFilter baseFilter )
{
int hr = 0;
IPin firstInputPin = null;
IEnumPins ePins;
hr = baseFilter.EnumPins( out ePins );
DsError.ThrowExceptionForHR( hr );
try
{
var pins = new IPin[ 1 ];
while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
{
PinInfo pinfo = new PinInfo() { filter = null };
try
{
hr = pins[ 0 ].QueryPinInfo( out pinfo );
DsError.ThrowExceptionForHR( hr );
if( pinfo.dir == PinDirection.Input )
{
firstInputPin = pins[ 0 ];
break;
}
}
finally
{
if( pinfo.filter != null )
C共通.tCOMオブジェクトを解放する( ref pinfo.filter );
DsUtils.FreePinInfo( pinfo );
if( firstInputPin == null )
C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref ePins );
}
return firstInputPin;
}
private static void tビデオレンダラとその入力ピンを探して返す( IFilterGraph graph, out IBaseFilter videoRenderer, out IPin inputPin )
{
int hr = 0;
string strフィルタ名 = null;
string strピンID = null;
// ビデオレンダラと入力ピンを探し、そのフィルタ名とピンIDを控える。
IEnumFilters eFilters;
hr = graph.EnumFilters( out eFilters );
DsError.ThrowExceptionForHR( hr );
try
{
var filters = new IBaseFilter[ 1 ];
while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
{
try
{
#region [ ]
//-----------------
IEnumPins ePins;
bool b出力ピンがある = false;
hr = filters[ 0 ].EnumPins( out ePins );
DsError.ThrowExceptionForHR( hr );
try
{
var pins = new IPin[ 1 ];
while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
{
try
{
if( b出力ピンがある )
continue;
PinDirection dir;
hr = pins[ 0 ].QueryDirection( out dir );
DsError.ThrowExceptionForHR( hr );
if( dir == PinDirection.Output )
b出力ピンがある = true;
}
finally
{
C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref ePins );
}
if( b出力ピンがある )
continue; // 次のフィルタへ
//-----------------
#endregion
#region [ MEDIATYPE_Video IDを取得する]
//-----------------
hr = filters[ 0 ].EnumPins( out ePins );
DsError.ThrowExceptionForHR( hr );
try
{
var pins = new IPin[ 1 ];
while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )
{
try
{
if( !string.IsNullOrEmpty( strフィルタ名 ) )
continue;
var mediaType = new AMMediaType();
#region [ MediaType ]
//-----------------
hr = pins[ 0 ].ConnectionMediaType( mediaType );
if( hr == CWin32.VFW_E_NOT_CONNECTED )
continue; // つながってない
DsError.ThrowExceptionForHR( hr );
//-----------------
#endregion
try
{
if( mediaType.majorType.Equals( MediaType.Video ) )
{
#region [ ]
//-----------------
FilterInfo filterInfo;
hr = filters[ 0 ].QueryFilterInfo( out filterInfo );
DsError.ThrowExceptionForHR( hr );
strフィルタ名 = filterInfo.achName;
C共通.tCOMオブジェクトを解放する( ref filterInfo.pGraph );
//-----------------
#endregion
#region [ ID取得]
//-----------------
hr = pins[ 0 ].QueryId( out strピンID );
DsError.ThrowExceptionForHR( hr );
//-----------------
#endregion
continue; // 次のピンへ。
}
}
finally
{
DsUtils.FreeAMMediaType( mediaType );
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref ePins );
}
//-----------------
#endregion
}
finally
{
C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
}
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref eFilters );
}
// 改めてフィルタ名とピンIDからこれらのインターフェースを取得し、戻り値として返す。
videoRenderer = null;
inputPin = null;
if( !string.IsNullOrEmpty( strフィルタ名 ) )
{
hr = graph.FindFilterByName( strフィルタ名, out videoRenderer );
DsError.ThrowExceptionForHR( hr );
hr = videoRenderer.FindPin( strピンID, out inputPin );
DsError.ThrowExceptionForHR( hr );
}
}
private static IBaseFilter tオーディオレンダラを探して返す( IFilterGraph graph )
{
int hr = 0;
IBaseFilter audioRenderer = null;
IEnumFilters eFilters;
hr = graph.EnumFilters( out eFilters );
DsError.ThrowExceptionForHR( hr );
try
{
var filters = new IBaseFilter[ 1 ];
while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )
{
if( ( filters[ 0 ] as IAMAudioRendererStats ) != null )
{
audioRenderer = filters[ 0 ];
break;
}
C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );
}
}
finally
{
C共通.tCOMオブジェクトを解放する( ref eFilters );
}
return audioRenderer;
}
#region [ ]
//-----------------
public const int nインスタンスIDの最大数 = 100;
protected static Dictionary<int, CDirectShow> dicインスタンス = new Dictionary<int, CDirectShow>(); // <インスタンスID, そのIDを持つインスタンス>
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;
}
}
}
protected static void tインスタンスを解放する( int nインスタンスID )
{
if( CDirectShow.dicインスタンス.ContainsKey( nインスタンスID ) )
CDirectShow.dicインスタンス.Remove( nインスタンスID );
}
//-----------------
#endregion
#region [ Dispose-Finalize ]
//-----------------
public virtual void Dispose()
{
this.Dispose( true );
GC.SuppressFinalize( this ); // ちゃんと Dispose されたので、ファイナライズ不要であることを CLR に伝える。
}
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;
C共通.tCOMオブジェクトを解放する( ref this.nullRenderer );
C共通.tCOMオブジェクトを解放する( ref this.memoryRenderer );
C共通.tCOMオブジェクトを解放する( ref this.memoryRendererObject );
C共通.tCOMオブジェクトを解放する( ref this.graphBuilder );
//-----------------
#endregion
C共通.t完全なガベージコレクションを実施する();
}
~CDirectShow()
{
// ファイナライザが呼ばれたということは、Dispose() されなかったということ。
// この場合、Managed リソースは先にファイナライズされている可能性があるので、Unmamaed リソースのみを解放する。
this.Dispose( false );
}
//-----------------
#endregion
#region [ protected ]
//-----------------
protected MemoryRenderer memoryRendererObject = null;
protected IMemoryRenderer memoryRenderer = null;
protected IBaseFilter nullRenderer = null;
protected int n再生一時停止呼び出しの累積回数 = 0;
//-----------------
#endregion
#region [ private ]
//-----------------
private int _n音量 = 100;
#if DEBUG
private DsROTEntry rot = null;
#endif
// 可能な数のスレッドを使用して画像を転送する。大きい画像ほど有効。多すぎるとプール内のスレッドが空くまで待たされるので注意。
private static int n並列度 = 0; // 0 の場合、最初の生成時に並列度を決定する。
private DGライン描画[] dgライン描画ARGB32;
private DGライン描画[] dgライン描画XRGB32;
private unsafe delegate void DGライン描画( int n );
private unsafe byte* ptrSnap = null;
private unsafe UInt32** ptrTexture = null;
private unsafe void tライン描画XRGB32( int n )
{
// Snap は RGB32、Textureは X8R8G8B8
UInt32* ptrTexture = this.ptrTexture[ n ];
for( int y = n; y < this.n高さpx; y += CDirectShow.n並列度 )
{
byte* ptrPixel = ptrSnap + ( ( ( this.n高さpx - y ) - 1 ) * this.nスキャンライン幅byte );
// アルファ無視なので一括コピー。CopyMemory() は自前でループ展開するよりも速い。
CWin32.CopyMemory( (void*) ptrTexture, (void*) ptrPixel, (uint) ( this.n幅px * 4 ) );
ptrTexture += this.n幅px * CDirectShow.n並列度;
}
}
private unsafe void tライン描画ARGB32( int n )
{
// Snap は RGB32、Textureは A8R8G8B8
UInt32* ptrTexture = this.ptrTexture[ n ];
for( int y = n; y < this.n高さpx; y += CDirectShow.n並列度 )
{
UInt32* ptrPixel = (UInt32*) ( ptrSnap + ( ( ( this.n高さpx - y ) - 1 ) * this.nスキャンライン幅byte ) );
//for( int x = 0; x < this.n幅px; x++ )
// *( ptrTexture + x ) = 0xFF000000 | *ptrPixel++;
// ↓ループ展開により高速化。160fps の曲が 200fps まで上がった。
if( this.n幅px == 0 ) goto LEXIT;
UInt32* pt = ptrTexture;
UInt32 nAlpha = 0xFF000000;
int d = ( this.n幅px % 32 );
switch( d )
{
case 1: goto L031;
case 2: goto L030;
case 3: goto L029;
case 4: goto L028;
case 5: goto L027;
case 6: goto L026;
case 7: goto L025;
case 8: goto L024;
case 9: goto L023;
case 10: goto L022;
case 11: goto L021;
case 12: goto L020;
case 13: goto L019;
case 14: goto L018;
case 15: goto L017;
case 16: goto L016;
case 17: goto L015;
case 18: goto L014;
case 19: goto L013;
case 20: goto L012;
case 21: goto L011;
case 22: goto L010;
case 23: goto L009;
case 24: goto L008;
case 25: goto L007;
case 26: goto L006;
case 27: goto L005;
case 28: goto L004;
case 29: goto L003;
case 30: goto L002;
case 31: goto L001;
}
L000: *pt++ = nAlpha | *ptrPixel++;
L001: *pt++ = nAlpha | *ptrPixel++;
L002: *pt++ = nAlpha | *ptrPixel++;
L003: *pt++ = nAlpha | *ptrPixel++;
L004: *pt++ = nAlpha | *ptrPixel++;
L005: *pt++ = nAlpha | *ptrPixel++;
L006: *pt++ = nAlpha | *ptrPixel++;
L007: *pt++ = nAlpha | *ptrPixel++;
L008: *pt++ = nAlpha | *ptrPixel++;
L009: *pt++ = nAlpha | *ptrPixel++;
L010: *pt++ = nAlpha | *ptrPixel++;
L011: *pt++ = nAlpha | *ptrPixel++;
L012: *pt++ = nAlpha | *ptrPixel++;
L013: *pt++ = nAlpha | *ptrPixel++;
L014: *pt++ = nAlpha | *ptrPixel++;
L015: *pt++ = nAlpha | *ptrPixel++;
L016: *pt++ = nAlpha | *ptrPixel++;
L017: *pt++ = nAlpha | *ptrPixel++;
L018: *pt++ = nAlpha | *ptrPixel++;
L019: *pt++ = nAlpha | *ptrPixel++;
L020: *pt++ = nAlpha | *ptrPixel++;
L021: *pt++ = nAlpha | *ptrPixel++;
L022: *pt++ = nAlpha | *ptrPixel++;
L023: *pt++ = nAlpha | *ptrPixel++;
L024: *pt++ = nAlpha | *ptrPixel++;
L025: *pt++ = nAlpha | *ptrPixel++;
L026: *pt++ = nAlpha | *ptrPixel++;
L027: *pt++ = nAlpha | *ptrPixel++;
L028: *pt++ = nAlpha | *ptrPixel++;
L029: *pt++ = nAlpha | *ptrPixel++;
L030: *pt++ = nAlpha | *ptrPixel++;
L031: *pt++ = nAlpha | *ptrPixel++;
if( ( pt - ptrTexture ) < this.n幅px ) goto L000;
LEXIT:
ptrTexture += this.n幅px * CDirectShow.n並列度;
}
}
//-----------------
#endregion
}
}