OSDN Git Service

Decoder.Read() で「配列の長さが不足です」例外が発生していたミスを修正。
[strokestylet/CsWin10Desktop3.git] / FDK24 / メディア / サウンド / WASAPI / Decoder.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Linq;
5 using CSCore;
6
7 namespace FDK.メディア.サウンド.WASAPI
8 {
9         /// <summary>
10         ///             指定されたメディアファイル(動画, 音楽)をデコードして、CSCore.IWaveStream オブジェクトを生成する。
11         /// </summary>
12         internal class Decoder : CSCore.IWaveSource
13         {
14                 /// <summary>
15                 ///             シークは常にサポートする。
16                 /// </summary>
17                 public bool CanSeek => ( true );
18
19                 /// <summary>
20                 ///             デコード後のオーディオデータの長さ[byte]。
21                 /// </summary>
22                 public long Length
23                 {
24                         get { return this._EncodedWaveData.Length; }
25                 }
26
27                 /// <summary>
28                 ///             現在の位置。
29                 ///             先頭からのオフセット[byte]で表す。
30                 /// </summary>
31                 public long Position
32                 {
33                         get { return this._Position; }
34                         set
35                         {
36                                 if( ( 0 > value ) || ( this.Length <= value ) )
37                                         throw new ArgumentOutOfRangeException();
38
39                                 this._Position = value;
40                         }
41                 }
42
43                 /// <summary>
44                 ///             デコード後のオーディオデータのフォーマット。
45                 /// </summary>
46                 public CSCore.WaveFormat WaveFormat
47                 {
48                         get;
49                         protected set;
50                 }
51
52                 /// <summary>
53                 ///             メディアファイル(動画、音楽)をデコードする。
54                 /// </summary>
55                 /// <param name="path">メディアファイル(MediaFoundation でデコードできるもの)</param>
56                 /// <param name="waveFormat">デコード先のフォーマット。</param>
57                 public Decoder( string path, CSCore.WaveFormat waveFormat )
58                 {
59                         //if( ( waveFormat.WaveFormatTag != AudioEncoding.IeeeFloat ) &&
60                         //      ( !CSCore.WaveFormatExtensible.SubTypeFromWaveFormat( waveFormat ).Equals( SharpDX.MediaFoundation.AudioFormatGuids.Float ) ) )
61                         //{
62                         //      throw new NotSupportedException( "IEEE Float 以外の形式のフォーマットはサポートしません。" );
63                         //}
64                         this.WaveFormat = waveFormat;
65
66                         this._初期化する( path );
67                 }
68
69                 public void Dispose()
70                 {
71                         this._解放する();
72                 }
73
74                 /// <summary>
75                 ///             連続した要素を読み込み、this.Position を読み込んだ要素の数だけ進める。
76                 /// </summary>
77                 /// <param name="buffer">
78                 ///             読み込んだ要素を格納するための配列。
79                 ///             このメソッドから戻ると、buffer には offset ~ (offset + count - 1) の数の要素が格納されている。
80                 /// </param>
81                 /// <param name="offset">
82                 ///             buffer に格納を始める位置。
83                 /// </param>
84                 /// <param name="count">
85                 ///             読み込む最大の要素数。
86                 /// </param>
87                 /// <returns>
88                 ///             buffer に読み込んだ要素の総数。
89                 /// </returns>
90                 public int Read( byte[] buffer, int offset, int count )
91                 {
92                         // 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
93
94                         if( ( null == this._EncodedWaveData ) && ( null == buffer ) )
95                                 return 0;
96
97                         // offset は、0~buffer.Length-1 に収める。
98                         offset = Math.Max( 0, Math.Min( buffer.Length - 1, offset ) );
99
100                         // count は、_EncodeWaveData.Length-Position, buffer.Length-offset, count のうちの最小値とする。
101                         count = Math.Min( Math.Min( this._EncodedWaveData.Length - (int) this._Position, count ), buffer.Length - offset );
102
103                         if( 0 < count )
104                         {
105                                 Array.Copy(
106                                         sourceArray: this._EncodedWaveData,
107                                         sourceIndex: this._Position,
108                                         destinationArray: buffer,
109                                         destinationIndex: offset,
110                                         length: count );
111
112                                 this._Position += count;
113                         }
114
115                         return count;
116                 }
117
118                 private SharpDX.MediaFoundation.MediaType _MediaType = null;
119                 private byte[] _EncodedWaveData = null;
120                 private long _Position = 0;
121
122                 private void _初期化する( string path )
123                 {
124                         try
125                         {
126                                 using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( path ) )
127                                 using( var waveStream = new System.IO.MemoryStream() )
128                                 {
129                                         #region " 最初のオーディオストリームを選択し、その他のすべてのストリームを非選択にする。"
130                                         //----------------
131                                         sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
132                                         sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true );
133                                         //----------------
134                                         #endregion
135                                         #region " デコードフォーマットを持つ MediaType を作成し、SourceReader に登録する。"
136                                         //----------------
137                                         using( var partialMediaType = new SharpDX.MediaFoundation.MediaType() )
138                                         {
139                                                 // 部分メディアタイプを作成し、オーディオフォーマットを設定する。 (オーディオフォーマットは IEEE FLOAT で固定。)
140                                                 partialMediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Audio );
141                                                 partialMediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.AudioFormatGuids.Float );
142                                                 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioNumChannels, this.WaveFormat.Channels );
143                                                 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerSecond, this.WaveFormat.SampleRate );
144                                                 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBlockAlignment, this.WaveFormat.BlockAlign );
145                                                 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioAvgBytesPerSecond, this.WaveFormat.BytesPerSecond );
146                                                 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBitsPerSample, this.WaveFormat.BitsPerSample );
147                                                 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AllSamplesIndependent, 1 ); // TRUE
148
149                                                 if( this.WaveFormat.WaveFormatTag == AudioEncoding.Extensible )
150                                                 {
151                                                         var wfmEx = this.WaveFormat as CSCore.WaveFormatExtensible;
152                                                         partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioChannelMask, (int) wfmEx.ChannelMask );
153                                                         partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerBlock, wfmEx.SamplesPerBlock );
154                                                         partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioValidBitsPerSample, wfmEx.ValidBitsPerSample );
155                                                 }
156
157                                                 // 作成したメディアタイプを sourceReader にセットする。必要なデコーダが見つからなかったら、ここで例外が発生する。
158                                                 sourceReader.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, partialMediaType );
159
160                                                 // 完成されたメディアタイプを取得する。
161                                                 this._MediaType = sourceReader.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream );
162
163                                                 // 最初のオーディオストリームが選択されていることを保証する。
164                                                 sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true );
165                                         }
166                                         //----------------
167                                         #endregion
168                                         #region " sourceReader からサンプルを取得してデコードし、waveStream へ書き込んだのち、byte[] _EncodedWaveData へ出力する。"
169                                         //-----------------
170                                         using( var pcmWriter = new System.IO.BinaryWriter( waveStream ) )
171                                         {
172                                                 while( true )
173                                                 {
174                                                         // 次のサンプルを読み込む。
175                                                         int dwActualStreamIndexRef = 0;
176                                                         var dwStreamFlagsRef = SharpDX.MediaFoundation.SourceReaderFlags.None;
177                                                         Int64 llTimestampRef = 0;
178                                                         using( var sample = sourceReader.ReadSample(
179                                                                 SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream,
180                                                                 SharpDX.MediaFoundation.SourceReaderControlFlags.None,
181                                                                 out dwActualStreamIndexRef,
182                                                                 out dwStreamFlagsRef,
183                                                                 out llTimestampRef ) )
184                                                         {
185                                                                 if( null == sample )
186                                                                         break;      // EndOfStream やエラーのときも null になる。
187
188                                                                 // sample をロックし、オーディオデータへのポインタを取得する。
189                                                                 int cbMaxLengthRef = 0;
190                                                                 int cbCurrentLengthRef = 0;
191                                                                 using( var mediaBuffer = sample.ConvertToContiguousBuffer() )
192                                                                 {
193                                                                         // オーディオデータをメモリストリームに書き込む。
194                                                                         var audioData = mediaBuffer.Lock( out cbMaxLengthRef, out cbCurrentLengthRef );
195                                                                         try
196                                                                         {
197                                                                                 byte[] dstData = new byte[ cbCurrentLengthRef ];
198                                                                                 System.Runtime.InteropServices.Marshal.Copy( audioData, dstData, 0, cbCurrentLengthRef );
199                                                                                 pcmWriter.Write( dstData, 0, cbCurrentLengthRef );
200                                                                         }
201                                                                         finally
202                                                                         {
203                                                                                 mediaBuffer.Unlock();
204                                                                         }
205                                                                 }
206                                                         }
207                                                 }
208
209                                                 // ストリームの内容を byte 配列に出力。
210                                                 this._EncodedWaveData = waveStream.ToArray();
211                                         }
212                                         //-----------------
213                                         #endregion
214                                 }
215                         }
216                         catch
217                         {
218                                 this._EncodedWaveData = new byte[] { };
219                         }
220
221                         this._Position = 0;
222                 }
223
224                 private void _解放する()
225                 {
226                         FDK.Utilities.解放する( ref this._MediaType );
227                 }
228
229                 #region " Win32 "
230                 //----------------
231                 private const int MF_E_INVALIDMEDIATYPE = unchecked((int) 0xC00D36B4);
232                 //----------------
233                 #endregion
234         }
235 }