OSDN Git Service

9b91b9c874bc7ca680693040ce21e0bacc9033d9
[dtxmania/dtxmania.git] / FDK17プロジェクト / コード / 05.DirectShow / CDirectShow.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Diagnostics;\r
4 using System.Runtime.InteropServices;\r
5 using System.IO;\r
6 using System.Drawing;\r
7 using System.Drawing.Imaging;\r
8 using System.Threading;\r
9 using SharpDX;\r
10 using SharpDX.Direct3D9;\r
11 using SharpDX.Multimedia;\r
12 using DirectShowLib;\r
13 \r
14 namespace FDK\r
15 {\r
16         /// <summary>\r
17         /// <para>DirectShowを用いたクリップ(動画+音声)を扱う。</para>\r
18         /// <para>1つのクリップにつき1つの CDirectShow インスタンスを生成する。</para>\r
19         /// <para>再生の開始や停止などの操作の他、任意の時点でスナップイメージを取得することができる。</para>\r
20         /// </summary>\r
21         public class CDirectShow : IDisposable\r
22         {\r
23                 // プロパティ\r
24 \r
25                 public const uint WM_DSGRAPHNOTIFY = CWin32.WM_APP + 1;\r
26 \r
27                 public enum Eグラフの状態 { 完全停止中, 再生のみ停止中, 再生中, 完全停止へ遷移中, 再生のみ停止へ遷移中, 再生へ遷移中, 未定 }\r
28                 public Eグラフの状態 eグラフの状態\r
29                 {\r
30                         get\r
31                         {\r
32                                 var status = Eグラフの状態.未定;\r
33 \r
34                                 if( this.MediaCtrl != null )\r
35                                 {\r
36                                         FilterState fs;\r
37                                         int hr = this.MediaCtrl.GetState( 0, out fs );          // それなりに重たいので注意。\r
38 \r
39                                         if( hr == CWin32.E_FAIL )\r
40                                         {\r
41                                                 #region [ 失敗。]\r
42                                                 //-----------------\r
43                                                 status = Eグラフの状態.未定;\r
44                                                 //-----------------\r
45                                                 #endregion\r
46                                         }\r
47                                         else if( hr == CWin32.VFW_S_STATE_INTERMEDIATE )\r
48                                         {\r
49                                                 #region [ 遷移中。]\r
50                                                 //-----------------\r
51                                                 switch( fs )\r
52                                                 {\r
53                                                         case FilterState.Running:\r
54                                                                 status = Eグラフの状態.再生へ遷移中;\r
55                                                                 break;\r
56 \r
57                                                         case FilterState.Paused:\r
58                                                                 status = Eグラフの状態.再生のみ停止へ遷移中;\r
59                                                                 break;\r
60 \r
61                                                         case FilterState.Stopped:\r
62                                                                 status = Eグラフの状態.完全停止へ遷移中;\r
63                                                                 break;\r
64 \r
65                                                         default:\r
66                                                                 status = Eグラフの状態.未定;\r
67                                                                 break;\r
68                                                 }\r
69                                                 //-----------------\r
70                                                 #endregion\r
71                                         }\r
72                                         else\r
73                                         {\r
74                                                 #region [ 安定状態。]\r
75                                                 //-----------------\r
76                                                 switch( fs )\r
77                                                 {\r
78                                                         case FilterState.Running:\r
79                                                                 status = Eグラフの状態.再生中;\r
80                                                                 break;\r
81 \r
82                                                         case FilterState.Paused:\r
83                                                                 status = Eグラフの状態.再生のみ停止中;\r
84                                                                 break;\r
85 \r
86                                                         case FilterState.Stopped:\r
87                                                                 status = Eグラフの状態.完全停止中;\r
88                                                                 break;\r
89 \r
90                                                         default:\r
91                                                                 status = Eグラフの状態.未定;\r
92                                                                 break;\r
93                                                 }\r
94                                                 //-----------------\r
95                                                 #endregion\r
96                                         }\r
97                                 }\r
98                                 return status;\r
99                         }\r
100                 }\r
101                 public bool b再生中;\r
102                 public bool bループ再生;\r
103 \r
104                 public int n幅px\r
105                 {\r
106                         get;\r
107                         protected set;\r
108                 }\r
109                 public int n高さpx\r
110                 {\r
111                         get;\r
112                         protected set;\r
113                 }\r
114                 public int nスキャンライン幅byte\r
115                 {\r
116                         get;\r
117                         protected set;\r
118                 }\r
119                 public int nデータサイズbyte\r
120                 {\r
121                         get;\r
122                         protected set;\r
123                 }\r
124                 public bool b上下反転\r
125                 {\r
126                         get;\r
127                         protected set;\r
128                 }\r
129 \r
130                 public bool b音声のみ\r
131                 {\r
132                         get;\r
133                         protected set;\r
134                 }\r
135                 \r
136                 public long n現在のグラフの再生位置ms\r
137                 {\r
138                         get\r
139                         {\r
140                                 if( this.MediaSeeking == null )\r
141                                         return 0;\r
142 \r
143                                 long current;\r
144                                 int hr = this.MediaSeeking.GetCurrentPosition( out current );\r
145                                 DsError.ThrowExceptionForHR( hr );\r
146                                 return (long) ( current / ( 1000.0 * 10.0 ) );\r
147                         }\r
148                 }\r
149                 /// <summary>\r
150                 /// <para>無音:0~100:原音。set のみ。</para>\r
151                 /// </summary>\r
152                 public int n音量\r
153                 {\r
154                         get\r
155                         {\r
156                                 return this._n音量;\r
157                         }\r
158                         set\r
159                         {\r
160                                 if( this.BasicAudio == null )\r
161                                         return;\r
162 \r
163 \r
164                                 // 値を保存。\r
165 \r
166                                 this._n音量 = value;\r
167 \r
168 \r
169                                 // リニア音量をデシベル音量に変換。\r
170 \r
171                                 int n音量db = 0;\r
172 \r
173                                 if( value == 0 )\r
174                                 {\r
175                                         n音量db = -10000;     // 完全無音\r
176                                 }\r
177                                 else\r
178                                 {\r
179                                         n音量db = (int) ( ( 20.0 * Math.Log10( ( (double) value ) / 100.0 ) ) * 100.0 );\r
180                                 }\r
181 \r
182 \r
183                                 // デシベル音量でグラフの音量を変更。\r
184 \r
185                                 this.BasicAudio.put_Volume( n音量db );\r
186                         }\r
187                 }\r
188                 /// <summary>\r
189                 /// <para>左:-100~中央:0~100:右。set のみ。</para>\r
190                 /// </summary>\r
191                 public int n位置\r
192                 {\r
193                         set\r
194                         {\r
195                                 if( this.BasicAudio == null )\r
196                                         return;\r
197 \r
198                                 // リニア位置をデシベル位置に変換。\r
199 \r
200                                 int n位置 = Math.Min( Math.Max( value, -100 ), +100 );\r
201                                 int n位置db = 0;\r
202 \r
203                                 if( n位置 == 0 )\r
204                                 {\r
205                                         n位置db = 0;\r
206                                 }\r
207                                 else if( n位置 == -100 )\r
208                                 {\r
209                                         n位置db = -10000;\r
210                                 }\r
211                                 else if( n位置 == 100 )\r
212                                 {\r
213                                         n位置db = +10000;\r
214                                 }\r
215                                 else if( n位置 < 0 )\r
216                                 {\r
217                                         n位置db = (int) ( ( 20.0 * Math.Log10( ( (double) ( n位置 + 100 ) ) / 100.0 ) ) * 100.0 );\r
218                                 }\r
219                                 else\r
220                                 {\r
221                                         n位置db = (int) ( ( -20.0 * Math.Log10( ( (double) ( 100 - n位置 ) ) / 100.0 ) ) * 100.0 );\r
222                                 }\r
223 \r
224                                 // デシベル位置でグラフの位置を変更。\r
225 \r
226                                 this.BasicAudio.put_Balance( n位置db );\r
227                         }\r
228                 }\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
234 \r
235                 /// <summary>\r
236                 /// <para>CDirectShowインスタンスに固有のID。</para>\r
237                 /// <para>DirectShow イベントをウィンドウに発信する際、MessageID として "WM_APP+インスタンスID" を発信する。</para>\r
238                 /// <para>これにより、受け側でイベント発信インスタンスを特定することが可能になる。</para>\r
239                 /// </summary>\r
240                 public int nインスタンスID\r
241                 {\r
242                         get;\r
243                         protected set;\r
244                 }\r
245 \r
246 \r
247                 // メソッド\r
248 \r
249                 public CDirectShow()\r
250                 {\r
251                 }\r
252                 public CDirectShow( string fileName, IntPtr hWnd, bool bオーディオレンダラなし )\r
253                 {\r
254                         // 初期化。\r
255 \r
256                         this.n幅px = 0;\r
257                         this.n高さpx = 0;\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
264                         this.b再生中 = false;\r
265                         this.bループ再生 = false;\r
266 \r
267 \r
268                         // 静的リストに登録し、インスタンスIDを得る。\r
269 \r
270                         CDirectShow.tインスタンスを登録する( this );\r
271 \r
272 \r
273                         // 並列処理準備。\r
274 \r
275                         if( CDirectShow.n並列度 == 0 )       // 算出がまだなら算出する。\r
276                                 CDirectShow.n並列度 = Environment.ProcessorCount;    // 並列度=CPU数とする。\r
277 \r
278                         unsafe\r
279                         {\r
280                                 this.dgライン描画ARGB32 = new DGライン描画[ CDirectShow.n並列度 ];\r
281                                 this.dgライン描画XRGB32 = new DGライン描画[ CDirectShow.n並列度 ];\r
282 \r
283                                 for( int i = 0; i < CDirectShow.n並列度; i++ )\r
284                                 {\r
285                                         this.dgライン描画ARGB32[ i ] = new DGライン描画( this.tライン描画ARGB32 );\r
286                                         this.dgライン描画XRGB32[ i ] = new DGライン描画( this.tライン描画XRGB32 );\r
287                                 }\r
288                         }\r
289 \r
290                         try\r
291                         {\r
292                                 int hr = 0;\r
293 \r
294 \r
295                                 // グラフビルダを生成。\r
296 \r
297                                 this.graphBuilder = (IGraphBuilder) new FilterGraph();\r
298 #if DEBUG\r
299                                 // ROT への登録。\r
300                                 this.rot = new DsROTEntry( graphBuilder );\r
301 #endif\r
302 \r
303 \r
304                                 // QueryInterface。存在しなければ null。\r
305 \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
310 \r
311 \r
312                                 // IMemoryRenderer をグラフに挿入。\r
313 \r
314                                 AMMediaType mediaType = null;\r
315 \r
316                                 this.memoryRendererObject = new MemoryRenderer();\r
317                                 this.memoryRenderer = (IMemoryRenderer) this.memoryRendererObject;\r
318                                 var baseFilter = (IBaseFilter) this.memoryRendererObject;\r
319 \r
320                                 hr = this.graphBuilder.AddFilter( baseFilter, "MemoryRenderer" );\r
321                                 DsError.ThrowExceptionForHR( hr );\r
322 \r
323 \r
324                                 // fileName からグラフを自動生成。\r
325 \r
326                                 hr = this.graphBuilder.RenderFile( fileName, null );    // IMediaControl.RenderFile() は推奨されない\r
327                                 DsError.ThrowExceptionForHR( hr );\r
328 \r
329 \r
330                                 // 音声のみ?\r
331 \r
332                                 {\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
339                                         {\r
340                                                 this.b音声のみ = true;\r
341                                         }\r
342                                         else\r
343                                         {\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
348                                         }\r
349                                 }\r
350 \r
351 \r
352                                 // イメージ情報を取得。\r
353 \r
354                                 if( !this.b音声のみ )\r
355                                 {\r
356                                         long n;\r
357                                         int m;\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
368                                 }\r
369 \r
370 \r
371                                 // グラフを修正する。\r
372 \r
373                                 if( bオーディオレンダラなし )\r
374                                 {\r
375                                         WaveFormat dummy1;\r
376                                         byte[] dummy2;\r
377                                         CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する( this.graphBuilder, out dummy1, out dummy2 );\r
378                                 }\r
379 \r
380 \r
381                                 // その他の処理。\r
382 \r
383                                 this.t再生準備開始();     // 1回以上 IMediaControl を呼び出してないと、IReferenceClock は取得できない。\r
384                                 this.t遷移完了まで待って状態を取得する();       // 完全に Pause へ遷移するのを待つ。(環境依存)\r
385 \r
386 \r
387                                 // イベント用ウィンドウハンドルを設定。\r
388 \r
389                                 this.MediaEventEx.SetNotifyWindow( hWnd, (int) WM_DSGRAPHNOTIFY, new IntPtr( this.nインスタンスID ) );\r
390                         }\r
391 #if !DEBUG\r
392                         catch( Exception e )\r
393                         {\r
394                                 C共通.t例外の詳細をログに出力する( e );\r
395                                 this.Dispose();\r
396                                 throw;  // 例外発出。\r
397                         }\r
398 #endif\r
399                         finally\r
400                         {\r
401                         }\r
402                 }\r
403 \r
404                 public void t再生準備開始()\r
405                 {\r
406                         if( this.MediaCtrl != null )\r
407                         {\r
408                                 int hr = this.MediaCtrl.Pause();                // 再生準備を開始する。ここでは準備が完了するまで待たない。\r
409                                 DsError.ThrowExceptionForHR( hr );\r
410                         }\r
411                 }\r
412                 public void t再生開始()\r
413                 {\r
414                         if( this.MediaCtrl != null && --this.n再生一時停止呼び出しの累積回数 <= 0 )\r
415                         {\r
416                                 //this.t遷移完了まで待って状態を取得する();             // 再生準備(だろう)がまだ完了してなければ、待つ。     → 意外と重い処理なので外部で判断して実行するよう変更する。(2011.8.7)\r
417 \r
418                                 int hr = this.MediaCtrl.Run();                                  // 再生開始。\r
419                                 DsError.ThrowExceptionForHR( hr );\r
420 \r
421                                 this.n再生一時停止呼び出しの累積回数 = 0;                // 一時停止回数はここでリセットされる。\r
422                                 this.b再生中 = true;\r
423                         }\r
424                 }\r
425                 public void t再生一時停止()\r
426                 {\r
427                         if( this.MediaCtrl != null && this.n再生一時停止呼び出しの累積回数 == 0 )\r
428                         {\r
429                                 int hr = this.MediaCtrl.Pause();\r
430                                 DsError.ThrowExceptionForHR( hr );\r
431                         }\r
432                         this.n再生一時停止呼び出しの累積回数++;\r
433                         this.b再生中 = false;\r
434                 }\r
435                 public void t再生停止()\r
436                 {\r
437                         if( this.MediaCtrl != null )\r
438                         {\r
439                                 int hr = this.MediaCtrl.Stop();\r
440                                 DsError.ThrowExceptionForHR( hr );\r
441                         }\r
442 \r
443                         // 次への準備。\r
444                         //this.t再生位置を変更する( 0.0 );             → より細かく制御するために、FDK外部で制御するように変更。(2011.8.7)\r
445                         //this.t再生準備開始();\r
446 \r
447                         this.n再生一時停止呼び出しの累積回数 = 0;        // 停止すると、一時停止呼び出し累積回数はリセットされる。\r
448                         this.b再生中 = false;\r
449                 }\r
450                 public void t再生位置を変更( double db再生位置ms )\r
451                 {\r
452                         if( this.MediaSeeking == null )\r
453                                 return;\r
454 \r
455                         int hr = this.MediaSeeking.SetPositions(\r
456                                 DsLong.FromInt64( (long) ( db再生位置ms * 1000.0 * 10.0 ) ),\r
457                                 AMSeekingSeekingFlags.AbsolutePositioning,\r
458                                 null,\r
459                                 AMSeekingSeekingFlags.NoPositioning );\r
460 \r
461                         DsError.ThrowExceptionForHR( hr );\r
462                 }\r
463                 public void t最初から再生開始()\r
464                 {\r
465                         this.t再生位置を変更( 0.0 );\r
466                         this.t再生開始();\r
467                 }\r
468                 public Eグラフの状態 t遷移完了まで待って状態を取得する()\r
469                 {\r
470                         var status = Eグラフの状態.未定;\r
471 \r
472                         if( this.MediaCtrl != null )\r
473                         {\r
474                                 FilterState fs;\r
475                                 int hr = this.MediaCtrl.GetState( 1000, out fs );       // 遷移完了まで最大1000ms待つ。\r
476                         }\r
477                         return this.eグラフの状態;\r
478                 }\r
479                 public unsafe void t現時点における最新のスナップイメージをTextureに転写する( CTexture texture )\r
480                 {\r
481                         int hr;\r
482 \r
483                         #region [ 再生してないなら何もせず帰還。(一時停止中はOK。)]\r
484                         //-----------------\r
485                         if( !this.b再生中 )\r
486                                 return;\r
487                         //-----------------\r
488                         #endregion\r
489                         #region [ 音声のみなら何もしない。]\r
490                         //-----------------\r
491                         if( this.b音声のみ )\r
492                                 return;\r
493                         //-----------------\r
494                         #endregion\r
495 \r
496                         DataRectangle dr = texture.texture.LockRectangle( 0, LockFlags.Discard );\r
497                         try\r
498                         {\r
499                                 if( this.nスキャンライン幅byte == dr.Pitch )\r
500                                 {\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
506                                         #endregion\r
507                                 }\r
508                                 else\r
509                                 {\r
510                                         this.b上下反転 = false;             // こちらの方法では常に正常\r
511 \r
512                                         #region [ (B) ピッチが合わないので、メモリに転送してからテクスチャに転送する。]\r
513                                         //-----------------\r
514 \r
515                                         #region [ IMemoryRenderer からバッファにイメージデータを読み込む。]\r
516                                         //-----------------\r
517                                         if( this.ip == IntPtr.Zero )\r
518                                                 this.ip = Marshal.AllocCoTaskMem( this.nデータサイズbyte );\r
519 \r
520                                         hr = this.memoryRenderer.GetCurrentBuffer( this.ip, this.nデータサイズbyte );\r
521                                         DsError.ThrowExceptionForHR( hr );\r
522                                         //-----------------\r
523                                         #endregion\r
524 \r
525                                         #region [ テクスチャにスナップイメージを転送。]\r
526                                         //-----------------\r
527                                         bool bARGB32 = true;\r
528 \r
529                                         switch( texture.Format )\r
530                                         {\r
531                                                 case Format.A8R8G8B8:\r
532                                                         bARGB32 = true;\r
533                                                         break;\r
534 \r
535                                                 case Format.X8R8G8B8:\r
536                                                         bARGB32 = false;\r
537                                                         break;\r
538 \r
539                                                 default:\r
540                                                         return;         // 未対応のフォーマットは無視。\r
541                                         }\r
542 \r
543                                         // スレッドプールを使って並列転送する準備。\r
544 \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
551 \r
552 \r
553                                         // 並列度が1ならシングルスレッド、2以上ならマルチスレッドで転送する。\r
554                                         // → CPUが1つの場合、わざわざスレッドプールのスレッドで処理するのは無駄。\r
555 \r
556                                         if( CDirectShow.n並列度 == 1 )\r
557                                         {\r
558                                                 if( bARGB32 )\r
559                                                         this.tライン描画ARGB32( 0 );\r
560                                                 else\r
561                                                         this.tライン描画XRGB32( 0 );\r
562                                         }\r
563                                         else\r
564                                         {\r
565                                                 // 転送開始。\r
566 \r
567                                                 var ar = new IAsyncResult[ CDirectShow.n並列度 ];\r
568                                                 for( int i = 0; i < CDirectShow.n並列度; i++ )\r
569                                                 {\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
573                                                 }\r
574 \r
575 \r
576                                                 // 転送完了待ち。\r
577 \r
578                                                 for( int i = 0; i < CDirectShow.n並列度; i++ )\r
579                                                 {\r
580                                                         if( bARGB32 )\r
581                                                                 this.dgライン描画ARGB32[ i ].EndInvoke( ar[ i ] );\r
582                                                         else\r
583                                                                 this.dgライン描画XRGB32[ i ].EndInvoke( ar[ i ] );\r
584                                                 }\r
585                                         }\r
586 \r
587                                         this.ptrSnap = null;\r
588                                         this.ptrTexture = null;\r
589                                         //-----------------\r
590                                         #endregion\r
591 \r
592                                         //-----------------\r
593                                         #endregion\r
594                                 }\r
595                         }\r
596                         finally\r
597                         {\r
598                                 texture.texture.UnlockRectangle( 0 );\r
599                         }\r
600                 }\r
601 \r
602                 private IntPtr ip = IntPtr.Zero;\r
603 \r
604                 public static void tグラフを解析しデバッグ出力する( IGraphBuilder graphBuilder )\r
605                 {\r
606                         if( graphBuilder == null )\r
607                         {\r
608                                 Debug.WriteLine( "指定されたグラフが null です。" );\r
609                                 return;\r
610                         }\r
611 \r
612                         int hr = 0;\r
613 \r
614                         IEnumFilters eFilters;\r
615                         hr = graphBuilder.EnumFilters( out eFilters );\r
616                         DsError.ThrowExceptionForHR( hr );\r
617                         {\r
618                                 var filters = new IBaseFilter[ 1 ];\r
619                                 while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )\r
620                                 {\r
621                                         FilterInfo filterInfo;\r
622                                         hr = filters[ 0 ].QueryFilterInfo( out filterInfo );\r
623                                         DsError.ThrowExceptionForHR( hr );\r
624                                         {\r
625                                                 Debug.WriteLine( filterInfo.achName );          // フィルタ名表示。\r
626                                                 if( filterInfo.pGraph != null )\r
627                                                         C共通.tCOMオブジェクトを解放する( ref filterInfo.pGraph );\r
628                                         }\r
629 \r
630                                         IEnumPins ePins;\r
631                                         hr = filters[ 0 ].EnumPins( out ePins );\r
632                                         DsError.ThrowExceptionForHR( hr );\r
633                                         {\r
634                                                 var pins = new IPin[ 1 ];\r
635                                                 while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )\r
636                                                 {\r
637                                                         PinInfo pinInfo;\r
638                                                         hr = pins[ 0 ].QueryPinInfo( out pinInfo );\r
639                                                         DsError.ThrowExceptionForHR( hr );\r
640                                                         {\r
641                                                                 Debug.Write( "  " + pinInfo.name );     // ピン名表示。\r
642                                                                 Debug.Write( ( pinInfo.dir == PinDirection.Input ) ? " ← " : " → " );\r
643 \r
644                                                                 IPin connectPin;\r
645                                                                 hr = pins[ 0 ].ConnectedTo( out connectPin );\r
646                                                                 if( hr != CWin32.S_OK )\r
647                                                                         Debug.WriteLine( "(未接続)" );\r
648                                                                 else\r
649                                                                 {\r
650                                                                         DsError.ThrowExceptionForHR( hr );\r
651 \r
652                                                                         PinInfo connectPinInfo;\r
653                                                                         hr = connectPin.QueryPinInfo( out connectPinInfo );\r
654                                                                         DsError.ThrowExceptionForHR( hr );\r
655                                                                         {\r
656                                                                                 FilterInfo connectFilterInfo;\r
657                                                                                 hr = connectPinInfo.filter.QueryFilterInfo( out connectFilterInfo );\r
658                                                                                 DsError.ThrowExceptionForHR( hr );\r
659                                                                                 {\r
660                                                                                         Debug.Write( "[" + connectFilterInfo.achName + "]." );  // 接続先フィルタ名\r
661 \r
662                                                                                         if( connectFilterInfo.pGraph != null )\r
663                                                                                                 C共通.tCOMオブジェクトを解放する( ref connectFilterInfo.pGraph );\r
664                                                                                 }\r
665 \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
670                                                                         }\r
671                                                                         C共通.tCOMオブジェクトを解放する( ref connectPin );\r
672                                                                 }\r
673                                                                 if( pinInfo.filter != null )\r
674                                                                         C共通.tCOMオブジェクトを解放する( ref pinInfo.filter );\r
675                                                                 DsUtils.FreePinInfo( pinInfo );\r
676                                                         }\r
677                                                         C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );\r
678                                                 }\r
679                                         }\r
680                                         C共通.tCOMオブジェクトを解放する( ref ePins );\r
681 \r
682                                         C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );\r
683                                 }\r
684                         }\r
685                         C共通.tCOMオブジェクトを解放する( ref eFilters );\r
686 \r
687                         Debug.Flush();\r
688                 }\r
689                 public static void tオーディオレンダラをNullレンダラに変えてフォーマットを取得する( IGraphBuilder graphBuilder, out WaveFormat wfx, out byte[] wfx拡張データ )\r
690                 {\r
691                         int hr = 0;\r
692 \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
698                         wfx = null;\r
699                         wfx拡張データ = new byte[ 0 ];\r
700 \r
701                         try\r
702                         {\r
703                                 // audioRenderer を探す。\r
704 \r
705                                 audioRenderer = CDirectShow.tオーディオレンダラを探して返す( graphBuilder );\r
706                                 if( audioRenderer == null )\r
707                                         return;         // なかった\r
708 \r
709                                 #region [ 音量ゼロで一度再生する。(オーディオレンダラの入力ピンMediaTypeが、接続時とは異なる「正しいもの」に変わる可能性があるため。)]\r
710                                 //-----------------\r
711                                 {\r
712                                         // ここに来た時点で、グラフのビデオレンダラは無効化(NullRendererへの置換や除去など)しておくこと。\r
713                                         // さもないと、StopWhenReady() 時に一瞬だけ Activeウィンドウが表示されてしまう。\r
714 \r
715                                         var mediaCtrl = (IMediaControl) graphBuilder;\r
716                                         var basicAudio = (IBasicAudio) graphBuilder;\r
717                                         \r
718                                         basicAudio.put_Volume( -10000 );        // 最小音量\r
719                                         \r
720 \r
721                                         // グラフを再生してすぐ止める。(Paused → Stopped へ遷移する)\r
722                                         \r
723                                         mediaCtrl.StopWhenReady();\r
724 \r
725                 \r
726                                         // グラフが Stopped に遷移完了するまで待つ。(StopWhenReady() はグラフが Stopped になるのを待たずに帰ってくる。)\r
727 \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
732                                         \r
733 \r
734                                         // 終了処理。\r
735 \r
736                                         basicAudio.put_Volume( 0 );                     // 最大音量\r
737                                         \r
738                                         basicAudio = null;\r
739                                         mediaCtrl = null;\r
740                                 }\r
741                                 //-----------------\r
742                                 #endregion\r
743 \r
744                                 // audioRenderer の入力ピンを探す。\r
745 \r
746                                 rendererInputPin = t最初の入力ピンを探して返す( audioRenderer );\r
747                                 if( rendererInputPin == null )\r
748                                         return;\r
749 \r
750 \r
751                                 // WAVEフォーマットを取得し、wfx 引数へ格納する。\r
752 \r
753                                 var type = new AMMediaType();\r
754                                 hr = rendererInputPin.ConnectionMediaType( type );\r
755                                 DsError.ThrowExceptionForHR( hr );\r
756                                 try\r
757                                 {\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
762 \r
763                                         wfx = WaveFormat.CreateCustomFormat( (WaveFormatEncoding) wfxTemp.wFormatTag, wfxTemp.nSamplesPerSec, wfxTemp.nChannels, wfxTemp.nAvgBytesPerSec, wfxTemp.nBlockAlign, wfxTemp.wBitsPerSample );\r
764                                         //-----------------\r
765                                         #endregion\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
770 \r
771                                         if( nはみ出しサイズbyte > 0 )\r
772                                         {\r
773                                                 wfx拡張データ = new byte[ nはみ出しサイズbyte ];\r
774                                                 var hGC = GCHandle.Alloc( wfx拡張データ, GCHandleType.Pinned );    // 動くなよー\r
775                                                 unsafe\r
776                                                 {\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
780                                                 }\r
781                                                 hGC.Free();\r
782                                         }\r
783                                         //-----------------\r
784                                         #endregion\r
785                                 }\r
786                                 finally\r
787                                 {\r
788                                         if( type != null )\r
789                                                 DsUtils.FreeAMMediaType( type );\r
790                                 }\r
791 \r
792 \r
793                                 // audioRenderer につながる出力ピンを探す。\r
794 \r
795                                 hr = rendererInputPin.ConnectedTo( out rendererConnectedOutputPin );\r
796                                 DsError.ThrowExceptionForHR( hr );\r
797 \r
798 \r
799                                 // audioRenderer をグラフから切断する。\r
800 \r
801                                 rendererInputPin.Disconnect();\r
802                                 rendererConnectedOutputPin.Disconnect();\r
803 \r
804 \r
805                                 // audioRenderer をグラフから除去する。\r
806 \r
807                                 hr = graphBuilder.RemoveFilter( audioRenderer );\r
808                                 DsError.ThrowExceptionForHR( hr );\r
809 \r
810 \r
811                                 // nullRenderer を作成し、グラフに追加する。\r
812 \r
813                                 nullRenderer = (IBaseFilter) new NullRenderer();\r
814                                 hr = graphBuilder.AddFilter( nullRenderer, "Audio Null Renderer" );\r
815                                 DsError.ThrowExceptionForHR( hr );\r
816 \r
817 \r
818                                 // nullRenderer の入力ピンを探す。\r
819 \r
820                                 hr = nullRenderer.FindPin( "In", out nullRendererInputPin );\r
821                                 DsError.ThrowExceptionForHR( hr );\r
822 \r
823 \r
824                                 // nullRenderer をグラフに接続する。\r
825 \r
826                                 hr = rendererConnectedOutputPin.Connect( nullRendererInputPin, null );\r
827                                 DsError.ThrowExceptionForHR( hr );\r
828                         }\r
829                         finally\r
830                         {\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
836                         }\r
837                 }\r
838                 public static void ConnectNullRendererFromSampleGrabber(IGraphBuilder graphBuilder, IBaseFilter sampleGrabber)\r
839                 {\r
840                         int hr = 0;\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
849 \r
850                         try\r
851                         {\r
852                                 // videoRenderer を探す。\r
853                                 CDirectShow.SearchMMRenderers(graphBuilder, out videoRenderer, out videoRendererInputPin, out audioRenderer, out audioRendererInputPin);\r
854                                 if (videoRenderer != null && audioRendererInputPin != null)\r
855                                 {\r
856                                         // 既存のレンダラにつながっているピン対を取得\r
857                                         hr = videoRendererInputPin.ConnectedTo(out connectedOutputPin);\r
858                                         DsError.ThrowExceptionForHR(hr);\r
859 \r
860                                         // それらを切断。前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。\r
861                                         hr = videoRendererInputPin.Disconnect();\r
862                                         DsError.ThrowExceptionForHR(hr);\r
863                                         hr = connectedOutputPin.Disconnect();\r
864                                         DsError.ThrowExceptionForHR(hr);\r
865 \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
872 \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
878 \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
890                                 }\r
891 \r
892                                 if( audioRenderer != null && audioRendererInputPin != null )\r
893                                 {\r
894                                         C共通.tCOMオブジェクトを解放する(ref connectedOutputPin);\r
895 \r
896                                         // 既存のレンダラにつながっているピン対を取得\r
897                                         hr = audioRendererInputPin.ConnectedTo(out connectedOutputPin);\r
898                                         DsError.ThrowExceptionForHR(hr);\r
899 \r
900                                         // それらを切断。前段の出力ピンとビデオレンダラの入力ピンを切断する。双方向から切断しないとグラフから切り離されないので注意。\r
901                                         hr = audioRendererInputPin.Disconnect();\r
902                                         DsError.ThrowExceptionForHR(hr);\r
903                                         hr = connectedOutputPin.Disconnect();\r
904                                         DsError.ThrowExceptionForHR(hr);\r
905 \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
912 \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
918                                 }\r
919                         }\r
920                         finally\r
921                         {\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
930                         }\r
931                 }\r
932 \r
933                 private static IPin t最初の入力ピンを探して返す( IBaseFilter baseFilter )\r
934                 {\r
935                         int hr = 0;\r
936 \r
937                         IPin firstInputPin = null;\r
938 \r
939                         IEnumPins ePins;\r
940                         hr = baseFilter.EnumPins( out ePins );\r
941                         DsError.ThrowExceptionForHR( hr );\r
942                         try\r
943                         {\r
944                                 var pins = new IPin[ 1 ];\r
945                                 while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )\r
946                                 {\r
947                                         PinInfo pinfo = new PinInfo() { filter = null };\r
948                                         try\r
949                                         {\r
950                                                 hr = pins[ 0 ].QueryPinInfo( out pinfo );\r
951                                                 DsError.ThrowExceptionForHR( hr );\r
952 \r
953                                                 if( pinfo.dir == PinDirection.Input )\r
954                                                 {\r
955                                                         firstInputPin = pins[ 0 ];\r
956                                                         break;\r
957                                                 }\r
958                                         }\r
959                                         finally\r
960                                         {\r
961                                                 if( pinfo.filter != null )\r
962                                                         C共通.tCOMオブジェクトを解放する( ref pinfo.filter );\r
963                                                 DsUtils.FreePinInfo( pinfo );\r
964 \r
965                                                 if( firstInputPin == null )\r
966                                                         C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );\r
967                                         }\r
968                                 }\r
969                         }\r
970                         finally\r
971                         {\r
972                                 C共通.tCOMオブジェクトを解放する( ref ePins );\r
973                         }\r
974 \r
975                         return firstInputPin;\r
976                 }\r
977                 private static void SearchMMRenderers( IFilterGraph graph, out IBaseFilter videoRenderer, out IPin inputVPin, out IBaseFilter audioRenderer, out IPin inputAPin )\r
978                 {\r
979                         int hr = 0;\r
980                         string strVRフィルタ名 = null;\r
981                         string strVRピンID = null;\r
982                         string strARフィルタ名 = null;\r
983                         string strARピンID = null;\r
984 \r
985                         // ビデオレンダラと入力ピンを探し、そのフィルタ名とピンIDを控える。\r
986 \r
987                         IEnumFilters eFilters;\r
988                         hr = graph.EnumFilters( out eFilters );\r
989                         DsError.ThrowExceptionForHR( hr );\r
990                         try\r
991                         {\r
992                                 var filters = new IBaseFilter[ 1 ];\r
993                                 while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )\r
994                                 {\r
995                                         try\r
996                                         {\r
997                                                 #region [ 出力ピンがない(レンダラである)ことを確認する。]\r
998                                                 //-----------------\r
999                                                 IEnumPins ePins;\r
1000                                                 bool b出力ピンがある = false;\r
1001 \r
1002                                                 hr = filters[ 0 ].EnumPins( out ePins );\r
1003                                                 DsError.ThrowExceptionForHR( hr );\r
1004                                                 try\r
1005                                                 {\r
1006                                                         var pins = new IPin[ 1 ];\r
1007                                                         while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )\r
1008                                                         {\r
1009                                                                 try\r
1010                                                                 {\r
1011                                                                         if( b出力ピンがある )\r
1012                                                                                 continue;\r
1013 \r
1014                                                                         PinDirection dir;\r
1015                                                                         hr = pins[ 0 ].QueryDirection( out dir );\r
1016                                                                         DsError.ThrowExceptionForHR( hr );\r
1017                                                                         if( dir == PinDirection.Output )\r
1018                                                                                 b出力ピンがある = true;\r
1019                                                                 }\r
1020                                                                 finally\r
1021                                                                 {\r
1022                                                                         C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );\r
1023                                                                 }\r
1024                                                         }\r
1025                                                 }\r
1026                                                 finally\r
1027                                                 {\r
1028                                                         C共通.tCOMオブジェクトを解放する( ref ePins );\r
1029                                                 }\r
1030 \r
1031                                                 if( b出力ピンがある )\r
1032                                                         continue;       // 次のフィルタへ\r
1033 \r
1034                                                 //-----------------\r
1035                                                 #endregion\r
1036                                                 #region [ 接続中の入力ピンが MEDIATYPE_Video に対応していたら、フィルタ名とピンIDを取得する。]\r
1037                                                 //-----------------\r
1038                                                 hr = filters[ 0 ].EnumPins( out ePins );\r
1039                                                 DsError.ThrowExceptionForHR( hr );\r
1040                                                 try\r
1041                                                 {\r
1042                                                         var pins = new IPin[ 1 ];\r
1043                                                         while( ePins.Next( 1, pins, IntPtr.Zero ) == CWin32.S_OK )\r
1044                                                         {\r
1045                                                                 try\r
1046                                                                 {\r
1047                                                                         if( !string.IsNullOrEmpty( strVRフィルタ名 ) )\r
1048                                                                                 continue;\r
1049 \r
1050                                                                         var mediaType = new AMMediaType();\r
1051 \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
1059                                                                         #endregion\r
1060 \r
1061                                                                         try\r
1062                                                                         {\r
1063                                                                                 if( mediaType.majorType.Equals( MediaType.Video ) )\r
1064                                                                                 {\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
1073                                                                                         #endregion\r
1074                                                                                         #region [ ピンID取得!]\r
1075                                                                                         //-----------------\r
1076                                                                                         hr = pins[ 0 ].QueryId( out strVRピンID );\r
1077                                                                                         DsError.ThrowExceptionForHR( hr );\r
1078                                                                                         //-----------------\r
1079                                                                                         #endregion\r
1080                                                                                 }\r
1081                                                                                 else if( mediaType.majorType.Equals( MediaType.Audio ) )\r
1082                                                                                 {\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
1090                                                                                 }\r
1091                                                                         }\r
1092                                                                         finally\r
1093                                                                         {\r
1094                                                                                 DsUtils.FreeAMMediaType( mediaType );\r
1095                                                                         }\r
1096                                                                 }\r
1097                                                                 finally\r
1098                                                                 {\r
1099                                                                         C共通.tCOMオブジェクトを解放する( ref pins[ 0 ] );\r
1100                                                                 }\r
1101                                                         }\r
1102                                                 }\r
1103                                                 finally\r
1104                                                 {\r
1105                                                         C共通.tCOMオブジェクトを解放する( ref ePins );\r
1106                                                 }\r
1107 \r
1108                                                 //-----------------\r
1109                                                 #endregion\r
1110                                         }\r
1111                                         finally\r
1112                                         {\r
1113                                                 C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );\r
1114                                         }\r
1115                                 }\r
1116                         }\r
1117                         finally\r
1118                         {\r
1119                                 C共通.tCOMオブジェクトを解放する( ref eFilters );\r
1120                         }\r
1121 \r
1122 \r
1123                         // 改めてフィルタ名とピンIDからこれらのインターフェースを取得し、戻り値として返す。\r
1124 \r
1125                         videoRenderer = null;\r
1126                         inputVPin = null;\r
1127                         audioRenderer = null;\r
1128                         inputAPin = null;\r
1129 \r
1130                         if( !string.IsNullOrEmpty( strVRフィルタ名 ) )\r
1131                         {\r
1132                                 hr = graph.FindFilterByName( strVRフィルタ名, out videoRenderer );\r
1133                                 DsError.ThrowExceptionForHR( hr );\r
1134 \r
1135                                 hr = videoRenderer.FindPin( strVRピンID, out inputVPin );\r
1136                                 DsError.ThrowExceptionForHR( hr );\r
1137                         }\r
1138 \r
1139                         if( !string.IsNullOrEmpty( strARフィルタ名 ) )\r
1140                         {\r
1141                                 hr = graph.FindFilterByName(strARフィルタ名, out audioRenderer);\r
1142                                 DsError.ThrowExceptionForHR(hr);\r
1143 \r
1144                                 hr = audioRenderer.FindPin(strARピンID, out inputAPin);\r
1145                                 DsError.ThrowExceptionForHR(hr);\r
1146                         }\r
1147                 }\r
1148                 private static IBaseFilter tオーディオレンダラを探して返す( IFilterGraph graph )\r
1149                 {\r
1150                         int hr = 0;\r
1151                         IBaseFilter audioRenderer = null;\r
1152 \r
1153                         IEnumFilters eFilters;\r
1154                         hr = graph.EnumFilters( out eFilters );\r
1155                         DsError.ThrowExceptionForHR( hr );\r
1156                         try\r
1157                         {\r
1158                                 var filters = new IBaseFilter[ 1 ];\r
1159                                 while( eFilters.Next( 1, filters, IntPtr.Zero ) == CWin32.S_OK )\r
1160                                 {\r
1161                                         if( ( filters[ 0 ] as IAMAudioRendererStats ) != null )\r
1162                                         {\r
1163                                                 audioRenderer = filters[ 0 ];\r
1164                                                 break;\r
1165                                         }\r
1166 \r
1167                                         C共通.tCOMオブジェクトを解放する( ref filters[ 0 ] );\r
1168                                 }\r
1169                         }\r
1170                         finally\r
1171                         {\r
1172                                 C共通.tCOMオブジェクトを解放する( ref eFilters );\r
1173                         }\r
1174                         return audioRenderer;\r
1175                 }\r
1176 \r
1177 \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
1182 \r
1183                 public static CDirectShow tインスタンスを返す( int nインスタンスID )\r
1184                 {\r
1185                         if( CDirectShow.dicインスタンス.ContainsKey( nインスタンスID ) )\r
1186                                 return CDirectShow.dicインスタンス[ nインスタンスID ];\r
1187 \r
1188                         return null;\r
1189                 }\r
1190                 protected static void tインスタンスを登録する( CDirectShow ds )\r
1191                 {\r
1192                         for( int i = 1; i < CDirectShow.nインスタンスIDの最大数; i++ )\r
1193                         {\r
1194                                 if( !CDirectShow.dicインスタンス.ContainsKey( i ) )               // 空いている番号を使う。\r
1195                                 {\r
1196                                         ds.nインスタンスID = i;\r
1197                                         CDirectShow.dicインスタンス.Add( i, ds );\r
1198                                         break;\r
1199                                 }\r
1200                         }\r
1201                 }\r
1202                 protected static void tインスタンスを解放する( int nインスタンスID )\r
1203                 {\r
1204                         if( CDirectShow.dicインスタンス.ContainsKey( nインスタンスID ) )\r
1205                                 CDirectShow.dicインスタンス.Remove( nインスタンスID );\r
1206                 }\r
1207                 //-----------------\r
1208                 #endregion\r
1209 \r
1210                 #region [ Dispose-Finalize パターン実装 ]\r
1211                 //-----------------\r
1212                 public virtual void Dispose()\r
1213                 {\r
1214                         this.Dispose( true );\r
1215                         GC.SuppressFinalize( this );    // ちゃんと Dispose されたので、ファイナライズ不要であることを CLR に伝える。\r
1216                 }\r
1217                 protected virtual void Dispose( bool bManagedリソースも解放する )\r
1218                 {\r
1219                         if( bManagedリソースも解放する )\r
1220                         {\r
1221                                 #region [ ROTから解放する。]\r
1222                                 //-----------------\r
1223 #if DEBUG\r
1224                                         C共通.tDisposeする( ref this.rot );\r
1225 #endif\r
1226                                 //-----------------\r
1227                                 #endregion\r
1228                                 \r
1229                                 CDirectShow.tインスタンスを解放する( this.nインスタンスID );\r
1230                         }\r
1231 \r
1232                         #region [ インターフェース参照をなくし、COMオブジェクトを解放する。 ]\r
1233                         //-----------------\r
1234                         if( this.ip != IntPtr.Zero )\r
1235                         {\r
1236                                 Marshal.FreeCoTaskMem( this.ip );\r
1237                                 this.ip = IntPtr.Zero;\r
1238                         }\r
1239 \r
1240                         if( this.MediaCtrl != null )\r
1241                         {\r
1242                                 this.MediaCtrl.Stop();\r
1243                                 this.MediaCtrl = null;\r
1244                         }\r
1245 \r
1246                         if( this.MediaEventEx != null )\r
1247                         {\r
1248                                 this.MediaEventEx.SetNotifyWindow( IntPtr.Zero, 0, IntPtr.Zero );\r
1249                                 this.MediaEventEx = null;\r
1250                         }\r
1251 \r
1252                         if( this.MediaSeeking != null )\r
1253                                 this.MediaSeeking = null;\r
1254 \r
1255                         if( this.BasicAudio != null )\r
1256                                 this.BasicAudio = null;\r
1257 \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
1263                         #endregion\r
1264 \r
1265                         C共通.t完全なガベージコレクションを実施する();\r
1266                 }\r
1267         ~CDirectShow()\r
1268                 {\r
1269                         // ファイナライザが呼ばれたということは、Dispose() されなかったということ。\r
1270                         // この場合、Managed リソースは先にファイナライズされている可能性があるので、Unmamaed リソースのみを解放する。\r
1271                         \r
1272                         this.Dispose( false );\r
1273         }\r
1274                 //-----------------\r
1275                 #endregion\r
1276 \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
1284                 #endregion\r
1285 \r
1286                 #region [ private ]\r
1287                 //-----------------\r
1288                 private int _n音量 = 100;\r
1289 #if DEBUG\r
1290                 private DsROTEntry rot = null;\r
1291 #endif\r
1292 \r
1293                 // 可能な数のスレッドを使用して画像を転送する。大きい画像ほど有効。多すぎるとプール内のスレッドが空くまで待たされるので注意。\r
1294                 private static int n並列度 = 0;      // 0 の場合、最初の生成時に並列度を決定する。\r
1295 \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
1301 \r
1302                 private unsafe void tライン描画XRGB32( int n )\r
1303                 {\r
1304                         // Snap は RGB32、Textureは X8R8G8B8\r
1305 \r
1306                         UInt32* ptrTexture = this.ptrTexture[ n ];\r
1307                         for( int y = n; y < this.n高さpx; y += CDirectShow.n並列度 )\r
1308                         {\r
1309                                 byte* ptrPixel = ptrSnap + ( ( ( this.n高さpx - y ) - 1 ) * this.nスキャンライン幅byte );\r
1310 \r
1311                                 // アルファ無視なので一括コピー。CopyMemory() は自前でループ展開するよりも速い。\r
1312                                 CWin32.CopyMemory( (void*) ptrTexture, (void*) ptrPixel, (uint) ( this.n幅px * 4 ) );\r
1313 \r
1314                                 ptrTexture += this.n幅px * CDirectShow.n並列度;\r
1315                         }\r
1316                 }\r
1317                 private unsafe void tライン描画ARGB32( int n )\r
1318                 {\r
1319                         // Snap は RGB32、Textureは A8R8G8B8\r
1320 \r
1321                         UInt32* ptrTexture = this.ptrTexture[ n ];\r
1322                         for( int y = n; y < this.n高さpx; y += CDirectShow.n並列度 )\r
1323                         {\r
1324                                 UInt32* ptrPixel = (UInt32*) ( ptrSnap + ( ( ( this.n高さpx - y ) - 1 ) * this.nスキャンライン幅byte ) );\r
1325 \r
1326                                 //for( int x = 0; x < this.n幅px; x++ )\r
1327                                 //      *( ptrTexture + x ) = 0xFF000000 | *ptrPixel++;\r
1328                                 //                      ↓ループ展開により高速化。160fps の曲が 200fps まで上がった。\r
1329 \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
1334 \r
1335                                 switch( d )\r
1336                                 {\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
1368                                 }\r
1369 \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
1403 \r
1404                         LEXIT:\r
1405                                 ptrTexture += this.n幅px * CDirectShow.n並列度;\r
1406                         }\r
1407                 }\r
1408                 //-----------------\r
1409                 #endregion\r
1410         }\r
1411 }\r