/// <summary> /// 指定したサイズの、空のテクスチャを作成する。 /// </summary> public テクスチャ(Size2F サイズ, BindFlags bindFlags = BindFlags.ShaderResource) { this._画像ファイルパス = null; this.ユーザ指定サイズ = サイズ; this._bindFlags = bindFlags; this._定数バッファ = this._定数バッファを作成する(); // テクスチャとシェーダーリソースビューを生成する。 if ((0f >= this.ユーザ指定サイズ.Width) && (0f >= this.ユーザ指定サイズ.Height)) { Log.ERROR($"テクスチャサイズが不正です。[{this.ユーザ指定サイズ}]"); return; } var テクスチャリソース = FDKUtilities.CreateShaderResourceView( DXResources.Instance.D3D11Device1, this._bindFlags, new Size2((int)this.ユーザ指定サイズ.Width, (int)this.ユーザ指定サイズ.Height)); this._ShaderResourceView = テクスチャリソース.srv; this.サイズ = this.ユーザ指定サイズ; this.Texture = テクスチャリソース.texture; }
public void 描画する(DeviceContext dc, Matrix3x2 換行列2D, float 透明度0to1 = 1.0f) { if (null == this._VideoSource) { return; } long 次のフレームの表示予定時刻100ns = this._VideoSource.Peek(); // 次のフレームがなければ負数 if (0 <= 次のフレームの表示予定時刻100ns) { if ((null != this._最後に描画したフレーム) && (次のフレームの表示予定時刻100ns < this._最後に描画したフレーム.表示時刻100ns)) { // (A) 次のフレームが前のフレームより過去 → ループしたので、タイマをリセットしてから描画する。 this._再生タイマ.リセットする(QPCTimer.秒をカウントに変換して返す(FDKUtilities.換_100ns単位からsec単位へ(次のフレームの表示予定時刻100ns))); this._次のフレームを読み込んで描画する(dc, 換行列2D, 透明度0to1); } else if (次のフレームの表示予定時刻100ns <= this._再生タイマ.現在のリアルタイムカウント100ns) { // (B) 次のフレームの表示時刻に達したので描画する。 this._次のフレームを読み込んで描画する(dc, 換行列2D, 透明度0to1); } else { // (C) 次のフレームの表示時刻にはまだ達していない → 最後に描画したフレームを再描画しておく this.最後のフレームを再描画する(dc, 換行列2D, 透明度0to1); } } else { // (D) デコードが追い付いてない、またはループせず再生が終わっている → 何も表示しない。デコードが追い付いてないなら点滅するだろう。 this._最後に描画したフレーム?.Dispose(); this._最後に描画したフレーム = null; } }
// 生成と終了 /// <summary> /// 指定した画像ファイルからテクスチャを作成する。 /// </summary> public テクスチャ(VariablePath 画像ファイルパス, BindFlags bindFlags = BindFlags.ShaderResource) { this._画像ファイルパス = 画像ファイルパス; this.ユーザ指定サイズ = Size2F.Zero; this._bindFlags = bindFlags; this._定数バッファ = this._定数バッファを作成する(); // テクスチャとシェーダーリソースビューを生成する。 if (this._画像ファイルパス.数なしパス.Nullでも空でもない()) { if (!System.IO.File.Exists(this._画像ファイルパス.数なしパス)) { Log.ERROR($"画像ファイルが存在しません。[{this._画像ファイルパス.変数付きパス}]"); return; } var テクスチャリソース = FDKUtilities.CreateShaderResourceViewFromFile( DXResources.Instance.D3D11Device1, this._bindFlags, this._画像ファイルパス); this._ShaderResourceView = テクスチャリソース.srv; this.サイズ = テクスチャリソース.viewSize; this.Texture = テクスチャリソース.texture; } }
protected override void On活性化() { var d3dDevice = グラフィックデバイス.Instance.D3DDevice; Debug.Assert(null != d3dDevice, "D3DDevice が取得されていません。"); #region " 定数バッファを生成する。" //---------------- var cBufferDesc = new BufferDescription() { Usage = ResourceUsage.Dynamic, // 動的使用法 BindFlags = BindFlags.ConstantBuffer, // 定数バッファ CpuAccessFlags = CpuAccessFlags.Write, // CPUから書き込む OptionFlags = ResourceOptionFlags.None, SizeInBytes = SharpDX.Utilities.SizeOf <ST定数バッファの転送元データ>(), // バッファサイズ StructureByteStride = 0, }; this._ConstantBuffer = new SharpDX.Direct3D11.Buffer(d3dDevice, cBufferDesc); //---------------- #endregion #region " テクスチャとシェーダーリソースビューを生成する。" //---------------- if (this._画像ファイルパス?.数なしパス.Nullでも空でもない() ?? false) { // (A) 画像ファイルから生成する場合。 if (false == System.IO.File.Exists(this._画像ファイルパス.数なしパス)) { Log.ERROR($"画像ファイルが存在しません。[{this._画像ファイルパス.変数付きパス}]"); return; } var 戻り値 = FDKUtilities.CreateShaderResourceViewFromFile( d3dDevice, this._bindFlags, this._画像ファイルパス); this._ShaderResourceView = 戻り値.srv; this.サイズ = 戻り値.viewSize; this.Texture = 戻り値.texture; } else if ((0f < this.ユーザ指定サイズ.Width) && (0f < this.ユーザ指定サイズ.Height)) { // (B) サイズを指定して空のテクスチャを生成する場合。 var 戻り値 = FDKUtilities.CreateShaderResourceView( d3dDevice, this._bindFlags, new Size2((int)this.ユーザ指定サイズ.Width, (int)this.ユーザ指定サイズ.Height)); this._ShaderResourceView = 戻り値.srv; this.Texture = 戻り値.texture; this.サイズ = this.ユーザ指定サイズ; } else { throw new InvalidOperationException(); } //---------------- #endregion }
public static int 最小公倍数を返す(int m, int n) { if ((0 >= m) || (0 >= n)) { throw new Exception("引数に0以下の数は指定できません。"); } return(m * n / FDKUtilities.最大公約数を返す(m, n)); }
public static (int 分子, int 分母) 約分する(int 分子, int 分母) { if (0 == 分子) { return(0, 1); } int 最大公約数 = 1; while (1 != (最大公約数 = FDKUtilities.最大公約数を返す(分子, 分母))) { 分子 /= 最大公約数; 分母 /= 最大公約数; } return(分子, 分母); }
public MediaFoundationFileVideoSource(VariablePath ファイルパス, double 再生速度 = 1.0) { this.再生速度 = Math.Max(0.01, Math.Min(10.0, 再生速度)); #region " フレームキューを生成。" //---------------- // キューサイズは3フレームとする。 this._FrameQueue = new BlockingQueue <VideoFrame>(3); //---------------- #endregion #region " ファイルから SourceReaderEx を生成する。" //---------------- using (var ビデオ属性 = new MediaAttributes()) { // DXVAに対応しているGPUの場合には、それをデコードに利用するよう指定する。 ビデオ属性.Set(SourceReaderAttributeKeys.D3DManager, グラフィックデバイス.Instance.DXGIDeviceManager); // 追加のビデオプロセッシングを有効にする。 ビデオ属性.Set(SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true); // 真偽値が bool だったり // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。 ビデオ属性.Set(SinkWriterAttributeKeys.ReadwriteDisableConverters, 0); // int だったり // 属性を使って、SourceReaderEx を生成。 using (var sourceReader = new SourceReader(ファイルパス.数なしパス, ビデオ属性)) // パスは URI 扱い this._SourceReaderEx = sourceReader.QueryInterface <SourceReaderEx>(); } // 最初のビデオストリームだけを選択。 this._SourceReaderEx.SetStreamSelection(SourceReaderIndex.AllStreams, false); this._SourceReaderEx.SetStreamSelection(SourceReaderIndex.FirstVideoStream, true); //---------------- #endregion #region " ビデオの長さを取得する。" //---------------- this.総演奏時間sec = FDKUtilities.換_100ns単位からsec単位へ( this._SourceReaderEx.GetPresentationAttribute(SourceReaderIndex.MediaSource, PresentationDescriptionAttributeKeys.Duration)) / this.再生速度; //---------------- #endregion #region " デコーダを選択し、完全メディアタイプを取得する。" //---------------- // 部分メディアタイプを設定する。 using (var videoMediaType = new MediaType()) { // フォーマットは ARGB32 で固定とする。(SourceReaderEx を使わない場合、H264 では ARGB32 が選べないので注意。) videoMediaType.Set(MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video); videoMediaType.Set(MediaTypeAttributeKeys.Subtype, VideoFormatGuids.Argb32); // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。 this._SourceReaderEx.SetCurrentMediaType(SourceReaderIndex.FirstVideoStream, videoMediaType); } // 完成されたメディアタイプを取得する。 this._MediaType = this._SourceReaderEx.GetCurrentMediaType(SourceReaderIndex.FirstVideoStream); //---------------- #endregion #region " ビデオのフレームサイズを取得する。" //---------------- long packedFrameSize = this._MediaType.Get(MediaTypeAttributeKeys.FrameSize); // 動画の途中でのサイズ変更には対応しない。 this.フレームサイズ = new Size2F((packedFrameSize >> 32) & 0xFFFFFFFF, (packedFrameSize) & 0xFFFFFFFF); //---------------- #endregion }
private void _デコードタスクエントリ(object obj引数) { Log.現在のスレッドに名前をつける("ビデオデコード"); var 引数 = ((double 再生開始時刻sec, double 再生速度))obj引数; double 再生速度 = Math.Max(0.01, Math.Min(10.0, 引数.再生速度)); double 再生開始時刻sec = Math.Max(0.0, 引数.再生開始時刻sec) / 再生速度; long 再生開始時刻100ns = FDKUtilities.換_sec単位から100ns単位へ(再生開始時刻sec); #region " 再生開始時刻までシーク(1)。" //---------------- if (0.0 < 再生開始時刻sec) { if (this.総演奏時間sec <= 再生開始時刻sec) { Log.Info($"再生開始時刻が総演奏時間を超えています。タスクを終了します。"); return; } // 再生開始時刻 が 0 なら、これを呼び出さないこと(ガタつきの原因になるため)。 this._SourceReaderEx.SetCurrentPosition(再生開始時刻100ns); } //---------------- #endregion // シーク(1)では、SetCurrentPosition() の仕様により、「指定された位置を超えない一番近いキーフレーム」までしか移動できない。 // なので、残りのシーク(キーフレームから再生開始時刻まで)を、メインループ内で引き続き行う。 // (残りのシークが終わって、ようやく デコード起動完了通知 がセットされる。) bool シーク中である = (0.0 < 再生開始時刻sec); if (!シーク中である) { this._デコード起動完了通知.Set(); // シークはしない } // メインループ。 while (true) { if (this._デコードキャンセル.Token.IsCancellationRequested) { Log.Info($"キャンセル通知を受信しました。"); break; } if (!this._サンプルをひとつデコードしてフレームをキューへ格納する(再生速度)) { break; // エラーまたは再生終了 } if (シーク中である) { #region " 再生開始時刻までシーク(2)。" //---------------- var frame = this._FrameQueue.Peek(); // 今格納されたフレームを覗く if (frame.表示時刻100ns >= 再生開始時刻100ns) { // シーク終了;今回のフレームから再生の対象(なのでTakeしない)。 シーク中である = false; // シークが終わったので、呼び出し元に起動完了を通知する。 this._デコード起動完了通知.Set(); } else { // 取り出して、すぐに破棄。 frame = this._FrameQueue.Take(); frame.Dispose(); frame = null; } //---------------- #endregion } } Log.Info($"タスクを終了します。"); }
private void _SourceReaderEx生成後の初期化() { Debug.Assert(null != this._SourceReaderEx); #region " ストリームを列挙して、Video と Audio の実ストリーム番号を調べる。" //---------------- try { for (int index = 0; true; index++) // 例外が発生するまでループ { using (var mediaType = this._SourceReaderEx.GetCurrentMediaType(index)) { if (mediaType.MajorType == MediaTypeGuids.Video) { this._Videoのストリーム番号 = index; } else if (mediaType.MajorType == MediaTypeGuids.Audio) { this._Audioのストリーム番号 = index; } } } } catch (SharpDXException e) { if (e.ResultCode == SharpDX.MediaFoundation.ResultCode.InvalidStreamNumber) { // 列挙完了 } else { throw; } } //---------------- #endregion this._SourceReaderEx.SetStreamSelection(SourceReaderIndex.AllStreams, false); this._長さsec = FDKUtilities.換_100ns単位からsec単位へ( this._SourceReaderEx.GetPresentationAttribute(SourceReaderIndex.MediaSource, PresentationDescriptionAttributeKeys.Duration)); if (0 <= this._Videoのストリーム番号) { #region " Video の初期化 " //---------------- this._SourceReaderEx.SetStreamSelection(this._Videoのストリーム番号, true); // 部分メディアタイプを設定する。 using (var videoMediaType = new MediaType()) { // フォーマットは ARGB32 で固定とする。(SourceReaderEx を使わない場合、H264 では ARGB32 が選べないので注意。) videoMediaType.Set(MediaTypeAttributeKeys.MajorType, MediaTypeGuids.Video); videoMediaType.Set(MediaTypeAttributeKeys.Subtype, VideoFormatGuids.Argb32); // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。 this._SourceReaderEx.SetCurrentMediaType(this._Videoのストリーム番号, videoMediaType); } // 完成されたメディアタイプを取得する。 this._VideoのMediaType = this._SourceReaderEx.GetCurrentMediaType(this._Videoのストリーム番号); // ビデオのフレームサイズを取得する。 long packedFrameSize = this._VideoのMediaType.Get(MediaTypeAttributeKeys.FrameSize); // 動画の途中でのサイズ変更には対応しない。 this._Videoのフレームサイズ = new Size2F((packedFrameSize >> 32) & 0xFFFFFFFF, (packedFrameSize) & 0xFFFFFFFF); //---------------- #endregion this._VideoSource = new StreamingVideoSource(this._Videoのフレームサイズ); } if (0 <= this._Audioのストリーム番号) { #region " Audio の初期化 " //---------------- this._SourceReaderEx.SetStreamSelection(this._Audioのストリーム番号, true); // 部分メディアタイプを設定する。 var wf = SharpDX.Multimedia.WaveFormat.CreateIeeeFloatWaveFormat(this._Audioのフォーマット.SampleRate, this._Audioのフォーマット.Channels); MediaFactory.CreateAudioMediaType(ref wf, out AudioMediaType audioMediaType); // 作成した部分メディアタイプを SourceReader にセットする。必要なデコーダが見つからなかったら、ここで例外が発生する。 using (audioMediaType) this._SourceReaderEx.SetCurrentMediaType(this._Audioのストリーム番号, audioMediaType); // 完成されたメディアタイプを取得する。 this._AudioのMediaType = this._SourceReaderEx.GetCurrentMediaType(this._Audioのストリーム番号); // フォーマットを取得する(念のため)。 var _CscoreAudioMediaType = new CSCore.MediaFoundation.MFMediaType(this._AudioのMediaType.NativePointer); // ネイティブポインタの共有で生成したのでDispose不要。 this._Audioのフォーマット = _CscoreAudioMediaType.ToWaveFormat(CSCore.MediaFoundation.MFWaveFormatExConvertFlags.Normal); _CscoreAudioMediaType = null; //---------------- #endregion this._WaveSource = new StreamingWaveSource(this._Audioのフォーマット); } #region " デコード開始。" //---------------- this._デコードキャンセル = new CancellationTokenSource(); this._デコード起動完了通知 = new ManualResetEvent(false); this._デコードタスク = Task.Factory.StartNew(this._デコードタスクエントリ, this._デコードキャンセル.Token); this._デコード起動完了通知.WaitOne(); //---------------- #endregion //Thread.Sleep( 500 ); // デコードデータが蓄積されるまで待機(手抜き }