2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Threading.Tasks;
11 /// 活性化後の最初の進行描画時に、再生が開始される。
13 public unsafe class 動画 : Activity
15 public enum 再生状態ID { 未再生, 再生中, 再生終了済み }
16 public 再生状態ID 再生状態 { get; protected set; } = 再生状態ID.未再生;
17 public bool 加算合成 { get; set; } = false;
18 public float 不透明度0to1 { get; set; } = 1.0f;
19 public bool ループ再生する { get; set; } = false;
21 public 動画( string 動画ファイルパス )
23 this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
25 protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
27 // todo: デバイスがリサイズされると動画もリセットされてしまう。活性化とコードを分離すること(課題)。
29 this.再生状態 = 再生状態ID.未再生;
31 string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス ); // Log出力用。
34 this.動画の生成に成功した = false;
38 this.デコーダ = new 動画デコーダ( dr, this.動画ファイルパス, this.ループ再生する );
39 this.デコーダ.デコードキャッシング();
43 this.動画の生成に成功した = true;
44 Log.Info( $"{Utilities.現在のメソッド名}: 動画を生成しました。[{変数付きファイルパス}]" );
46 // 準備完了。以降は、サンプルを読み込みたいときに、適宜 ReadSample() する。
47 // コールバックを使っていない(非同期である)ので、IMFSourceReader::ReadSample() はサンプルが用意できるまでブロックすることに注意。
52 //this.活性化していない = true; --> 失敗しても、一応活性化は成功とする。(進行描画はしない。)
55 protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
57 // todo: デバイスがリサイズされると動画もリセットされてしまう。非活性化とコードを分離すること(課題)。
61 Log.Info( $"{Utilities.現在のメソッド名}: 動画を解放しました。[{フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス )}]" );
63 public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f, bool ループ再生する = false )
65 // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
67 dr.拡大行列DPXtoPX // スケーリング(1) DPX → PX
68 * SharpDX.Matrix3x2.Scaling( 描画先矩形dpx.Width / this.デコーダ.サイズdpx.Width, 描画先矩形dpx.Height / this.デコーダ.サイズdpx.Height ) // スケーリング(2)
69 * SharpDX.Matrix3x2.Translation( 描画先矩形dpx.Left * dr.拡大率DPXtoPX横方向, 描画先矩形dpx.Top * dr.拡大率DPXtoPX縦方向 ); // 平行移動(物理単位)、
72 this.進行描画する( dr, 変換行列2Dpx );
74 public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列 )
76 Debug.Assert( this.活性化している );
78 // 活性化でエラーが発生している場合は、何もしない(何も描画しない)。
79 if( false == this.動画の生成に成功した )
82 // 以下、再生状態とデコーダ状態に応じて処理分岐。
86 // (A) 未再生なら、デコードを開始する。(タイマはまだ開始しない。)
87 this.再生状態 = 再生状態ID.再生中;
88 this.デコーダ.デコードを開始する();
93 // (B) 再生が終了している場合は、何もしない(何も描画しない)。
97 // (C) 再生中なら、D2Dビットマップを描画する。
99 var dummy = (SharpDX.Direct2D1.Bitmap) null;
101 if( this.デコーダ.次のビットマップの有無を確認する( out dummy, out 再生時刻sec ) &&
102 ( this.タイマ.現在のリアルタイムカウント秒単位 >= 再生時刻sec + this.デコーダ.ループした際の先頭時刻sec ) )
104 // (C-a) 確認できて、かつ再生時刻になった場合 → サンプルをD2Dビットマップに転写し、描画する。
105 this.前回表示したD2Dビットマップ?.Dispose();
106 this.デコーダ.キューからビットマップを取り出す( out this.前回表示したD2Dビットマップ, out 再生時刻sec ); // 正式に取り出す。
107 this.D2Dビットマップを描画する( dr, 変換行列 );
111 // (C-b) 確認できなかった(キューが空だった)か、または再生時刻がまだの場合 → 現在のD2Dビットマップを再度表示する。
112 this.D2Dビットマップを描画する( dr, 変換行列 );
120 protected 動画デコーダ デコーダ = null;
122 private string 動画ファイルパス = "";
123 private bool 動画の生成に成功した = false; // 活性化時に成功の成否を設定。
124 private SharpDX.Direct2D1.Bitmap 前回表示したD2Dビットマップ = null;
126 private FDK.カウンタ.QPCTimer タイマ = new FDK.カウンタ.QPCTimer();
128 private void D2Dビットマップを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx )
130 if( null == this.前回表示したD2Dビットマップ )
133 Utilities.D2DBatchDraw( dr.D2DContext1, () => {
135 // 変換行列とブレンドモードを設定する。
136 dr.D2DContext1.Transform = 変換行列2Dpx;
137 dr.D2DContext1.PrimitiveBlend = ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
140 dr.D2DContext1.DrawBitmap( this.前回表示したD2Dビットマップ, this.不透明度0to1, SharpDX.Direct2D1.InterpolationMode.Linear );
143 private void 全リソースを解放する()
145 FDK.Utilities.解放する( ref this.前回表示したD2Dビットマップ );