using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace FDK.メディア { public class 動画 : FDK.Activity { public string 動画ファイルパス { get; protected set; } = null; public SharpDX.Size2F サイズdpx { get; protected set; } public double 長さsec { get; protected set; } = 0.0; public bool 加算合成 { get; set; } = false; /// /// 0:透明 ~ 1:不透明 /// public float 不透明度 { get; set; } = 1.0f; public 動画( string 動画ファイルパス, int キューのサイズ = 16 ) { this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス ); this.キューのサイズ = キューのサイズ; } public void 再生を開始する( double 開始位置sec = 0.0, bool ループ再生する = false ) { this.ループ再生する = ループ再生する; // タスクを起動する。 this.デコードタスク.Value = System.Threading.Tasks.Task.Factory.StartNew( this.デコードタスクエントリ, (object) 開始位置sec ); this.デコードタスク起動完了.WaitOne(); } public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f ) { // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。 var 変換行列2Dpx = dr.拡大行列DPXtoPX // スケーリング(1) DPX → PX * SharpDX.Matrix3x2.Scaling( 描画先矩形dpx.Width / this.サイズdpx.Width, 描画先矩形dpx.Height / this.サイズdpx.Height ) // スケーリング(2) * SharpDX.Matrix3x2.Translation( 描画先矩形dpx.Left * dr.拡大率DPXtoPX横方向, 描画先矩形dpx.Top * dr.拡大率DPXtoPX縦方向 ); // 平行移動(物理単位)、 this.進行描画する( dr, 変換行列2Dpx ); } public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列 ) { #region " 条件チェック。" //---------------- if( null == this.デコードタスク.Value || // 再生をまだ開始していないか、あるいはすでに再生を完了してデコードタスクを終了済みである。 null == this.SourceReaderEx || // 動画の準備に失敗した。 null == this.MediaType || // 同上 null == this.WicBitmap ) // 同上 { return; } //---------------- #endregion Action 次のフレームを表示する = ( frame ) => { this.次のフレームを取り出す( out frame ); this.最後に表示したフレーム?.Dispose(); this.最後に表示したフレーム = frame; this.D2DBitmapを描画する( dr, 変換行列, frame.D2DBitmap ); }; Action 前のフレームを表示する = () => { this.D2DBitmapを描画する( dr, 変換行列, this.最後に表示したフレーム?.D2DBitmap ); }; var フレーム = (FrameQueueItem) null; this.次のフレームを確認する( out フレーム ); if( null != フレーム ) // 次のフレームがある。 { // (A) 次のフレームが前のフレームより過去 → ループしたので、タイマをリセットする。 if( ( null != this.最後に表示したフレーム ) && ( フレーム.表示時刻sec < this.最後に表示したフレーム.表示時刻sec ) ) { this.再生タイマ.Value.リセットする( FDK.カウンタ.QPCTimer.秒をカウントに変換して返す( フレーム.表示時刻sec ) ); 次のフレームを表示する( フレーム ); } // (B) 次のフレームの表示時刻に達した。 else if( フレーム.表示時刻sec <= this.再生タイマ.Value.現在のリアルタイムカウント秒単位 ) { 次のフレームを表示する( フレーム ); } // (C) 次のフレームの表示時刻にはまだ達していない。 else { 前のフレームを表示する(); } } // (D) デコードが追い付いてない、またはループせず再生が終わっている。 else { // 何も表示しない → 真っ黒画像。デコードが追い付いてないなら点滅するだろう。 } } protected int キューのサイズ = 0; protected class FrameQueueItem : IDisposable { public double 表示時刻sec = 0; public SharpDX.Direct2D1.Bitmap D2DBitmap = null; public void Dispose() { FDK.Utilities.解放する( ref this.D2DBitmap ); } } protected ConcurrentQueue フレームキュー = null; protected bool ループ再生する = false; protected FDK.同期.RWLock デコードタスク = new 同期.RWLock(); protected System.Threading.AutoResetEvent デコードタスク起動完了 = null; protected System.Threading.ManualResetEvent キューが空いた = null; protected System.Threading.AutoResetEvent デコードタスクを終了せよ = null; protected SharpDX.MediaFoundation.SourceReaderEx SourceReaderEx = null; protected SharpDX.MediaFoundation.MediaType MediaType = null; protected SharpDX.WIC.Bitmap WicBitmap = null; // MediaFoundation は WICBitmap に出力する。 protected SharpDX.Direct2D1.DeviceContext1 デコードタスク用D2DDeviceContext参照 = null; // D2Dはスレッドセーフであること。 protected FrameQueueItem 最後に表示したフレーム = null; protected override void On活性化( デバイスリソース dr ) { this.デコードタスク.Value = null; // タスクが起動していないときは null であることを保証する。 this.デコードタスク起動完了 = new System.Threading.AutoResetEvent( false ); this.キューが空いた = new System.Threading.ManualResetEvent( true ); this.デコードタスクを終了せよ = new System.Threading.AutoResetEvent( false ); this.再生タイマ.Value = new カウンタ.QPCTimer(); } protected override void On非活性化( デバイスリソース dr ) { this.キューが空いた.Close(); this.デコードタスクを終了せよ.Close(); } protected override void Onデバイス依存リソースの作成( デバイスリソース dr ) { FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" ); this.デコードタスク用D2DDeviceContext参照 = dr.D2DContext1; this.フレームキュー = new ConcurrentQueue(); this.最後に表示したフレーム = null; // 動画ファイルから、SourceReaderEx, MediaType, WicBitmap を生成する。 string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス ); // Log出力用。 #region " 動画ファイルパスの有効性を確認する。" //----------------- if( string.IsNullOrEmpty( 動画ファイルパス ) ) { Log.ERROR( $"動画ファイルパスが null または空文字列です。[{変数付きファイルパス}]" ); return; } if( false == System.IO.File.Exists( 動画ファイルパス ) ) { Log.ERROR( $"動画ファイルが存在しません。[{変数付きファイルパス}]" ); return; } //----------------- #endregion #region " SourceReaderEx を生成する。" //----------------- try { using( var 属性 = new SharpDX.MediaFoundation.MediaAttributes() ) { // DXVAに対応しているGPUの場合には、それをデコードに利用するよう指定する。 属性.Set( SharpDX.MediaFoundation.SourceReaderAttributeKeys.D3DManager, dr.DXGIDeviceManager ); // 追加のビデオプロセッシングを有効にする。 属性.Set( SharpDX.MediaFoundation.SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true ); // 真偽値が bool だったり // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。 属性.Set( SharpDX.MediaFoundation.SinkWriterAttributeKeys.ReadwriteDisableConverters, 0 ); // int だったり // 属性を使って、SourceReaderEx を生成。 using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( 動画ファイルパス, 属性 ) ) // パスは URI 扱い { this.SourceReaderEx = sourceReader.QueryInterface(); } } } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"SourceReaderEx の作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } //----------------- #endregion #region " 最初のビデオストリームを選択し、その他のすべてのストリームを非選択にする。" //----------------- try { this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false ); this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"ストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } //----------------- #endregion #region " 部分 MediaType を作成し、SourceReaderEx に登録する。" //----------------- try { using( var mediaType = new SharpDX.MediaFoundation.MediaType() ) { // フォーマットは ARGB32 で固定とする。(SourceReaderEx を使わない場合、H264 では ARGB32 が選べないので注意。) mediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Video ); mediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.VideoFormatGuids.Argb32 ); // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。 this.SourceReaderEx.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, mediaType ); } } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"MediaType (Video, ARGB32) の設定または必要なデコーダの読み込みに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } //----------------- #endregion #region " ビデオストリームが選択されていることを再度保証する。" //----------------- try { this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"最初のビデオストリームの選択に失敗しました(MediaType 設定後)。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } //----------------- #endregion #region " 完全 MediaType と動画の情報を取得する。" //----------------- try { this.MediaType = this.SourceReaderEx.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"完全メディアタイプの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } // フレームサイズを取得する。 try { // 動画の途中でのサイズ変更には対応しない。 var packedFrameSize = this.MediaType.Get( SharpDX.MediaFoundation.MediaTypeAttributeKeys.FrameSize ); this.サイズdpx = new SharpDX.Size2F( ( packedFrameSize >> 32 ) & 0xFFFFFFFF, ( packedFrameSize ) & 0xFFFFFFFF ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"フレームサイズの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } // 動画の長さを取得する。 try { this.長さsec = this.SourceReaderEx.GetPresentationAttribute( SharpDX.MediaFoundation.SourceReaderIndex.MediaSource, SharpDX.MediaFoundation.PresentationDescriptionAttributeKeys.Duration ) / ( 1000.0 * 1000.0 * 10.0 ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"動画の長さの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } //----------------- #endregion #region " 描画先となる WicBitmap を作成する。" //----------------- try { this.WicBitmap = new SharpDX.WIC.Bitmap( dr.WicImagingFactory2, (int) this.サイズdpx.Width, (int) this.サイズdpx.Height, SharpDX.WIC.PixelFormat.Format32bppBGR, SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"描画先となる WICビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" ); throw; } //----------------- #endregion this.ストックする(); FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" ); } protected override void Onデバイス依存リソースの解放( デバイスリソース dr ) { #region " デコードタスクが起動していたら、終了する。" //---------------- this.デコードタスクを終了せよ.Set(); this.デコードタスク.Value?.Wait(); this.デコードタスク.Value = null; //---------------- #endregion FDK.Utilities.解放する( ref this.最後に表示したフレーム ); FDK.Utilities.解放する( ref this.WicBitmap ); FDK.Utilities.解放する( ref this.MediaType ); FDK.Utilities.解放する( ref this.SourceReaderEx ); this.キューをクリアする(); this.フレームキュー = null; this.デコードタスク用D2DDeviceContext参照 = null; } private void キューをクリアする() { while( 0 < this.フレームキュー.Count ) { var item = (FrameQueueItem) null; if( this.フレームキュー.TryDequeue( out item ) ) item.Dispose(); } this.キューが空いた?.Set(); } private void 次のフレームを確認する( out FrameQueueItem フレーム ) { if( ( 0 == this.フレームキュー.Count ) || ( false == this.フレームキュー.TryPeek( out フレーム ) ) ) // キューから取り出さない { フレーム = null; // キューが空だったか、Peek が一歩遅かった?(ないはずだが } } private void 次のフレームを取り出す( out FrameQueueItem フレーム ) { if( ( 0 == this.フレームキュー.Count ) || ( false == this.フレームキュー.TryDequeue( out フレーム ) ) ) // キューから取り出す { フレーム = null; // キューが空だったか、Peek が一歩遅かった?(ないはずだが } else { this.キューが空いた.Set(); } } private void D2DBitmapを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx, SharpDX.Direct2D1.Bitmap D2DBitmap ) { if( null == D2DBitmap ) return; FDK.Utilities.D2DBatchDraw( dr.D2DContext1, () => { dr.D2DContext1.Transform = 変換行列2Dpx; dr.D2DContext1.PrimitiveBlend = ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver; dr.D2DContext1.DrawBitmap( D2DBitmap, this.不透明度, SharpDX.Direct2D1.InterpolationMode.Linear ); } ); } // 以下、デコードタスク用。 private FDK.同期.RWLock 再生タイマ = new 同期.RWLock<カウンタ.QPCTimer>(); protected void デコードタスクエントリ( object obj再生開始位置sec ) { FDK.Log.Info( "デコードタスクを起動しました。" ); var 再生開始位置sec = (double) obj再生開始位置sec; if( 0.0 < 再生開始位置sec ) this.再生位置までストリームを進める( 再生開始位置sec ); const int EVID_キューが空いた = 0; const int EVID_デコードタスクを終了せよ = 1; var events = new System.Threading.WaitHandle[ 2 ]; events[ EVID_キューが空いた ] = this.キューが空いた; events[ EVID_デコードタスクを終了せよ ] = this.デコードタスクを終了せよ; this.デコードタスク起動完了.Set(); this.再生タイマ.Value.リセットする( FDK.カウンタ.QPCTimer.秒をカウントに変換して返す( 再生開始位置sec ) ); while( System.Threading.WaitHandle.WaitAny( events ) == EVID_キューが空いた ) { // キューが空いてるので、サンプルを1つデコードする。 if( this.サンプルをひとつデコードしてフレームをキューへ格納する() ) { // キューがいっぱいになったら、空くまで待つ。 if( キューのサイズ == this.フレームキュー.Count ) { this.キューが空いた.Reset(); // 次の while で空くのを待つ。 } } else { break; // エラーあるいはストリーム終了 → デコードタスクを終了する。 } } this.デコードタスク.Value = null; FDK.Log.Info( "デコードタスクを終了しました。" ); } /// 格納できたかスキップした場合は true、エラーあるいはストリーム終了なら false。 private bool サンプルをひとつデコードしてフレームをキューへ格納する() { var sample = (SharpDX.MediaFoundation.Sample) null; var bitmap = (SharpDX.Direct2D1.Bitmap) null; try { long サンプルの表示時刻100ns = 0; #region " ソースリーダーから次のサンプルをひとつデコードする。" //----------------- var ストリームフラグ = SharpDX.MediaFoundation.SourceReaderFlags.None; int 実ストリーム番号 = 0; sample = this.SourceReaderEx.ReadSample( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, SharpDX.MediaFoundation.SourceReaderControlFlags.None, out 実ストリーム番号, out ストリームフラグ, out サンプルの表示時刻100ns ); if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) ) // BOX化コストとか気にしない { #region " ストリーム終了 " //---------------- if( this.ループ再生する ) { FDK.Log.Info( "動画をループ再生します。" ); this.SourceReaderEx.SetCurrentPosition( 0 ); return this.サンプルをひとつデコードしてフレームをキューへ格納する(); } else { FDK.Log.Info( "動画の再生を終了します。" ); return false; } //---------------- #endregion } else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) ) { #region " エラー。" //---------------- throw new SharpDX.SharpDXException( SharpDX.Result.Fail ); //---------------- #endregion } //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Newstream ) ) //{ //} //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Nativemediatypechanged ) ) //{ //} //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Currentmediatypechanged ) ) //{ // 動画の途中でのサイズ変更には対応しない。 //} //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.StreamTick ) ) //{ //} //else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.AllEffectsremoved ) ) //{ //} //--------------------------------------------------- #endregion //if( サンプルの表示時刻100ns < this.再生タイマ.Value.現在のリアルタイムカウント100ns単位 ) // return true; // もう表示時刻は通り過ぎてるのでスキップする。---> この実装だとループのし始めには常に true になってしまうので却下。 this.サンプルをビットマップに転送する( sample, out bitmap ); this.フレームキュー.Enqueue( new FrameQueueItem() { D2DBitmap = bitmap, 表示時刻sec = サンプルの表示時刻100ns / ( 10.0 * 1000.0 * 1000.0 ), } ); bitmap = null; // キューに格納したので、ここでは Dispose しない。 } catch( Exception e ) { FDK.Log.Info( $"エラーが発生したので、動画の再生を終了します。[{e.Message}]" ); return false; } finally { bitmap?.Dispose(); sample?.Dispose(); } return true; } private void 再生位置までストリームを進める( double 再生位置sec ) { #region " ストリームがシーク不可なら何もしない。" //---------------- var flags = this.SourceReaderEx.GetPresentationAttribute( SharpDX.MediaFoundation.SourceReaderIndex.MediaSource, SharpDX.MediaFoundation.SourceReaderAttributeKeys.MediaSourceCharacteristics ); if( ( flags & (int) SharpDX.MediaFoundation.MediaSourceCharacteristics.CanSeek ) == 0 ) { FDK.Log.WARNING( "この動画はシークできないようです。" ); return; } //---------------- #endregion // ストリームの再生位置を移動する。 this.キューをクリアする(); long 再生位置100ns = (long) ( 再生位置sec * 1000.0 * 1000.0 * 10.0 ); this.SourceReaderEx.SetCurrentPosition( 再生位置100ns ); // キーフレームから再生位置100nsまで ReadSample する。 var ストリームフラグ = SharpDX.MediaFoundation.SourceReaderFlags.None; int 実ストリーム番号 = 0; long サンプルの表示時刻100ns = 0; while( サンプルの表示時刻100ns < 再生位置100ns ) { // サンプルを取得。 var sample = this.SourceReaderEx.ReadSample( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, SharpDX.MediaFoundation.SourceReaderControlFlags.None, out 実ストリーム番号, out ストリームフラグ, out サンプルの表示時刻100ns ); // 即解放。 sample?.Dispose(); if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) ) { // ストリーム終了。 return; } else if( ストリームフラグ.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) ) { // エラー発生。 FDK.Log.ERROR( $"動画の再生位置を移動中に、エラーが発生しました。" ); return; } } this.ストックする(); FDK.Log.Info( $"動画の再生位置を {再生位置sec}sec へ移動しました。" ); } private void ストックする() { for( int i = 0; i < this.キューのサイズ; i++ ) this.サンプルをひとつデコードしてフレームをキューへ格納する(); this.キューが空いた.Reset(); // 埋まった } private unsafe void サンプルをビットマップに転送する( SharpDX.MediaFoundation.Sample Sample, out SharpDX.Direct2D1.Bitmap D2DBitmap ) { var buffer = (SharpDX.MediaFoundation.MediaBuffer) null; var buffer2d2 = (SharpDX.MediaFoundation.Buffer2D2) null; try { #region " サンプルからサンプルバッファ (MediaBuffer) を取得する。" //----------------- try { buffer = Sample.ConvertToContiguousBuffer(); } catch( Exception e ) { Log.ERROR( $"サンプルバッファの取得に失敗しました。(0x{e.HResult:x8})" ); throw; } //----------------- #endregion #region " サンプルバッファを Buffer2D2 にキャストする。" //----------------- try { buffer2d2 = buffer.QueryInterface(); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"サンプルバッファから Buffer2D2 へのキャストに失敗しました。(0x{e.HResult:x8})" ); throw; } //----------------- #endregion #region " サンプルバッファをロックする。" //----------------- byte[] scanLine0_bp = new byte[ 8 ]; // 「生ポインタ」が格納される。32bitなら[0~3]、64bitなら[0~7]が有効。(CPUではなく.NETに依存) int pitch = 0; byte[] bufferStart_bp = new byte[ 8 ]; // 「生ポインタ」が格納される。こちらは使わない。 int bufferLength; try { buffer2d2.Lock2DSize( SharpDX.MediaFoundation.Buffer2DLockFlags.Read, scanLine0_bp, out pitch, bufferStart_bp, out bufferLength ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"サンプルバッファのロックに失敗しました。(0x{e.HResult:x8})" ); throw; } //----------------- #endregion try { #region " サンプルバッファのネイティブ先頭アドレスを取得する。" //----------------- byte* scanLine0 = null; try { scanLine0 = (byte*) this.生ポインタを格納したbyte配列からポインタを取得して返す( scanLine0_bp ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"サンプルバッファのアドレスの取得に失敗しました。(0x{e.HResult:x8})" ); throw; } //----------------- #endregion #region " サンプルから WicBitmap へ画像をコピーする。" //----------------- try { // 描画先である WICBitmap をロックする。 using( var bitmapLock = this.WicBitmap.Lock( new SharpDX.Rectangle( 0, 0, this.WicBitmap.Size.Width, this.WicBitmap.Size.Height ), SharpDX.WIC.BitmapLockFlags.Write ) ) { // サンプルバッファからWICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピーする。 int bitmapStride = bitmapLock.Stride; byte* src = scanLine0; byte* dest = (byte*) bitmapLock.Data.DataPointer.ToPointer(); for( int y = 0; y < this.サイズdpx.Height; y++ ) { // ARGB32 to G8B8R8X8 ではデータ変換が不要なので、一行を一気にコピー。 動画.CopyMemory( dest, src, (int) this.サイズdpx.Width * 4 ); // ARGB=4バイト。 src += pitch; dest += bitmapStride; // bitmapStride は byte 単位 } } } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"WicBitmap の Lock に失敗しました。(0x{e.HResult:x8})" ); throw; } catch( Exception e ) { Log.ERROR( $"サンプルバッファから WIC ビットマップへのデータの転送に失敗しました。(0x{e.HResult:x8})" ); throw; } //---------------- #endregion #region " WicBitmap から D2DBitmap を生成する。" //---------------- try { D2DBitmap = SharpDX.Direct2D1.Bitmap.FromWicBitmap( this.デコードタスク用D2DDeviceContext参照, this.WicBitmap ); } catch( SharpDX.SharpDXException e ) { Log.ERROR( $"D2Dビットマップの作成に失敗しました。(0x{e.HResult:x8})" ); throw; } //---------------- #endregion } finally { #region " サンプルバッファのロックを解除する。" //----------------- buffer2d2.Unlock2D(); //----------------- #endregion } } finally { FDK.Utilities.解放する( ref buffer2d2 ); FDK.Utilities.解放する( ref buffer ); } } private unsafe void* 生ポインタを格納したbyte配列からポインタを取得して返す( byte[] 生ポインタ ) { if( ( 4 == IntPtr.Size ) && System.BitConverter.IsLittleEndian ) { #region " (A) 32bit, リトルエンディアン " //---------------- int 生アドレス32bit = 0; for( int i = 0; i < 4; i++ ) 生アドレス32bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 ); return new IntPtr( 生アドレス32bit ).ToPointer(); //---------------- #endregion } else if( ( 8 == IntPtr.Size ) && System.BitConverter.IsLittleEndian ) { #region " (B) 64bit, リトルエンディアン " //---------------- long 生アドレス64bit = 0; for( int i = 0; i < 8; i++ ) 生アドレス64bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 ); return new IntPtr( 生アドレス64bit ).ToPointer(); //---------------- #endregion } else if( ( 4 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) ) { #region " (C) 32bit, ビッグエンディアン " //---------------- int 生アドレス32bit = 0; for( int i = 0; i < 4; i++ ) 生アドレス32bit += ( (int) 生ポインタ[ 4 - i ] ) << ( i * 8 ); return new IntPtr( 生アドレス32bit ).ToPointer(); //---------------- #endregion } else if( ( 8 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) ) { #region " (D) 64bit, ビッグエンディアン " //---------------- long 生アドレス64bit = 0; for( int i = 0; i < 8; i++ ) 生アドレス64bit += ( (int) 生ポインタ[ 8 - i ] ) << ( i * 8 ); return new IntPtr( 生アドレス64bit ).ToPointer(); //---------------- #endregion } throw new SharpDX.SharpDXException( SharpDX.Result.NotImplemented, "この .NET アーキテクチャには対応していません。" ); } #region " Win32 API " //----------------- [System.Runtime.InteropServices.DllImport( "kernel32.dll", SetLastError = true )] private static extern unsafe void CopyMemory( void* dst, void* src, int size ); //----------------- #endregion } }