OSDN Git Service

ffa165f25069af2d121c8728a8afa3992c778510
[strokestylet/CsWin10Desktop3.git] / FDK24 / メディア / 動画デコーダ.cs
1 using System;
2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
8
9 namespace FDK.メディア
10 {
11         /// <summary>
12         /// 動画のソースリーダーを持ち、タスク(ワーカスレッド)を使ってサンプルをデコードし、キューに蓄えるて提供する。
13         /// </summary>
14         public class 動画デコーダ : FDK.同期.RWLockAction, IDisposable
15         {
16                 // 外部依存Action。
17                 public Func<double> 現在の再生時刻100ns = null;
18
19                 public SharpDX.Size2 サイズdpx
20                 {
21                         get
22                         {
23                                 SharpDX.Size2 size = SharpDX.Size2.Zero;
24                                 this.ReadLock( () => {
25                                         size = this.bs_サイズdpx;
26                                 } );
27                                 return size;
28                         }
29                         set
30                         {
31                                 this.WriteLock( () => {
32                                         this.bs_サイズdpx = value;
33                                 } );
34                         }
35                 }
36                 public double ループした際の先頭時刻sec
37                 {
38                         get
39                         {
40                                 double sec = 0.0;
41                                 this.ReadLock( () => {
42                                         sec = this.bs_ループした際の先頭時刻sec;
43                                 } );
44                                 return sec;
45                         }
46                         set
47                         {
48                                 this.WriteLock( () => {
49                                         this.bs_ループした際の先頭時刻sec = value;
50                                 } );
51                         }
52                 }
53
54                 public 動画デコーダ( デバイスリソース dr, string 動画ファイルパス, bool ループ再生する, int キューのサイズ )
55                 {
56                         this.dr = dr;
57                         this.キューのサイズ = キューのサイズ;
58
59                         this.ループ再生する = ループ再生する;
60                         string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );   // Log出力用。
61
62                         #region " 動画ファイルパスの有効性を確認する。"
63                         //-----------------
64                         if( string.IsNullOrEmpty( 動画ファイルパス ) )
65                         {
66                                 Log.ERROR( $"動画ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
67                                 return;
68                         }
69                         if( false == System.IO.File.Exists( 動画ファイルパス ) )
70                         {
71                                 Log.ERROR( $"動画ファイルが存在しません。[{変数付きファイルパス}]" );
72                                 return;
73                         }
74                         //-----------------
75                         #endregion
76                         #region " 動画のファイルパス(URI扱い)から SourceReaderEx を作成する。"
77                         //-----------------
78                         try
79                         {
80                                 using( var 属性 = new SharpDX.MediaFoundation.MediaAttributes() )
81                                 {
82                                         // DXVAに対応しているGPUの場合にはそれをデコードに利用するよう指定する。
83                                         属性.Set<SharpDX.ComObject>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.D3DManager, dr.DXGIDeviceManager );
84
85                                         // 追加のビデオプロセッシングを有効にする。
86                                         属性.Set<bool>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true );  // bool だったり
87
88                                         // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。
89                                         属性.Set<int>( SharpDX.MediaFoundation.SinkWriterAttributeKeys.ReadwriteDisableConverters, 0 );           // int だったり
90
91                                         // 属性を使って、SourceReaderEx を生成。
92                                         using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( 動画ファイルパス, 属性 ) )
93                                         {
94                                                 this.SourceReaderEx = sourceReader.QueryInterface<SharpDX.MediaFoundation.SourceReaderEx>();
95                                         }
96                                 }
97                         }
98                         catch( SharpDX.SharpDXException e )
99                         {
100                                 Log.ERROR( $"SourceReaderEx の作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
101                                 throw;
102                         }
103                         //-----------------
104                         #endregion
105                         #region " 最初のビデオストリームを選択し、その他のすべてのストリームを非選択にする。"
106                         //-----------------
107                         try
108                         {
109                                 this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
110                         }
111                         catch( SharpDX.SharpDXException e )
112                         {
113                                 Log.ERROR( $"すべてのストリームの選択解除に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
114                                 throw;
115                         }
116                         try
117                         {
118                                 this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
119                         }
120                         catch( SharpDX.SharpDXException e )
121                         {
122                                 Log.ERROR( $"最初のビデオストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
123                                 throw;
124                         }
125                         //-----------------
126                         #endregion
127                         #region " ARGB32 フォーマットに合わせたメディアタイプを作成し、SourceReaderEx に登録してデコーダを準備する。"
128                         //-----------------
129                         try
130                         {
131                                 using( var mediaType = new SharpDX.MediaFoundation.MediaType() )
132                                 {
133                                         // フォーマットを部分メディアタイプの属性に設定。
134                                         mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Video );
135                                         mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.VideoFormatGuids.Argb32 ); // ARGB32 フォーマットで固定。SourceReaderEx を使わない場合、H264 では NV12 しか選べないので注意。
136
137                                         // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。
138                                         this.SourceReaderEx.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, mediaType );
139                                 }
140                         }
141                         catch( SharpDX.SharpDXException e )
142                         {
143                                 Log.ERROR( $"MediaType (Video, ARGB32) の設定または必要なデコーダの読み込みに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
144                                 throw;
145                         }
146                         //-----------------
147                         #endregion
148                         #region " ビデオストリームが選択されていることを再度保証する。"
149                         //-----------------
150                         try
151                         {
152                                 this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
153                         }
154                         catch( SharpDX.SharpDXException e )
155                         {
156                                 Log.ERROR( $"最初のビデオストリームの選択に失敗しました(MediaType 設定後)。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
157                                 throw;
158                         }
159                         //-----------------
160                         #endregion
161                         #region " デコーダの読み込みにより完成した完全メディアタイプを取得する。"
162                         //-----------------
163                         try
164                         {
165                                 this.MediaType = this.SourceReaderEx.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream );
166                         }
167                         catch( SharpDX.SharpDXException e )
168                         {
169                                 Log.ERROR( $"完全メディアタイプの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
170                                 throw;
171                         }
172                         //-----------------
173                         #endregion
174                         #region " フレームサイズを取得する。動画の途中でのサイズ変更には対応しない。"
175                         //-----------------
176                         try
177                         {
178                                 var packedFrameSize = this.MediaType.Get<long>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.FrameSize );
179                                 this.サイズdpx = new SharpDX.Size2( (int) ( ( packedFrameSize >> 32 ) & 0xFFFFFFFF ), (int) ( ( packedFrameSize ) & 0xFFFFFFFF ) );
180                         }
181                         catch( SharpDX.SharpDXException e )
182                         {
183                                 Log.ERROR( $"フレームサイズの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
184                                 throw;
185                         }
186                         //-----------------
187                         #endregion
188                         #region " 描画先WICビットマップを作成する。"
189                         //-----------------
190                         try
191                         {
192                                 this.WICビットマップ = new SharpDX.WIC.Bitmap(
193                                         dr.WicImagingFactory2,
194                                         this.サイズdpx.Width,
195                                         this.サイズdpx.Height,
196                                         SharpDX.WIC.PixelFormat.Format32bppBGR,
197                                         SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad );
198                         }
199                         catch( SharpDX.SharpDXException e )
200                         {
201                                 Log.ERROR( $"描画先 WIC ビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
202                                 throw;
203                         }
204                         //-----------------
205                         #endregion
206                 }
207                 public void Dispose()
208                 {
209                         // デコードタスクを停止する。
210                         this.タスクを終了せよ.Set();
211                         this.デコードタスク?.Wait();     // デコードタスクは、デコーダを起動してない場合は null になる。
212
213                         this.キューが空いた.Close();
214                         this.タスクを終了せよ.Close();
215
216                         // キュー内の全アイテムを解放する。
217                         QueueItem item = null;
218                         while( ( 0 < this.SampleQueue.Count ) && ( this.SampleQueue.TryDequeue( out item ) ) )
219                         {
220                                 // キューの中の各アイテムのCOM参照カウントは 1 なので、それぞれ 1 回だけ Dispose すればいい。
221                                 item.Dispose();
222                         }
223
224                         FDK.Utilities.解放する( ref this.WICビットマップ );
225                         FDK.Utilities.解放する( ref this.MediaType );
226                         FDK.Utilities.解放する( ref this.SourceReaderEx );
227                 }
228                 public void デコードキャッシング()
229                 {
230                         // 最初のほうのサンプルのデコードには 100~200 ms ほどかかってしまうので、あらかじめ少しデコードしてメモリにキャッシュさせることでこれを緩和する。
231
232                         // 先頭から複数のフレームを読み込む。
233                         for( int i = 0; i < 60; i++ )   // 60フレームもあれば、2つめのキーフレームくらいには届くだろう……
234                         {
235                                 #region " SourceReader から次のサンプル(フレーム)を1つ取得しては解放する。"
236                                 //-----------------
237                                 var sample = (SharpDX.MediaFoundation.Sample) null;
238                                 int 実ストリーム番号 = 0;
239                                 var ストリームフラグ = SharpDX.MediaFoundation.SourceReaderFlags.None;
240                                 long サンプルの時刻100ns = 0;
241                                 try
242                                 {
243                                         sample = this.SourceReaderEx.ReadSample(
244                                                 SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
245                                                 SharpDX.MediaFoundation.SourceReaderControlFlags.None,
246                                                 out 実ストリーム番号,
247                                                 out ストリームフラグ,
248                                                 out サンプルの時刻100ns );
249                                 }
250                                 catch( SharpDX.SharpDXException e )
251                                 {
252                                         Log.ERROR( $"SourceReaderEx.ReadSample() に失敗しました。(0x{e.HResult:x8})" );
253                                         throw;
254                                 }
255                                 finally
256                                 {
257                                         // すぐ解放。
258                                         sample?.Dispose();
259                                 }
260                                 //-----------------
261                                 #endregion
262                         }
263
264                         // SourceReader を先頭へリセット。
265                         this.SourceReaderEx.SetCurrentPosition( 0 );
266
267                         Log.Info( $"{Utilities.現在のメソッド名}: 動画のセットアップ(フレームキャッシング)を行いました。" );
268                 }
269                 public void デコードを開始する()
270                 {
271                         this.デコードタスク = Task.Factory.StartNew( this.デコードタスクエントリ );
272                 }
273                 /// <remarks>
274                 /// 呼び出し元は、取得したサンプルを使い終わったら Dispose すること。
275                 /// </remarks>
276                 public bool キューからビットマップを取り出す( out SharpDX.Direct2D1.Bitmap D2Dビットマップ, out double 再生時刻sec )
277                 {
278                         QueueItem item = null;
279                         if( ( 0 == this.SampleQueue.Count ) || ( false == this.SampleQueue.TryDequeue( out item ) ) )
280                         {
281                                 D2Dビットマップ = null;
282                                 再生時刻sec = 0.0;
283                                 return false;   // キューが空だったか、Dequeue が一歩遅かった?(ないはずだが
284                         }
285
286                         D2Dビットマップ = item.D2Dビットマップ;             // キューから出しても、代入しても、COMの参照カウントは変化しない。
287                         再生時刻sec = item.再生時刻sec;
288
289                         this.前フレームの時刻sec = item.再生時刻sec;
290                         this.キューが空いた.Set(); // キューが空いたので、このイベントを set する。
291
292                         return true;
293                 }
294                 public bool 次のビットマップの有無を確認する( out SharpDX.Direct2D1.Bitmap D2Dビットマップ, out double 再生時刻sec )
295                 {
296                         QueueItem item = null;
297                         if( ( 0 == this.SampleQueue.Count ) || ( false == this.SampleQueue.TryPeek( out item ) ) )      // キューから取り出さない。
298                         {
299                                 D2Dビットマップ = null;
300                                 再生時刻sec = 0.0;
301                                 return false;   // キューが空だったか、Peek が一歩遅かった?(ないはずだが
302                         }
303
304                         D2Dビットマップ = item.D2Dビットマップ;       // 代入しても、COMの参照カウントは変化しない。
305                         再生時刻sec = item.再生時刻sec;
306                         return true;
307                 }
308
309                 protected class QueueItem : IDisposable
310                 {
311                         public double 再生時刻sec = 0;    // 動画の先頭を 0 とする時刻。秒単位。
312                         public SharpDX.Direct2D1.Bitmap D2Dビットマップ = null;
313
314                         public void Dispose()
315                         {
316                                 FDK.Utilities.解放する( ref this.D2Dビットマップ );
317                         }
318                 }
319                 protected ConcurrentQueue<QueueItem> SampleQueue = new ConcurrentQueue<QueueItem>();
320                 protected SharpDX.MediaFoundation.SourceReaderEx SourceReaderEx;
321                 protected SharpDX.MediaFoundation.MediaType MediaType;
322                 protected bool ループ再生する = false;
323                 protected double 前フレームの時刻sec = -1.0;
324                 protected System.Threading.Tasks.Task デコードタスク = null;
325                 protected ManualResetEvent キューが空いた = new ManualResetEvent( true );
326                 protected AutoResetEvent タスクを終了せよ = new AutoResetEvent( false );
327                 protected SharpDX.WIC.Bitmap WICビットマップ = null;    // MediaFoundation は WICBitmap に出力する。
328
329                 protected void デコードタスクエントリ()
330                 {
331                         var events = new WaitHandle[ 2 ];
332                         events[ 0 ] = this.キューが空いた;
333                         events[ 1 ] = this.タスクを終了せよ;
334
335                         while( WaitHandle.WaitAny( events ) != 1 )      // WaitAny() は、シグナル状態になったハンドルの配列インデックスを返す。
336                         {
337                                 // キューが空いてるので、サンプルを1つデコードして格納する。
338                                 if( this.サンプルをひとつ取得してキューへ格納する() )
339                                 {
340                                         // キューがいっぱいになったら、空くまで待つ。
341                                         if( キューのサイズ == this.SampleQueue.Count )
342                                                 this.キューが空いた.Reset();     // 空いたらこれが set される。
343                                 }
344                                 else
345                                 {
346                                         break;  // エラーまたはストリーム終了 → デコードタスクを終了する。
347                                 }
348                         }
349                 }
350                 protected bool サンプルをひとつ取得してキューへ格納する()
351                 {
352                         var sample = (SharpDX.MediaFoundation.Sample) null;
353                         var bitmap = (SharpDX.Direct2D1.Bitmap) null;
354                         long 再生時刻100ns = 0;   // 100ns 単位
355
356                         try
357                         {
358                                 #region " ソースリーダーから次のサンプル(フレーム)を1つ取得する。"
359                                 //-----------------
360                                 var ストリームフラグ = SharpDX.MediaFoundation.SourceReaderFlags.None;
361                                 int 実ストリーム番号 = 0;
362
363                                 sample = this.SourceReaderEx.ReadSample(
364                                         SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
365                                         SharpDX.MediaFoundation.SourceReaderControlFlags.None,
366                                         out 実ストリーム番号,
367                                         out ストリームフラグ,
368                                         out 再生時刻100ns );
369                                 //-----------------
370                                 #endregion
371                                 #region " 取得結果フラグに応じて、必要な処理があれば行なう。"
372                                 //---------------------------------------------------
373                                 if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) )  // BOX化コストとか気にしない
374                                 {
375                                         #region " ストリーム終了 "
376                                         //----------------
377                                         if( this.ループ再生する )
378                                         {
379                                                 // (A) ループ再生する場合
380                                                 FDK.Log.Info( "動画をループ再生します。" );
381                                                 this.ループした際の先頭時刻sec = this.前フレームの時刻sec; // EndOfStream フラグがセットされたときは、ReadSample() で返されるサンプル時刻は無効(0 になっている)。
382                                                 this.SourceReaderEx.SetCurrentPosition( 0 );            // ストリーム再生位置を先頭へ。
383                                                 return this.サンプルをひとつ取得してキューへ格納する(); // 再帰で先頭サンプルを取得して返す。
384                                         }
385                                         else
386                                         {
387                                                 // (B) ループ再生しない場合
388                                                 FDK.Log.Info( "動画の再生を終了します。" );
389                                                 return false;
390                                         }
391                                         //----------------
392                                         #endregion
393                                 }
394                                 //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Newstream ) )
395                                 //{
396                                 //}
397                                 //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Nativemediatypechanged ) )
398                                 //{
399                                 //}
400                                 //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Currentmediatypechanged ) )
401                                 //{
402                                 //}
403                                 //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.StreamTick ) )
404                                 //{
405                                 //}
406                                 //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.AllEffectsremoved ) )
407                                 //{
408                                 //}
409                                 else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) )
410                                 {
411                                         #region " エラー "
412                                         //----------------
413                                         FDK.Log.Info( $"エラーが発生したので、動画の再生を終了します。" );
414                                         throw new SharpDX.SharpDXException( SharpDX.Result.Fail );
415                                         //----------------
416                                         #endregion
417                                 }
418                                 //---------------------------------------------------
419                                 #endregion
420
421                                 if( this.現在の再生時刻100ns() < 再生時刻100ns )   // 現時点でもう再生時刻を超えてるなら、スキップする。
422                                 {
423                                         this.サンプルをD2Dビットマップに転送する( sample, out bitmap );
424
425                                         // ビットマップをキューへ格納する。
426                                         this.SampleQueue.Enqueue( new QueueItem() {
427                                                 D2Dビットマップ = bitmap,  // COM参照カウントは変化しない。
428                                                 再生時刻sec = (double) ( 再生時刻100ns / ( 10.0 * 1000.0 * 1000.0 ) ),  // 100ns単位 → 秒単位へ変換。
429                                         } );
430                                 }
431                         }
432                         catch( Exception e )
433                         {
434                                 FDK.Log.Info( $"エラーが発生したので、動画の再生を終了します。[{e.Message}]" );
435                                 return false;
436                         }
437                         finally
438                         {
439                                 sample?.Dispose();
440                         }
441
442                         return true;
443                 }
444
445                 private int キューのサイズ = 16;
446                 private デバイスリソース dr = null;
447
448                 private unsafe void サンプルをD2Dビットマップに転送する( SharpDX.MediaFoundation.Sample サンプル, out SharpDX.Direct2D1.Bitmap D2Dビットマップ )
449                 {
450                         var buffer = (SharpDX.MediaFoundation.MediaBuffer) null;
451                         var buffer2d2 = (SharpDX.MediaFoundation.Buffer2D2) null;
452                         try
453                         {
454                                 #region " サンプルからサンプルバッファ (MediaBuffer) を取得する。"
455                                 //-----------------
456                                 try
457                                 {
458                                         buffer = サンプル.ConvertToContiguousBuffer();
459                                 }
460                                 catch( Exception e )
461                                 {
462                                         Log.ERROR( $"サンプルバッファの取得に失敗しました。(0x{e.HResult:x8})" );
463                                         throw;
464                                 }
465                                 //-----------------
466                                 #endregion
467                                 #region " サンプルバッファを Buffer2D2 にキャストする。"
468                                 //-----------------
469                                 try
470                                 {
471                                         buffer2d2 = buffer.QueryInterface<SharpDX.MediaFoundation.Buffer2D2>();
472                                 }
473                                 catch( SharpDX.SharpDXException e )
474                                 {
475                                         Log.ERROR( $"サンプルバッファから Buffer2D2 へのキャストに失敗しました。(0x{e.HResult:x8})" );
476                                         throw;
477                                 }
478                                 //-----------------
479                                 #endregion
480                                 #region " サンプルバッファをロックする。"
481                                 //-----------------
482                                 byte[] scanLine0_bp = new byte[ 8 ];    // 「生ポインタ」が格納される。32bitなら[0~3]、64bitなら[0~7]が有効。(CPUではなく.NETに依存)
483                                 int pitch = 0;
484                                 byte[] bufferStart_bp = new byte[ 8 ];  // 「生ポインタ」が格納される。こちらは使わない。
485                                 int bufferLength;
486                                 try
487                                 {
488                                         buffer2d2.Lock2DSize(
489                                                 SharpDX.MediaFoundation.Buffer2DLockFlags.Read,
490                                                 scanLine0_bp,
491                                                 out pitch,
492                                                 bufferStart_bp,
493                                                 out bufferLength );
494                                 }
495                                 catch( SharpDX.SharpDXException e )
496                                 {
497                                         Log.ERROR( $"サンプルバッファのロックに失敗しました。(0x{e.HResult:x8})" );
498                                         throw;
499                                 }
500                                 //-----------------
501                                 #endregion
502                                 try
503                                 {
504                                         #region " サンプルバッファのネイティブ先頭アドレスを取得する。"
505                                         //-----------------
506                                         byte* scanLine0 = null;
507                                         try
508                                         {
509                                                 scanLine0 = (byte*) this.生ポインタを格納したbyte配列からポインタを取得して返す( scanLine0_bp );
510                                         }
511                                         catch( SharpDX.SharpDXException e )
512                                         {
513                                                 Log.ERROR( $"サンプルバッファのアドレスの取得に失敗しました。(0x{e.HResult:x8})" );
514                                                 throw;
515                                         }
516                                         //-----------------
517                                         #endregion
518                                         #region " サンプルから WICBitmap を生成する。"
519                                         //-----------------
520                                         try
521                                         {
522                                                 // 描画先WICビットマップをロックする。
523                                                 using( var bitmapLock = this.WICビットマップ.Lock(
524                                                         new SharpDX.Rectangle( 0, 0, this.WICビットマップ.Size.Width, this.WICビットマップ.Size.Height ),
525                                                         SharpDX.WIC.BitmapLockFlags.Write ) )
526                                                 {
527                                                         // サンプルバッファからWICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピー。
528                                                         int bitmapStride = bitmapLock.Stride;
529                                                         byte* src = scanLine0;
530                                                         byte* dest = (byte*) bitmapLock.Data.DataPointer.ToPointer();
531
532                                                         for( int y = 0; y < this.サイズdpx.Height; y++ )
533                                                         {
534                                                                 // ARGB32 to G8B8R8X8 ではデータ変換が不要なので、一行を一気にコピー。
535                                                                 動画デコーダ.CopyMemory( dest, src, this.サイズdpx.Width * 4 );   // ARGB=4バイト。
536                                                                 src += pitch;
537                                                                 dest += bitmapStride;   // bitmapStride は byte 単位
538                                                         }
539                                                 }
540                                         }
541                                         catch( SharpDX.SharpDXException e )
542                                         {
543                                                 Log.ERROR( $"WICビットマップの Lock に失敗しました。(0x{e.HResult:x8})" );
544                                                 throw;
545                                         }
546                                         catch( Exception e )
547                                         {
548                                                 Log.ERROR( $"サンプルバッファから WIC ビットマップへのデータの転送に失敗しました。(0x{e.HResult:x8})" );
549                                                 throw;
550                                         }
551                                         //----------------
552                                         #endregion
553                                         #region " WICビットマップからD2Dビットマップを生成する。"
554                                         //----------------
555                                         try
556                                         {
557                                                  D2Dビットマップ = SharpDX.Direct2D1.Bitmap.FromWicBitmap(
558                                                         dr.D2DContext1,
559                                                         this.WICビットマップ );
560                                         }
561                                         catch( SharpDX.SharpDXException e )
562                                         {
563                                                 Log.ERROR( $"D2Dビットマップの作成に失敗しました。(0x{e.HResult:x8})" );
564                                                 throw;
565                                         }
566                                         //----------------
567                                         #endregion
568                                 }
569                                 finally
570                                 {
571                                         #region " サンプルバッファのロックを解除する。"
572                                         //-----------------
573                                         buffer2d2.Unlock2D();
574                                         //-----------------
575                                         #endregion
576                                 }
577                         }
578                         finally
579                         {
580                                 FDK.Utilities.解放する( ref buffer2d2 );
581                                 FDK.Utilities.解放する( ref buffer );
582                         }
583                 }
584                 private unsafe void* 生ポインタを格納したbyte配列からポインタを取得して返す( byte[] 生ポインタ )
585                 {
586                         if( ( 4 == IntPtr.Size ) && System.BitConverter.IsLittleEndian )
587                         {
588                                 // (A) 32bit, リトルエンディアン
589                                 int 生アドレス32bit = 0;
590                                 for( int i = 0; i < 4; i++ )
591                                         生アドレス32bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 );
592                                 return new IntPtr( 生アドレス32bit ).ToPointer();
593                         }
594                         else if( ( 8 == IntPtr.Size ) && System.BitConverter.IsLittleEndian )
595                         {
596                                 // (B) 64bit, リトルエンディアン
597                                 long 生アドレス64bit = 0;
598                                 for( int i = 0; i < 8; i++ )
599                                         生アドレス64bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 );
600                                 return new IntPtr( 生アドレス64bit ).ToPointer();
601                         }
602                         else if( ( 4 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) )
603                         {
604                                 // (C) 32bit, ビッグエンディアン
605                                 int 生アドレス32bit = 0;
606                                 for( int i = 0; i < 4; i++ )
607                                         生アドレス32bit += ( (int) 生ポインタ[ 4 - i ] ) << ( i * 8 );
608                                 return new IntPtr( 生アドレス32bit ).ToPointer();
609                         }
610                         else if( ( 8 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) )
611                         {
612                                 // (D) 64bit, ビッグエンディアン
613                                 long 生アドレス64bit = 0;
614                                 for( int i = 0; i < 8; i++ )
615                                         生アドレス64bit += ( (int) 生ポインタ[ 8 - i ] ) << ( i * 8 );
616                                 return new IntPtr( 生アドレス64bit ).ToPointer();
617                         }
618
619                         throw new SharpDX.SharpDXException( SharpDX.Result.NotImplemented, "この .NET アーキテクチャには対応していません。" );
620                 }
621
622                 #region " バックストア。"
623                 //----------------
624                 private double bs_ループした際の先頭時刻sec = 0.0;
625                 private SharpDX.Size2 bs_サイズdpx = new SharpDX.Size2( 1, 1 );
626                 //----------------
627                 #endregion
628
629                 #region " Win32 API "
630                 //-----------------
631                 [System.Runtime.InteropServices.DllImport( "kernel32.dll", SetLastError = true )]
632                 private static extern unsafe void CopyMemory( void* dst, void* src, int size );
633                 //-----------------
634                 #endregion
635         }
636 }