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;
20 public int キューのサイズ { get; set; } = 16;
23 get { return ( null != this.デコーダ ) ? this.デコーダ.長さsec : 0.0; }
26 public 動画( string 動画ファイルパス, int キューのサイズ )
28 this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
29 this.キューのサイズ = キューのサイズ;
31 protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
33 // todo: デバイスがリサイズされると動画もリセットされてしまう。活性化とコードを分離すること(課題)。
35 this.再生状態 = 再生状態ID.未再生;
37 string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス ); // Log出力用。
40 this.動画の生成に成功した = false;
43 this.デコーダ = new 動画デコーダ( dr, this.動画ファイルパス, this.ループ再生する, this.キューのサイズ );
44 this.デコーダ.現在の再生時刻100ns = () => {
45 return this.タイマ.Value.現在のリアルタイムカウント100ns単位;
47 this.デコーダ.デコードキャッシング();
49 this.動画の生成に成功した = true;
50 Log.Info( $"{Utilities.現在のメソッド名}: 動画を生成しました。[{変数付きファイルパス}]" );
52 // 準備完了。以降は、サンプルを読み込みたいときに、適宜 ReadSample() する。
53 // コールバックを使っていない(非同期である)ので、IMFSourceReader::ReadSample() はサンプルが用意できるまでブロックすることに注意。
58 //this.活性化していない = true; --> 失敗しても、一応活性化は成功とする。(進行描画はしない。)
61 protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
63 // todo: デバイスがリサイズされると動画もリセットされてしまう。非活性化とコードを分離すること(課題)。
67 Log.Info( $"{Utilities.現在のメソッド名}: 動画を解放しました。[{フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス )}]" );
69 public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f, bool ループ再生する = false )
71 // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
73 dr.拡大行列DPXtoPX // スケーリング(1) DPX → PX
74 * SharpDX.Matrix3x2.Scaling( 描画先矩形dpx.Width / this.デコーダ.サイズdpx.Width, 描画先矩形dpx.Height / this.デコーダ.サイズdpx.Height ) // スケーリング(2)
75 * SharpDX.Matrix3x2.Translation( 描画先矩形dpx.Left * dr.拡大率DPXtoPX横方向, 描画先矩形dpx.Top * dr.拡大率DPXtoPX縦方向 ); // 平行移動(物理単位)、
78 this.進行描画する( dr, 変換行列2Dpx );
80 public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列 )
82 Debug.Assert( this.活性化している );
84 // 活性化でエラーが発生している場合は、何もしない(何も描画しない)。
85 if( false == this.動画の生成に成功した )
88 // 以下、再生状態とデコーダ状態に応じて処理分岐。
92 // (A) 未再生なら、デコードを開始する。(タイマはまだ開始しない。)
93 this.再生状態 = 再生状態ID.再生中;
94 this.デコーダ.デコードを開始する();
95 this.タイマ.Value.リセットする();
99 // (B) 再生が終了している場合は、何もしない(何も描画しない)。
103 // (C) 再生中なら、D2Dビットマップを描画する。
105 var dummy = (SharpDX.Direct2D1.Bitmap) null;
107 bool 次がある = this.デコーダ.次のビットマップの有無を確認する( out dummy, out 再生時刻sec );
108 double 現在時刻sec = this.タイマ.Value.現在のリアルタイムカウント秒単位;
109 if( 次がある && ( 現在時刻sec >= 再生時刻sec ) )
111 // (C-a) 確認できて、かつ再生時刻になった場合 → サンプルをD2Dビットマップに転写し、描画する。
112 this.前回表示したD2Dビットマップ?.Dispose();
113 this.デコーダ.キューからビットマップを取り出す( out this.前回表示したD2Dビットマップ, out 再生時刻sec ); // 正式に取り出す。
114 this.D2Dビットマップを描画する( dr, 変換行列 );
118 // (C-b) 確認できなかった(キューが空だった)か、または再生時刻がまだの場合 → 現在のD2Dビットマップを再度表示する。
119 this.D2Dビットマップを描画する( dr, 変換行列 );
126 public void 再生位置secを移動する( double 再生位置sec )
128 this.デコーダ?.再生位置secを移動する( 再生位置sec );
131 protected 動画デコーダ デコーダ = null;
133 private string 動画ファイルパス = "";
134 private bool 動画の生成に成功した = false; // 活性化時に成功の成否を設定。
135 private SharpDX.Direct2D1.Bitmap 前回表示したD2Dビットマップ = null;
137 private FDK.同期.RWLock<FDK.カウンタ.QPCTimer> タイマ = new RWLock<カウンタ.QPCTimer>( new カウンタ.QPCTimer() );
139 private void D2Dビットマップを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx )
141 if( null == this.前回表示したD2Dビットマップ )
144 Utilities.D2DBatchDraw( dr.D2DContext1, () => {
146 // 変換行列とブレンドモードを設定する。
147 dr.D2DContext1.Transform = 変換行列2Dpx;
148 dr.D2DContext1.PrimitiveBlend = ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
151 dr.D2DContext1.DrawBitmap( this.前回表示したD2Dビットマップ, this.不透明度0to1, SharpDX.Direct2D1.InterpolationMode.Linear );
154 private void 全リソースを解放する()
156 FDK.Utilities.解放する( ref this.前回表示したD2Dビットマップ );