OSDN Git Service

動画デコーダスレッドでD2Dビットマップの生成まで行うようにした。
[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.Threading.Tasks;
6 using FDK.同期;
7
8 namespace FDK.メディア
9 {
10         /// <remarks>
11         /// 活性化後の最初の進行描画時に、再生が開始される。
12         /// </remarks>
13         public unsafe class 動画 : Activity
14         {
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
21                 public 動画( string 動画ファイルパス )
22                 {
23                         this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
24                 }
25                 protected override void Onデバイス依存リソースの作成( デバイスリソース dr )
26                 {
27                         // todo: デバイスがリサイズされると動画もリセットされてしまう。活性化とコードを分離すること(課題)。
28
29                         this.再生状態 = 再生状態ID.未再生;
30
31                         string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス );   // Log出力用。
32                         try
33                         {
34                                 this.動画の生成に成功した = false;
35
36                                 #region " デコーダを生成する。"
37                                 //----------------
38                                 this.デコーダ = new 動画デコーダ( dr, this.動画ファイルパス, this.ループ再生する );
39                                 this.デコーダ.デコードキャッシング();
40                                 //----------------
41                                 #endregion
42
43                                 this.動画の生成に成功した = true;
44                                 Log.Info( $"{Utilities.現在のメソッド名}: 動画を生成しました。[{変数付きファイルパス}]" );
45
46                                 // 準備完了。以降は、サンプルを読み込みたいときに、適宜 ReadSample() する。
47                                 // コールバックを使っていない(非同期である)ので、IMFSourceReader::ReadSample() はサンプルが用意できるまでブロックすることに注意。
48                         }
49                         catch
50                         {
51                                 this.全リソースを解放する();
52                                 //this.活性化していない = true; --> 失敗しても、一応活性化は成功とする。(進行描画はしない。)
53                         }
54                 }
55                 protected override void Onデバイス依存リソースの解放( デバイスリソース dr )
56                 {
57                         // todo: デバイスがリサイズされると動画もリセットされてしまう。非活性化とコードを分離すること(課題)。
58
59                         this.全リソースを解放する();
60
61                         Log.Info( $"{Utilities.現在のメソッド名}: 動画を解放しました。[{フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス )}]" );
62                 }
63                 public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f, bool ループ再生する = false )
64                 {
65                         // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
66                         var 変換行列2Dpx =
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縦方向 );  // 平行移動(物理単位)、
70
71                         // 描画する。
72                         this.進行描画する( dr, 変換行列2Dpx );
73                 }
74                 public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列 )
75                 {
76                         Debug.Assert( this.活性化している );
77
78                         // 活性化でエラーが発生している場合は、何もしない(何も描画しない)。
79                         if( false == this.動画の生成に成功した )
80                                 return;
81
82                         // 以下、再生状態とデコーダ状態に応じて処理分岐。
83                         switch( this.再生状態 )
84                         {
85                                 case 再生状態ID.未再生:
86                                         // (A) 未再生なら、デコードを開始する。(タイマはまだ開始しない。)
87                                         this.再生状態 = 再生状態ID.再生中;
88                                         this.デコーダ.デコードを開始する();
89                                         this.タイマ.リセットする();
90                                         break;
91
92                                 case 再生状態ID.再生終了済み:
93                                         // (B) 再生が終了している場合は、何もしない(何も描画しない)。
94                                         break;
95
96                                 case 再生状態ID.再生中:
97                                         // (C) 再生中なら、D2Dビットマップを描画する。
98                                         {
99                                                 var dummy = (SharpDX.Direct2D1.Bitmap) null;
100                                                 var 再生時刻sec = 0.0;
101                                                 if( this.デコーダ.次のビットマップの有無を確認する( out dummy, out 再生時刻sec ) &&
102                                                         ( this.タイマ.現在のリアルタイムカウント秒単位 >= 再生時刻sec + this.デコーダ.ループした際の先頭時刻sec ) )
103                                                 {
104                                                         // (C-a) 確認できて、かつ再生時刻になった場合 → サンプルをD2Dビットマップに転写し、描画する。
105                                                         this.前回表示したD2Dビットマップ?.Dispose();
106                                                         this.デコーダ.キューからビットマップを取り出す( out this.前回表示したD2Dビットマップ, out 再生時刻sec );  // 正式に取り出す。
107                                                         this.D2Dビットマップを描画する( dr, 変換行列 );
108                                                 }
109                                                 else
110                                                 {
111                                                         // (C-b) 確認できなかった(キューが空だった)か、または再生時刻がまだの場合 → 現在のD2Dビットマップを再度表示する。
112                                                         this.D2Dビットマップを描画する( dr, 変換行列 );
113                                                         break;
114                                                 }
115                                         }
116                                         break;
117                         }
118                 }
119
120                 protected 動画デコーダ デコーダ = null;
121
122                 private string 動画ファイルパス = "";
123                 private bool 動画の生成に成功した = false;    // 活性化時に成功の成否を設定。
124                 private SharpDX.Direct2D1.Bitmap 前回表示したD2Dビットマップ = null;
125
126                 private FDK.カウンタ.QPCTimer タイマ = new FDK.カウンタ.QPCTimer();
127
128                 private void D2Dビットマップを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx )
129                 {
130                         if( null == this.前回表示したD2Dビットマップ )
131                                 return;
132
133                         Utilities.D2DBatchDraw( dr.D2DContext1, () => {
134
135                                 // 変換行列とブレンドモードを設定する。
136                                 dr.D2DContext1.Transform = 変換行列2Dpx;
137                                 dr.D2DContext1.PrimitiveBlend = ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
138
139                                 // D2Dビットマップを描画する。
140                                 dr.D2DContext1.DrawBitmap( this.前回表示したD2Dビットマップ, this.不透明度0to1, SharpDX.Direct2D1.InterpolationMode.Linear );
141                         } );
142                 }
143                 private void 全リソースを解放する()
144                 {
145                         FDK.Utilities.解放する( ref this.前回表示したD2Dビットマップ );
146
147                         this.デコーダ.Dispose();
148                 }
149         }
150 }