OSDN Git Service

ビュアーモードで起動したときの終了時に Null 参照例外が発生することがあるミスを修正。
[strokestylet/CsWin10Desktop3.git] / FDK24 / メディア / 動画.cs
index 0f3ef42..7925e11 100644 (file)
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Threading.Tasks;
-using FDK.同期;
+using System.Linq;
 
 namespace FDK.メディア
 {
-       /// <remarks>
-       /// 活性化後の最初の進行描画時に、再生が開始される。
-       /// </remarks>
-       public unsafe class 動画 : Activity
+       public class 動画 : FDK.Activity
        {
-               public enum 再生状態ID { 未再生, 再生中, 再生終了済み }
-               public FDK.同期.RWLock<再生状態ID> 再生状態 { get; } = new RWLock<再生状態ID>( 再生状態ID.未再生 );
-               public bool 加算合成 { get; set; } = false;
-               public float 不透明度0to1 { get; set; } = 1.0f;
-               public bool ループ再生する { get; set; } = false;
+               public string 動画ファイルパス
+               {
+                       get;
+                       protected set;
+               } = null;
+
+               public SharpDX.Size2F サイズdpx
+               {
+                       get;
+                       protected set;
+               }
+
+               public double 長さsec
+               {
+                       get;
+                       protected set;
+               } = 0.0;
 
-               public 動画( string 動画ファイルパス )
+               public bool 加算合成
+               {
+                       get;
+                       set;
+               } = false;
+
+               /// <summary>
+               ///             0:透明 ~ 1:不透明
+               /// </summary>
+               public float 不透明度
+               {
+                       get;
+                       set;
+               } = 1.0f;
+
+               public 動画( string 動画ファイルパス, int キューのサイズ = 16 )
                {
                        this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
+                       this._キューのサイズ = キューのサイズ;
                }
-               protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
+
+               public void 再生を開始する( double 開始位置sec = 0.0, bool ループ再生する = false )
                {
-                       // todo: デバイスがリサイズされると動画もリセットされてしまう。活性化とコードを分離すること(課題)。
+                       this._ループ再生する = ループ再生する;
+
+                       // タスクを起動する。
+                       this._デコードタスク = System.Threading.Tasks.Task.Factory.StartNew( this._デコードタスクエントリ, (object) 開始位置sec );
+                       this._デコードタスク起動完了.WaitOne();
+               }
+
+               public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f )
+               {
+                       // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
+                       var 変換行列2Dpx =
+                               dr.拡大行列DPXtoPX // スケーリング(1) DPX → PX
+                               * SharpDX.Matrix3x2.Scaling( 描画先矩形dpx.Width / this.サイズdpx.Width, 描画先矩形dpx.Height / this.サイズdpx.Height )  // スケーリング(2)
+                               * SharpDX.Matrix3x2.Translation( 描画先矩形dpx.Left * dr.拡大率DPXtoPX横方向, 描画先矩形dpx.Top * dr.拡大率DPXtoPX縦方向 );  // 平行移動(物理単位)、
+
+                       this.進行描画する( dr, 変換行列2Dpx, 不透明度0to1 );
+               }
+
+               public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列, float 不透明度0to1 = 1.0f )
+               {
+                       #region " 条件チェック。"
+                       //----------------
+                       if( null == this._デコードタスク ||      // 再生をまだ開始していないか、あるいはすでに再生を完了してデコードタスクを終了済みである。
+                               null == this._SourceReaderEx ||         // 動画の準備に失敗した。
+                               null == this._MediaType ||                      // 同上
+                               null == this._WicBitmap )                       // 同上
+                       {
+                               return;
+                       }
+                       //----------------
+                       #endregion
+
+                       Action<FrameQueueItem> 次のフレームを表示する = ( frame ) => {
+                               this._次のフレームを取り出す( out frame );
+                               this._最後に表示したフレーム?.Dispose();
+                               this._最後に表示したフレーム = frame;
+                               this._D2DBitmapを描画する( dr, 変換行列, frame.D2DBitmap, 不透明度0to1 );
+                       };
+                       Action 前のフレームを表示する = () => {
+                               this._D2DBitmapを描画する( dr, 変換行列, this._最後に表示したフレーム?.D2DBitmap, 不透明度0to1 );
+                       };
+
+                       var フレーム = (FrameQueueItem) null;
+                       this._次のフレームを確認する( out フレーム );
+
+
+                       if( null != フレーム )  // 次のフレームがある。
+                       {
+                               // (A) 次のフレームが前のフレームより過去 → ループしたので、タイマをリセットする。
+                               if( ( null != this._最後に表示したフレーム ) &&
+                                       ( フレーム.表示時刻sec < this._最後に表示したフレーム.表示時刻sec ) )
+                               {
+                                       this._再生タイマ.Value.リセットする( FDK.カウンタ.QPCTimer.秒をカウントに変換して返す( フレーム.表示時刻sec ) );
+                                       次のフレームを表示する( フレーム );
+                               }
+
+                               // (B) 次のフレームの表示時刻に達した。
+                               else if( フレーム.表示時刻sec <= this._再生タイマ.Value.現在のリアルタイムカウントsec )
+                               {
+                                       次のフレームを表示する( フレーム );
+                               }
+
+                               // (C) 次のフレームの表示時刻にはまだ達していない。
+                               else
+                               {
+                                       前のフレームを表示する();
+                               }
+                       }
+
+                       // (D) デコードが追い付いてない、またはループせず再生が終わっている。
+                       else
+                       {
+                               // 何も表示しない → 真っ黒画像。デコードが追い付いてないなら点滅するだろう。
+                       }
+               }
+
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       this._デコードタスク = null;  // タスクが起動していないときは null であることを保証する。
+                       this._デコードタスク起動完了 = new System.Threading.AutoResetEvent( false );
+                       this._キューが空いた = new System.Threading.ManualResetEvent( true );
+                       this._デコードタスクを終了せよ = new System.Threading.AutoResetEvent( false );
+                       this._再生タイマ.Value = new カウンタ.QPCTimer();
+               }
 
-                       this.再生状態.Value = 再生状態ID.未再生;
-                       this.前フレームの時刻 = FDK.カウンタ.QPCTimer.未使用;
-                       this.ループした際の先頭時刻 = 0;
+               protected override void On非活性化( デバイスリソース dr )
+               {
+                       this._キューが空いた.Close();
+                       this._デコードタスクを終了せよ.Close();
+               }
 
-                       string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス );
-                       var result = SharpDX.Result.Ok;
+               protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
+               {
+                       FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
                        try
                        {
-                               this.動画の生成に成功した = false;
+                               this._デコードタスク用D2DDeviceContext参照 = dr.D2DContext1;
+                               this._フレームキュー = new ConcurrentQueue<FrameQueueItem>();
+                               this._最後に表示したフレーム = null;
+
+                               // 動画ファイルから、SourceReaderEx, MediaType, WicBitmap を生成する。
 
-                               // 動画ファイルパスを基に、SourceReaderEx, MediaType, フレームサイズ, 描画先WICビットマップを準備する
+                               string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );   // Log出力用
 
                                #region " 動画ファイルパスの有効性を確認する。"
                                //-----------------
-                               if( string.IsNullOrEmpty( this.動画ファイルパス ) )
+                               if( string.IsNullOrEmpty( 動画ファイルパス ) )
                                {
                                        Log.ERROR( $"動画ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
                                        return;
                                }
-                               if( false == System.IO.File.Exists( this.動画ファイルパス ) )
+                               if( false == System.IO.File.Exists( 動画ファイルパス ) )
                                {
                                        Log.ERROR( $"動画ファイルが存在しません。[{変数付きファイルパス}]" );
                                        return;
                                }
                                //-----------------
                                #endregion
-                               #region " 動画のファイルパス(URI扱い)から SourceReaderEx を作成する。"
+                               #region " SourceReaderEx を生成する。"
                                //-----------------
                                try
                                {
                                        using( var 属性 = new SharpDX.MediaFoundation.MediaAttributes() )
                                        {
-                                               // DXVAに対応しているGPUの場合にはそれをデコードに利用するよう指定する。
+                                               // DXVAã\81«å¯¾å¿\9cã\81\97ã\81¦ã\81\84ã\82\8bGPUã\81®å ´å\90\88ã\81«ã\81¯ã\80\81ã\81\9dã\82\8cã\82\92ã\83\87ã\82³ã\83¼ã\83\89ã\81«å\88©ç\94¨ã\81\99ã\82\8bã\82\88ã\81\86æ\8c\87å®\9aã\81\99ã\82\8bã\80\82
                                                属性.Set<SharpDX.ComObject>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.D3DManager, dr.DXGIDeviceManager );
 
                                                // 追加のビデオプロセッシングを有効にする。
-                                               属性.Set<bool>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true );  // bool だったり
+                                               属性.Set<bool>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true );  // 真偽値が bool だったり
 
                                                // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。
                                                属性.Set<int>( SharpDX.MediaFoundation.SinkWriterAttributeKeys.ReadwriteDisableConverters, 0 );           // int だったり
 
                                                // 属性を使って、SourceReaderEx を生成。
-                                               using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( this.動画ファイルパス, 属性 ) )
+                                               using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( 動画ファイルパス, 属性 ) )    // パスは URI 扱い
                                                {
-                                                       this.SourceReaderEx = sourceReader.QueryInterface<SharpDX.MediaFoundation.SourceReaderEx>();
+                                                       this._SourceReaderEx = sourceReader.QueryInterface<SharpDX.MediaFoundation.SourceReaderEx>();
                                                }
                                        }
                                }
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"SourceReaderEx の作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       throw;
                                }
                                //-----------------
                                #endregion
@@ -85,44 +200,34 @@ namespace FDK.メディア
                                //-----------------
                                try
                                {
-                                       this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
+                                       this._SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
+                                       this._SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
                                }
                                catch( SharpDX.SharpDXException e )
                                {
-                                       Log.ERROR( $"すべてのストリームの選択解除に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       return;
-                               }
-                               try
-                               {
-                                       this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
-                               }
-                               catch( SharpDX.SharpDXException e )
-                               {
-                                       Log.ERROR( $"最初のビデオストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       Log.ERROR( $"ストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       throw;
                                }
                                //-----------------
                                #endregion
-                               #region " ARGB32 フォーマットに合わせたメディアタイプを作成し、SourceReaderEx に登録してデコーダを準備する。"
+                               #region " 部分 MediaType を作成し、SourceReaderEx に登録する。"
                                //-----------------
                                try
                                {
                                        using( var mediaType = new SharpDX.MediaFoundation.MediaType() )
                                        {
-                                               // ã\83\95ã\82©ã\83¼ã\83\9eã\83\83ã\83\88ã\82\92é\83¨å\88\86ã\83¡ã\83\87ã\82£ã\82¢ã\82¿ã\82¤ã\83\97ã\81®å±\9eæ\80§ã\81«è¨­å®\9aã\80\82
+                                               // ã\83\95ã\82©ã\83¼ã\83\9eã\83\83ã\83\88ã\81¯ ARGB32 ã\81§å\9bºå®\9aã\81¨ã\81\99ã\82\8bã\80\82ï¼\88SourceReaderEx ã\82\92使ã\82\8fã\81ªã\81\84å ´å\90\88ã\80\81H264 ã\81§ã\81¯ ARGB32 ã\81\8cé\81¸ã\81¹ã\81ªã\81\84ã\81®ã\81§æ³¨æ\84\8fã\80\82ï¼\89
                                                mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Video );
-                                               mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.VideoFormatGuids.Argb32 ); // ARGB32 フォーマットで固定。
+                                               mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.VideoFormatGuids.Argb32 );
 
                                                // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。
-                                               this.SourceReaderEx.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, mediaType );
+                                               this._SourceReaderEx.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, mediaType );
                                        }
                                }
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"MediaType (Video, ARGB32) の設定または必要なデコーダの読み込みに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       throw;
                                }
                                //-----------------
                                #endregion
@@ -130,171 +235,389 @@ namespace FDK.メディア
                                //-----------------
                                try
                                {
-                                       this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
+                                       this._SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
                                }
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"最初のビデオストリームの選択に失敗しました(MediaType 設定後)。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       throw;
                                }
                                //-----------------
                                #endregion
-                               #region " デコーダの読み込みにより完成した完全メディアタイプを取得する。"
+                               #region " 完全 MediaType と動画の情報を取得する。"
                                //-----------------
                                try
                                {
-                                       this.MediaType = this.SourceReaderEx.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream );
+                                       this._MediaType = this._SourceReaderEx.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream );
                                }
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"完全メディアタイプの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       throw;
                                }
-                               //-----------------
-                               #endregion
-                               #region " フレームサイズを取得する。動画の途中でのサイズ変更には対応しない。"
-                               //-----------------
+
+                               // フレームサイズを取得する。
                                try
                                {
-                                       var packedFrameSize = this.MediaType.Get<long>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.FrameSize );
-                                       this.サイズdpx.Width = (int) ( ( packedFrameSize >> 32 ) & 0xFFFFFFFF );
-                                       this.サイズdpx.Height = (int) ( ( packedFrameSize ) & 0xFFFFFFFF );
+                                       // 動画の途中でのサイズ変更には対応しない。
+                                       var packedFrameSize = this._MediaType.Get<long>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.FrameSize );
+                                       this.サイズdpx = new SharpDX.Size2F( ( packedFrameSize >> 32 ) & 0xFFFFFFFF, ( packedFrameSize ) & 0xFFFFFFFF );
                                }
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"フレームサイズの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       throw;
+                               }
+
+                               // 動画の長さを取得する。
+                               try
+                               {
+                                       this.長さsec = this._SourceReaderEx.GetPresentationAttribute(
+                                               SharpDX.MediaFoundation.SourceReaderIndex.MediaSource,
+                                               SharpDX.MediaFoundation.PresentationDescriptionAttributeKeys.Duration
+                                               ) / ( 1000.0 * 1000.0 * 10.0 );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"動画の長さの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       throw;
                                }
                                //-----------------
                                #endregion
-                               #region " 描画先WICビットマップを作成する。"
+                               #region " 描画先となる WicBitmap を作成する。"
                                //-----------------
                                try
                                {
-                                       this.WICビットマップ = new SharpDX.WIC.Bitmap(
+                                       this._WicBitmap = new SharpDX.WIC.Bitmap(
                                                dr.WicImagingFactory2,
-                                               this.サイズdpx.Width,
-                                               this.サイズdpx.Height,
+                                               (int) this.サイズdpx.Width,
+                                               (int) this.サイズdpx.Height,
                                                SharpDX.WIC.PixelFormat.Format32bppBGR,
                                                SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad );
                                }
                                catch( SharpDX.SharpDXException e )
                                {
-                                       Log.ERROR( $"描画先 WIC ビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
-                                       result = e.ResultCode;
-                                       return;
+                                       Log.ERROR( $"描画先となる WICビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       throw;
                                }
                                //-----------------
                                #endregion
 
-                               this.動画の生成に成功した = true;
-                               Log.Info( $"{Utilities.現在のメソッド名}: 動画を生成しました。[{変数付きファイルパス}]" );
-
-                               #region " デコードの前処理を行う。"
-                               //----------------
-                               this.デコードキャッシング();
-                               Log.Info( $"{Utilities.現在のメソッド名}: 動画のセットアップを行いました。[{変数付きファイルパス}]" );
-                               //----------------
-                               #endregion
-
-                               // 準備完了。以降は、サンプルを読み込みたいときに、適宜 ReadSample() する。
-                               // コールバックを使っていないので、IMFSourceReader::ReadSample() はサンプルが用意できるまでブロックすることに注意。
+                               this._ストックする();
                        }
                        finally
                        {
-                               if( result.Failure )
-                                       this.全リソースを解放する();
-
-                               // 失敗しても、一応活性化は成功とする。(進行描画はされない。)
+                               FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
                        }
                }
+
                protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
                {
-                       // todo: デバイスがリサイズされると動画もリセットされてしまう。非活性化とコードを分離すること(課題)。
+                       FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
 
-                       // デコード中なら、タスクが終了するまで待つ。
-                       while( ( this.再生状態.Value == 再生状態ID.再生中 ) && ( this.デコーダ状態.Value == デコーダ状態ID.デコード中 ) )
-                               System.Threading.Thread.Sleep( 500 );
+                       #region " デコードタスクが起動していたら、終了する。"
+                       //----------------
+                       if( null != this._デコードタスク )
+                       {
+                               this._デコードタスクを終了せよ.Set();
+
+                               if( !this._デコードタスク.Wait( 2000 ) )
+                                       FDK.Log.WARNING( "デコードタスクの終了待ちがタイムアウトしました。" );
 
-                       this.全リソースを解放する();
-                       Log.Info( $"{Utilities.現在のメソッド名}: 動画を解放しました。[{フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス )}]" );
+                               this._デコードタスク = null;
+                       }
+                       //----------------
+                       #endregion
+
+                       FDK.Utilities.解放する( ref this._最後に表示したフレーム );
+                       FDK.Utilities.解放する( ref this._WicBitmap );
+                       FDK.Utilities.解放する( ref this._MediaType );
+                       FDK.Utilities.解放する( ref this._SourceReaderEx );
+
+                       this._キューをクリアする();
+                       this._フレームキュー = null;
+                       this._デコードタスク用D2DDeviceContext参照 = null;
+
+                       FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
                }
-               public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f, bool ループ再生する = false )
+
+               private int _キューのサイズ = 0;
+               private class FrameQueueItem : IDisposable
                {
-                       // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
-                       var 変換行列2Dpx = SharpDX.Matrix3x2.Identity
-                               * SharpDX.Matrix3x2.Scaling( 描画先矩形dpx.Width / this.サイズdpx.Width, 描画先矩形dpx.Height / this.サイズdpx.Height )  // スケーリング。
-                               * dr.行列を単位変換するDPXtoPX( SharpDX.Matrix3x2.Translation( 描画先矩形dpx.Left, 描画先矩形dpx.Top ) );  // 平行移動(物理単位)、
+                       public double 表示時刻sec = 0;
+                       public SharpDX.Direct2D1.Bitmap D2DBitmap = null;
 
-                       // 描画する。
-                       this.進行描画する( dr, 変換行列2Dpx );
+                       public void Dispose()
+                       {
+                               FDK.Utilities.解放する( ref this.D2DBitmap );
+                       }
+               }
+               private ConcurrentQueue<FrameQueueItem> _フレームキュー = null;
+               private FrameQueueItem _最後に表示したフレーム = null;
+               private bool _ループ再生する = false;
+               private System.Threading.Tasks.Task _デコードタスク = null;
+               private System.Threading.AutoResetEvent _デコードタスク起動完了 = null;
+               private System.Threading.ManualResetEvent _キューが空いた = null;
+               private System.Threading.AutoResetEvent _デコードタスクを終了せよ = null;
+               private SharpDX.MediaFoundation.SourceReaderEx _SourceReaderEx = null;
+               private SharpDX.MediaFoundation.MediaType _MediaType = null;
+               private SharpDX.WIC.Bitmap _WicBitmap = null;    // MediaFoundation は WICBitmap に出力する。
+               private SharpDX.Direct2D1.DeviceContext1 _デコードタスク用D2DDeviceContext参照 = null; // D2Dはスレッドセーフであること。
+
+               private void _キューをクリアする()
+               {
+                       while( 0 < this._フレームキュー.Count )
+                       {
+                               var item = (FrameQueueItem) null;
+                               if( this._フレームキュー.TryDequeue( out item ) )
+                                       item.Dispose();
+                       }
+
+                       this._キューが空いた?.Set();
                }
-               public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列 )
+
+               private void _次のフレームを確認する( out FrameQueueItem フレーム )
                {
-                       Debug.Assert( this.活性化している );
+                       if( ( 0 == this._フレームキュー.Count ) ||
+                               ( false == this._フレームキュー.TryPeek( out フレーム ) ) )  // キューから取り出さない
+                       {
+                               フレーム = null;    // キューが空だったか、Peek が一歩遅かった?(ないはずだが
+                       }
+               }
 
-                       // 活性化でエラーが発生している場合は、何もしない(何も描画しない)。
-                       if( false == this.動画の生成に成功した )
+               private void _次のフレームを取り出す( out FrameQueueItem フレーム )
+               {
+                       if( ( 0 == this._フレームキュー.Count ) ||
+                               ( false == this._フレームキュー.TryDequeue( out フレーム ) ) )  // キューから取り出す
+                       {
+                               フレーム = null;    // キューが空だったか、Peek が一歩遅かった?(ないはずだが
+                       }
+                       else
+                       {
+                               this._キューが空いた.Set();
+                       }
+               }
+
+               private void _D2DBitmapを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx, SharpDX.Direct2D1.Bitmap D2DBitmap, float 不透明度 )
+               {
+                       if( null == D2DBitmap )
                                return;
 
-                       // 以下、再生状態とデコーダ状態に応じて処理分岐。
-                       switch( this.再生状態.Value )
+                       FDK.Utilities.D2DBatchDraw( dr.D2DContext1, () => {
+                               dr.D2DContext1.Transform = 変換行列2Dpx;
+                               dr.D2DContext1.PrimitiveBlend = ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
+                               dr.D2DContext1.DrawBitmap( D2DBitmap, 不透明度, SharpDX.Direct2D1.InterpolationMode.Linear );
+                       } );
+               }
+
+               // 以下、デコードタスク用。
+
+               private FDK.同期.RWLock<FDK.カウンタ.QPCTimer> _再生タイマ = new 同期.RWLock<カウンタ.QPCTimer>();
+
+               private void _デコードタスクエントリ( object obj再生開始位置sec )
+               {
+                       FDK.Log.Info( "デコードタスクを起動しました。" );
+
+                       var 再生開始位置sec = (double) obj再生開始位置sec;
+
+                       if( 0.0 < 再生開始位置sec )
+                       this._再生位置までストリームを進める( 再生開始位置sec );
+
+                       const int EVID_キューが空いた = 0;
+                       const int EVID_デコードタスクを終了せよ = 1;
+                       var events = new System.Threading.WaitHandle[ 2 ];
+                       events[ EVID_キューが空いた ] = this._キューが空いた;
+                       events[ EVID_デコードタスクを終了せよ ] = this._デコードタスクを終了せよ;
+
+                       this._デコードタスク起動完了.Set();
+
+                       this._再生タイマ.Value.リセットする( FDK.カウンタ.QPCTimer.秒をカウントに変換して返す( 再生開始位置sec ) );
+
+                       while( System.Threading.WaitHandle.WaitAny( events ) == EVID_キューが空いた )
                        {
-                               case 再生状態ID.未再生:
-                                       // (A) 未再生なら、デコードタスクを開始する。
-                                       this.再生状態.Value = 再生状態ID.再生中;
-                                       this.デコードタスクを開始する();
-                                       break;
-
-                               case 再生状態ID.再生終了済み:
-                                       // (B) 再生が終了している場合は、何もしない(何も描画しない)。
-                                       break;
-
-                               case 再生状態ID.再生中:
-                                       // (C) 再生中なら、D2Dビットマップを描画する。
-                                       switch( this.デコーダ状態.Value )
+                               // キューが空いてるので、サンプルを1つデコードする。
+                               if( this._サンプルをひとつデコードしてフレームをキューへ格納する() )
+                               {
+                                       // キューがいっぱいになったら、空くまで待つ。
+                                       if( _キューのサイズ == this._フレームキュー.Count )
                                        {
-                                               case デコーダ状態ID.デコード中:
-                                                       // (C-a) タスクが次のサンプルフレームをデコード中なら、現在のD2Dビットマップ(直前のフレーム画像が入ってる)をもう一度描画する。
-                                                       this.D2Dビットマップを描画する( dr, 変換行列 );
-                                                       break;
-
-                                               case デコーダ状態ID.完了:
-                                                       // (C-b) タスクの次のサンプルフレームのデコード処理が完了しているなら、サンプルをD2Dビットマップに転送し、それを描画し、デコードタスクを再び起動する。
-                                                       this.サンプルをD2Dビットマップに転送する( dr );
-                                                       this.D2Dビットマップを描画する( dr, 変換行列 );
-                                                       this.デコードタスクを開始する();
-                                                       break;
-
-                                               case デコーダ状態ID.スキップ:
-                                                       // (C-c) タスクの次のサンプルフレームをスキップしたなら、現在のD2Dビットマップ(直前のフレーム画像が入ってる)をもう一度描画し、デコードタスクを再び起動する。
-                                                       this.D2Dビットマップを描画する( dr, 変換行列 );
-                                                       this.デコードタスクを開始する();
-                                                       break;
+                                               this._キューが空いた.Reset();   // 次の while で空くのを待つ。
                                        }
-                                       break;
+                               }
+                               else
+                               {
+                                       break;  // エラーあるいはストリーム終了 → デコードタスクを終了する。
+                               }
+                       }
+
+                       this._デコードタスク = null;
+
+                       FDK.Log.Info( "デコードタスクを終了しました。" );
+               }
+
+               /// <returns>
+               ///             格納できたかスキップした場合は true、エラーあるいはストリーム終了なら false。
+               ///     </returns>
+               private bool _サンプルをひとつデコードしてフレームをキューへ格納する()
+               {
+                       var sample = (SharpDX.MediaFoundation.Sample) null;
+                       var bitmap = (SharpDX.Direct2D1.Bitmap) null;
+
+                       try
+                       {
+                               long サンプルの表示時刻100ns = 0;
+
+                               #region " ソースリーダーから次のサンプルをひとつデコードする。"
+                               //-----------------
+                               var ストリームフラグ = SharpDX.MediaFoundation.SourceReaderFlags.None;
+                               int 実ストリーム番号 = 0;
+
+                               sample = this._SourceReaderEx.ReadSample(
+                                       SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
+                                       SharpDX.MediaFoundation.SourceReaderControlFlags.None,
+                                       out 実ストリーム番号,
+                                       out ストリームフラグ,
+                                       out サンプルの表示時刻100ns );
+
+                               if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) )  // BOX化コストとか気にしない
+                               {
+                                       #region " ストリーム終了 "
+                                       //----------------
+                                       if( this._ループ再生する )
+                                       {
+                                               FDK.Log.Info( "動画をループ再生します。" );
+                                               this._SourceReaderEx.SetCurrentPosition( 0 );
+                                               return this._サンプルをひとつデコードしてフレームをキューへ格納する();
+                                       }
+                                       else
+                                       {
+                                               FDK.Log.Info( "動画の再生を終了します。" );
+                                               return false;
+                                       }
+                                       //----------------
+                                       #endregion
+                               }
+                               else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) )
+                               {
+                                       #region " エラー。"
+                                       //----------------
+                                       throw new SharpDX.SharpDXException( SharpDX.Result.Fail );
+                                       //----------------
+                                       #endregion
+                               }
+                               //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Newstream ) )
+                               //{
+                               //}
+                               //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Nativemediatypechanged ) )
+                               //{
+                               //}
+                               //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Currentmediatypechanged ) )
+                               //{
+                               //   動画の途中でのサイズ変更には対応しない。
+                               //}
+                               //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.StreamTick ) )
+                               //{
+                               //}
+                               //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.AllEffectsremoved ) )
+                               //{
+                               //}
+                               //---------------------------------------------------
+                               #endregion
+
+                               //if( サンプルの表示時刻100ns < this.再生タイマ.Value.現在のリアルタイムカウント100ns単位 )
+                               //      return true;    // もう表示時刻は通り過ぎてるのでスキップする。---> この実装だとループのし始めには常に true になってしまうので却下。
+
+                               this._サンプルをビットマップに転送する( sample, out bitmap );
+
+                               this._フレームキュー.Enqueue( new FrameQueueItem()  {
+                                       D2DBitmap = bitmap,
+                                       表示時刻sec = サンプルの表示時刻100ns / ( 10.0 * 1000.0 * 1000.0 ),
+                               } );
+
+                               bitmap = null;  // キューに格納したので、ここでは Dispose しない。
+                       }
+                       catch( Exception e )
+                       {
+                               FDK.Log.Info( $"エラーが発生したので、動画の再生を終了します。[{e.Message}]" );
+                               return false;
+                       }
+                       finally
+                       {
+                               bitmap?.Dispose();
+                               sample?.Dispose();
+                       }
+
+                       return true;
+               }
+
+               private void _再生位置までストリームを進める( double 再生位置sec )
+               {
+                       #region " ストリームがシーク不可なら何もしない。"
+                       //----------------
+                       var flags = this._SourceReaderEx.GetPresentationAttribute(
+                               SharpDX.MediaFoundation.SourceReaderIndex.MediaSource,
+                               SharpDX.MediaFoundation.SourceReaderAttributeKeys.MediaSourceCharacteristics );
+                       if( ( flags & (int) SharpDX.MediaFoundation.MediaSourceCharacteristics.CanSeek ) == 0 )
+                       {
+                               FDK.Log.WARNING( "この動画はシークできないようです。" );
+                               return;
+                       }
+                       //----------------
+                       #endregion
+
+                       // ストリームの再生位置を移動する。
+
+                       this._キューをクリアする();
+
+                       long 再生位置100ns = (long) ( 再生位置sec * 1000.0 * 1000.0 * 10.0 );
+                       this._SourceReaderEx.SetCurrentPosition( 再生位置100ns );
+
+                       // キーフレームから再生位置100nsまで ReadSample する。
+
+                       var ストリームフラグ = SharpDX.MediaFoundation.SourceReaderFlags.None;
+                       int 実ストリーム番号 = 0;
+                       long サンプルの表示時刻100ns = 0;
+
+                       while( サンプルの表示時刻100ns < 再生位置100ns )
+                       {
+                               // サンプルを取得。
+                               var sample = this._SourceReaderEx.ReadSample(
+                                       SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
+                                       SharpDX.MediaFoundation.SourceReaderControlFlags.None,
+                                       out 実ストリーム番号,
+                                       out ストリームフラグ,
+                                       out サンプルの表示時刻100ns );
+
+                               // 即解放。
+                               sample?.Dispose();
+
+                               if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) )
+                               {
+                                       // ストリーム終了。
+                                       return;
+                               }
+                               else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) )
+                               {
+                                       // エラー発生。
+                                       FDK.Log.ERROR( $"動画の再生位置を移動中に、エラーが発生しました。" );
+                                       return;
+                               }
                        }
+
+                       this._ストックする();
+
+                       FDK.Log.Info( $"動画の再生位置を {再生位置sec}sec へ移動しました。" );
                }
 
-               private enum デコーダ状態ID { 完了, デコード中, スキップ }
-               private readonly FDK.同期.RWLock<デコーダ状態ID> デコーダ状態 = new RWLock<デコーダ状態ID>( デコーダ状態ID.完了 );
-               private string 動画ファイルパス = "";
-               private bool 動画の生成に成功した = false;    // 活性化時に成功の成否を設定。
-               private SharpDX.Result LastSharpDXResult = SharpDX.Result.Ok;
-               private SharpDX.MediaFoundation.Sample Sample = null;
-               private SharpDX.MediaFoundation.SourceReaderEx SourceReaderEx = null;
-               private SharpDX.WIC.Bitmap WICビットマップ = null;    // MediaFoundation は WICBitmap に出力する。
-               private SharpDX.Direct2D1.Bitmap D2Dビットマップ = null;  // WICBitmap をこれに転送して D2D に Drawする。
-               private SharpDX.MediaFoundation.MediaType MediaType = null;
-               private SharpDX.Size2 サイズdpx = new SharpDX.Size2( 1, 1 );  // CopyMemory()で使うので、Size2F じゃなく Size2 を使う。
-               private FDK.カウンタ.QPCTimer タイマ = new FDK.カウンタ.QPCTimer();
-               private long ループした際の先頭時刻 = 0;
-               private long 前フレームの時刻 = FDK.カウンタ.QPCTimer.未使用;  // 未使用 = 先頭のフレームである
-
-               private unsafe void サンプルをD2Dビットマップに転送する( デバイスリソース dr )
+               private void _ストックする()
+               {
+                       for( int i = 0; i < this._キューのサイズ; i++ )
+                               this._サンプルをひとつデコードしてフレームをキューへ格納する();
+
+                       this._キューが空いた.Reset();    // 埋まった
+               }
+
+               private unsafe void _サンプルをビットマップに転送する( SharpDX.MediaFoundation.Sample Sample, out SharpDX.Direct2D1.Bitmap D2DBitmap )
                {
                        var buffer = (SharpDX.MediaFoundation.MediaBuffer) null;
                        var buffer2d2 = (SharpDX.MediaFoundation.Buffer2D2) null;
@@ -304,13 +627,12 @@ namespace FDK.メディア
                                //-----------------
                                try
                                {
-                                       buffer = this.Sample.ConvertToContiguousBuffer();
+                                       buffer = Sample.ConvertToContiguousBuffer();
                                }
-                               catch( SharpDX.SharpDXException e )
+                               catch( Exception e )
                                {
                                        Log.ERROR( $"サンプルバッファの取得に失敗しました。(0x{e.HResult:x8})" );
-                                       this.LastSharpDXResult = e.ResultCode;
-                                       return;
+                                       throw;
                                }
                                //-----------------
                                #endregion
@@ -323,16 +645,15 @@ namespace FDK.メディア
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"サンプルバッファから Buffer2D2 へのキャストに失敗しました。(0x{e.HResult:x8})" );
-                                       this.LastSharpDXResult = e.ResultCode;
-                                       return;
+                                       throw;
                                }
                                //-----------------
                                #endregion
                                #region " サンプルバッファをロックする。"
                                //-----------------
-                               byte[] scanLine0_bp = new byte[ 8 ];   // 生ポインタが格納される。32bitなら[0~3]、64bitなら[0~7]が有効。(CPUではなく.NETに依存)
+                               byte[] scanLine0_bp = new byte[ 8 ];    // 「生ポインタ」が格納される。32bitなら[0~3]、64bitなら[0~7]が有効。(CPUではなく.NETに依存)
                                int pitch = 0;
-                               byte[] bufferStart_bp = new byte[ 8 ];  // 生ポインタが格納される。詳細は同上だが、ここでは受け取るだけで何もしない。
+                               byte[] bufferStart_bp = new byte[ 8 ];  // 「生ポインタ」が格納される。こちらは使わない。
                                int bufferLength;
                                try
                                {
@@ -346,8 +667,7 @@ namespace FDK.メディア
                                catch( SharpDX.SharpDXException e )
                                {
                                        Log.ERROR( $"サンプルバッファのロックに失敗しました。(0x{e.HResult:x8})" );
-                                       this.LastSharpDXResult = e.ResultCode;
-                                       return;
+                                       throw;
                                }
                                //-----------------
                                #endregion
@@ -358,361 +678,138 @@ namespace FDK.メディア
                                        byte* scanLine0 = null;
                                        try
                                        {
-                                               scanLine0 = (byte*) this.生ポインタを格納したbyte配列からポインタを取得して返す( scanLine0_bp );
+                                               scanLine0 = (byte*) this._生ポインタを格納したbyte配列からポインタを取得して返す( scanLine0_bp );
                                        }
                                        catch( SharpDX.SharpDXException e )
                                        {
                                                Log.ERROR( $"サンプルバッファのアドレスの取得に失敗しました。(0x{e.HResult:x8})" );
-                                               this.LastSharpDXResult = e.ResultCode;
-                                               return;
+                                               throw;
                                        }
                                        //-----------------
                                        #endregion
-                                       #region " ã\82µã\83³ã\83\97ã\83«ã\83\90ã\83\83ã\83\95ã\82¡ã\81\8bã\82\89æ\8f\8fç\94»å\85\88WICã\83\93ã\83\83ã\83\88ã\83\9eã\83\83ã\83\97ã\81¸ã\80\81ARGB32 ã\82\92 G8B8R8X8 ã\81«å¤\89æ\8f\9bã\81\97ã\81ªã\81\8cã\82\89コピーする。"
+                                       #region " ã\82µã\83³ã\83\97ã\83«ã\81\8bã\82\89 WicBitmap ã\81¸ç\94»å\83\8fã\82\92コピーする。"
                                        //-----------------
-                                       if( null == this.D2Dビットマップ )
+                                       try
                                        {
-                                               // (A) 初回は WICBitmap 経由で D2Dビットマップを新規生成する。
-                                               #region " サンプルバッファから描画先WICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピーする。"
-                                               //-----------------
-                                               try
+                                               // 描画先である WICBitmap をロックする。
+                                               using( var bitmapLock = this._WicBitmap.Lock(
+                                                       new SharpDX.Rectangle( 0, 0, this._WicBitmap.Size.Width, this._WicBitmap.Size.Height ),
+                                                       SharpDX.WIC.BitmapLockFlags.Write ) )
                                                {
-                                                       // 描画先WICビットマップをロックする。
-                                                       using( var bitmapLock = this.WICビットマップ.Lock(
-                                                               new SharpDX.Rectangle( 0, 0, this.WICビットマップ.Size.Width, this.WICビットマップ.Size.Height ),
-                                                               SharpDX.WIC.BitmapLockFlags.Write ) )
-                                                       {
-                                                               // サンプルバッファからWICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピー。
-                                                               int bitmapStride = bitmapLock.Stride;
-                                                               byte* src = scanLine0;
-                                                               byte* dest = (byte*) bitmapLock.Data.DataPointer.ToPointer();
+                                                       // サンプルバッファからWICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピーする。
+                                                       int bitmapStride = bitmapLock.Stride;
+                                                       byte* src = scanLine0;
+                                                       byte* dest = (byte*) bitmapLock.Data.DataPointer.ToPointer();
 
+                                                       if( pitch != bitmapStride )
+                                                       {
                                                                for( int y = 0; y < this.サイズdpx.Height; y++ )
                                                                {
                                                                        // ARGB32 to G8B8R8X8 ではデータ変換が不要なので、一行を一気にコピー。
-                                                                       動画.CopyMemory( dest, src, this.サイズdpx.Width * 4 );   // ARGB=4バイト。
+                                                                       動画.CopyMemory( dest, src, (int) this.サイズdpx.Width * 4 );   // ARGB=4バイト。
                                                                        src += pitch;
                                                                        dest += bitmapStride;   // bitmapStride は byte 単位
                                                                }
                                                        }
+                                                       else
+                                                       {
+                                                               // ARGB32 to G8B8R8X8 ではデータ変換が不要、かつ pitch と bitmapStride が等しいので、全行を一括してコピー。
+                                                               動画.CopyMemory( dest, src, (int) ( this.サイズdpx.Width * this.サイズdpx.Height * 4 ) );   // ARGB=4バイト。
+                                                       }
                                                }
-                                               catch( SharpDX.SharpDXException e )
-                                               {
-                                                       Log.ERROR( $"WICビットマップの Lock に失敗しました。(0x{e.HResult:x8})" );
-                                                       this.LastSharpDXResult = e.ResultCode;
-                                                       return;
-                                               }
-                                               catch( Exception e )
-                                               {
-                                                       Log.ERROR( $"サンプルバッファから WIC ビットマップへのデータの転送に失敗しました。(0x{e.HResult:x8})" );
-                                                       this.LastSharpDXResult = SharpDX.Result.GetResultFromException( e );
-                                                       return;
-                                               }
-                                               //-----------------
-                                               #endregion
-                                               #region " 描画先WICビットマップからD2Dビットマップを生成する。 "
-                                               //-----------------
-                                               try
-                                               {
-                                                       this.D2Dビットマップ?.Dispose();
-                                                       this.D2Dビットマップ = SharpDX.Direct2D1.Bitmap.FromWicBitmap(
-                                                               dr.D2DContext1,
-                                                               this.WICビットマップ );
-                                               }
-                                               catch( SharpDX.SharpDXException e )
-                                               {
-                                                       Log.ERROR( $"D2Dビットマップの作成に失敗しました。(0x{e.HResult:x8})" );
-                                                       this.LastSharpDXResult = e.ResultCode;
-                                                       return;
-                                               }
-                                               //-----------------
-                                               #endregion
-                                       }
-                                       else
-                                       {
-                                               // (B) 2回目以降は直接 D2Dビットマップへコピーする。
-                                               this.D2Dビットマップ.CopyFromMemory( new IntPtr( scanLine0 ), pitch );
-                                       }
-                                       //-----------------
-                                       #endregion
-                               }
-                               finally
-                               {
-                                       #region " サンプルバッファのロックを解除する。"
-                                       //-----------------
-                                       buffer2d2.Unlock2D();
-                                       //-----------------
-                                       #endregion
-                               }
-                       }
-                       finally
-                       {
-                               FDK.Utilities.解放する( ref buffer2d2 );
-                               FDK.Utilities.解放する( ref buffer );
-                               if( this.LastSharpDXResult.Failure )
-                               {
-                                       // 描画に失敗したら、全リソースを解放して再生終了。
-                                       this.再生状態.Value = 再生状態ID.再生終了済み;
-                                       this.全リソースを解放する();
-                               }
-                       }
-               }
-               private void D2Dビットマップを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx )
-               {
-                       if( null == this.D2Dビットマップ )
-                               return;
-
-                       Utilities.D2DBatchDraw( dr.D2DContext1, () => {
-
-                               // 変換行列とブレンドモードを設定する。
-                               dr.D2DContext1.Transform = 変換行列2Dpx;
-                               dr.D2DContext1.PrimitiveBlend =
-                                       ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
-
-                               // D2Dビットマップを描画する。
-                               dr.D2DContext1.DrawBitmap( this.D2Dビットマップ, this.不透明度0to1, SharpDX.Direct2D1.InterpolationMode.Linear );
-                       } );
-               }
-               private void デコードタスクを開始する()
-               {
-                       // 再生中なら、デコードタスクをタスクとして起動する(タスクの完了を待たず、このメソッドはすぐ抜ける)。
-                       if( this.再生状態.Value == 再生状態ID.再生中 )
-                       {
-                               Task.Run( () => {
-                                       this.デコードタスク( this.ループ再生する );
-                               } );
-                       }
-               }
-               private void デコードタスク( bool ループ再生する )
-               {
-                       //  サンプル(フレーム)を1つ取得するタスク。
-
-                       this.デコーダ状態.Value = デコーダ状態ID.デコード中;
-                       this.LastSharpDXResult = SharpDX.Result.Ok;
-                       try
-                       {
-                               var streamFlags = SharpDX.MediaFoundation.SourceReaderFlags.None;
-                               long サンプルの時刻 = 0;
-
-                               #region " SourceReader から次のサンプル(フレーム)を1つ取得する。"
-                               //-----------------
-                               {
-                                       int actualStreamIndex = 0;
-                                       try
-                                       {
-                                               this.Sample?.Dispose();
-                                               this.Sample = this.SourceReaderEx.ReadSample(
-                                                       SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
-                                                       SharpDX.MediaFoundation.SourceReaderControlFlags.None,
-                                                       out actualStreamIndex,
-                                                       out streamFlags,
-                                                       out サンプルの時刻 );
                                        }
                                        catch( SharpDX.SharpDXException e )
                                        {
-                                               Log.ERROR( $"SourceReaderEx.ReadSample() に失敗しました。(0x{e.HResult:x8})" );
-                                               this.LastSharpDXResult = e.ResultCode;
-                                               return;
+                                               Log.ERROR( $"WicBitmap の Lock に失敗しました。(0x{e.HResult:x8})" );
+                                               throw;
                                        }
-                               }
-                               //-----------------
-                               #endregion
-                               #region " 取得結果フラグに応じて、必要な処理があれば行なう。"
-                               //---------------------------------------------------
-                               if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) )  // BOX化コストとか気にしない
-                               {
-                                       #region " ストリーム終了 "
-                                       //----------------
-                                       if( ループ再生する )   // メンバじゃなくパラメータのほう。
+                                       catch( Exception e )
                                        {
-                                               // (A) ループ再生する場合
-                                               FDK.Log.Info( "動画をループ再生します。" );
-                                               this.ループした際の先頭時刻 = this.前フレームの時刻;   // このフラグがセットされるときは、ReadSample() で返されるサンプル時刻は 0 になってるので使えない。
-                                               this.SourceReaderEx.SetCurrentPosition( 0 );  // ストリーム再生位置を先頭へ。
-                                               this.デコードタスク( ループ再生する ); // 再帰で先頭サンプルを取得して終了。
-                                               return;
-                                       }
-                                       else
-                                       {
-                                               // (B) ループ再生しない場合
-                                               FDK.Log.Info( "動画の再生を終了します。" );
-                                               this.再生状態.Value = 再生状態ID.再生終了済み;
-                                               this.LastSharpDXResult = SharpDX.Result.Ok; // SharpDX のエラーによる終了じゃないよと。
-                                               return;
+                                               Log.ERROR( $"サンプルバッファから WIC ビットマップへのデータの転送に失敗しました。(0x{e.HResult:x8})" );
+                                               throw;
                                        }
                                        //----------------
                                        #endregion
-                               }
-                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Newstream ) )
-                               {
-                                       // 未対応。
-                               }
-                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Nativemediatypechanged ) )
-                               {
-                                       // 未対応。
-                               }
-                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Currentmediatypechanged ) )
-                               {
-                                       // 未対応。
-                               }
-                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.StreamTick ) )
-                               {
-                                       // 未対応。
-                               }
-                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.AllEffectsremoved ) )
-                               {
-                                       // 未対応。
-                               }
-                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) )
-                               {
-                                       #region " エラー "
+                                       #region " WicBitmap から D2DBitmap を生成する。"
                                        //----------------
-                                       FDK.Log.Info( "エラーが発生したので、動画の再生を終了します。" );
-                                       this.再生状態.Value = 再生状態ID.再生終了済み;
-                                       this.LastSharpDXResult = SharpDX.Result.Fail;
-                                       return;
-                                       //----------------
-                                       #endregion
-                               }
-                               //---------------------------------------------------
-                               #endregion
-                               #region " 前フレームの時刻を、先頭時刻+サンプルの時刻 に設定する。"
-                               //-----------------
-                               if( FDK.カウンタ.QPCTimer.未使用 == 前フレームの時刻 )    // 最初のフレームである場合(ループ時は除く)
-                               {
-                                       // 最初のフレームの取得には時間がかかることが多いので、最初のフレームを取得した場合のみ、タイマをサンプルの時刻にリセットする。
-                                       this.タイマ.リセットする( サンプルの時刻 );
-                               }
-
-                               サンプルの時刻 += this.ループした際の先頭時刻;
-                               this.前フレームの時刻 = サンプルの時刻;
-                               //-----------------
-                               #endregion
-                               #region " サンプルの表示時刻までの時刻調整を行う。"
-                               //-----------------
-                               var 現在時刻 = this.タイマ.現在のリアルタイムカウント100ns単位;
-                               if( 現在時刻 > サンプルの時刻 )
-                               {
-                                       // (A) サンプルの時刻が過去である → このサンプルとタスクは捨てる(さっさと次のサンプルへ移る)。
-                                       this.デコーダ状態.Value = デコーダ状態ID.スキップ;
-                                       return;
-                               }
-                               else
-                               {
-                                       // (B) サンプルの時刻が未来である → その時刻を過ぎるまで Sleep する。
-                                       while( 現在時刻 < サンプルの時刻 )
-                                       {
-                                               System.Threading.Thread.Sleep( 2 );      // 500fps の動画まで対応(理論上(汗
-                                               現在時刻 = タイマ.現在のリアルタイムカウント100ns単位;
-                                       }
-                               }
-                               //-----------------
-                               #endregion
-                       }
-                       finally
-                       {
-                               #region " 失敗した、または EndOfStream に達した場合は、Sample を無効化する。"
-                               //----------------
-                               if( this.LastSharpDXResult.Failure )
-                               {
-                                       FDK.Log.Info( "SharpDX でエラーが発生したので、動画の再生を終了します。" );
-                                       this.再生状態.Value = 再生状態ID.再生終了済み;
-                                       FDK.Utilities.解放する( ref this.Sample );
-                               }
-                               //----------------
-                               #endregion
-
-                               this.デコーダ状態.Value = デコーダ状態ID.完了;
-                       }
-               }
-               private void デコードキャッシング()
-               {
-                       // 最初のほうのサンプルのデコードには 100~200 ms ほどかかってしまうので、あらかじめ少しデコードしてメモリにキャッシュさせることでこれを緩和する。
-
-                       this.デコーダ状態.Value = デコーダ状態ID.デコード中;
-                       try
-                       {
-                               // 先頭から複数のフレームを読み込む。
-                               for( int i = 0; i < 60; i++ )   // 60フレームもあれば、2つめのキーフレームくらいには届くだろう……
-                               {
-                                       #region " SourceReader から次のサンプル(フレーム)を1つ取得しては解放する。"
-                                       //-----------------
-                                       var sample = (SharpDX.MediaFoundation.Sample) null;
-                                       int actualStreamIndex = 0;
-                                       var streamFlags = SharpDX.MediaFoundation.SourceReaderFlags.None;
-                                       long サンプルの時刻 = 0;
                                        try
                                        {
-                                               sample = this.SourceReaderEx.ReadSample(
-                                                       SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
-                                                       SharpDX.MediaFoundation.SourceReaderControlFlags.None,
-                                                       out actualStreamIndex,
-                                                       out streamFlags,
-                                                       out サンプルの時刻 );
+                                               D2DBitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap( this._デコードタスク用D2DDeviceContext参照, this._WicBitmap );
                                        }
                                        catch( SharpDX.SharpDXException e )
                                        {
-                                               Log.ERROR( $"SourceReaderEx.ReadSample() に失敗しました。(0x{e.HResult:x8})" );
-                                               this.LastSharpDXResult = e.ResultCode;
-                                               return;
-                                       }
-                                       finally
-                                       {
-                                               // すぐ解放。
-                                               sample?.Dispose();
+                                               Log.ERROR( $"D2Dビットマップの作成に失敗しました。(0x{e.HResult:x8})" );
+                                               throw;
                                        }
+                                       //----------------
+                                       #endregion
+                               }
+                               finally
+                               {
+                                       #region " サンプルバッファのロックを解除する。"
+                                       //-----------------
+                                       buffer2d2.Unlock2D();
                                        //-----------------
                                        #endregion
                                }
-                               // SourceReader を先頭へリセット。
-                               this.SourceReaderEx.SetCurrentPosition( 0 );
                        }
                        finally
                        {
-                               this.デコーダ状態.Value = デコーダ状態ID.完了;
+                               FDK.Utilities.解放する( ref buffer2d2 );
+                               FDK.Utilities.解放する( ref buffer );
                        }
                }
-               private void 全リソースを解放する()
-               {
-                       FDK.Utilities.解放する( ref this.D2Dビットマップ );
-                       FDK.Utilities.解放する( ref this.WICビットマップ );
-                       FDK.Utilities.解放する( ref this.Sample );
-                       FDK.Utilities.解放する( ref this.MediaType );
-                       FDK.Utilities.解放する( ref this.SourceReaderEx );
-               }
-               private unsafe void* 生ポインタを格納したbyte配列からポインタを取得して返す( byte[] 生ポインタ )
+
+               private unsafe void* _生ポインタを格納したbyte配列からポインタを取得して返す( byte[] 生ポインタ )
                {
                        if( ( 4 == IntPtr.Size ) && System.BitConverter.IsLittleEndian )
                        {
-                               // (A) 32bit, リトルエンディアン
+                               #region " (A) 32bit, リトルエンディアン "
+                               //----------------
                                int 生アドレス32bit = 0;
+
                                for( int i = 0; i < 4; i++ )
                                        生アドレス32bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 );
+
                                return new IntPtr( 生アドレス32bit ).ToPointer();
+                               //----------------
+                               #endregion
                        }
                        else if( ( 8 == IntPtr.Size ) && System.BitConverter.IsLittleEndian )
                        {
-                               // (B) 64bit, リトルエンディアン
+                               #region " (B) 64bit, リトルエンディアン "
+                               //----------------
                                long 生アドレス64bit = 0;
                                for( int i = 0; i < 8; i++ )
                                        生アドレス64bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 );
+
                                return new IntPtr( 生アドレス64bit ).ToPointer();
+                               //----------------
+                               #endregion
                        }
                        else if( ( 4 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) )
                        {
-                               // (C) 32bit, ビッグエンディアン
+                               #region " (C) 32bit, ビッグエンディアン "
+                               //----------------
                                int 生アドレス32bit = 0;
                                for( int i = 0; i < 4; i++ )
                                        生アドレス32bit += ( (int) 生ポインタ[ 4 - i ] ) << ( i * 8 );
+
                                return new IntPtr( 生アドレス32bit ).ToPointer();
+                               //----------------
+                               #endregion
                        }
                        else if( ( 8 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) )
                        {
-                               // (D) 64bit, ビッグエンディアン
+                               #region " (D) 64bit, ビッグエンディアン "
+                               //----------------
                                long 生アドレス64bit = 0;
                                for( int i = 0; i < 8; i++ )
                                        生アドレス64bit += ( (int) 生ポインタ[ 8 - i ] ) << ( i * 8 );
+
                                return new IntPtr( 生アドレス64bit ).ToPointer();
+                               //----------------
+                               #endregion
                        }
 
                        throw new SharpDX.SharpDXException( SharpDX.Result.NotImplemented, "この .NET アーキテクチャには対応していません。" );