OSDN Git Service

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