using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using CSCore;
namespace FDK.メディア.サウンド.WASAPI
{
///
/// 指定されたメディアファイル(動画, 音楽)をデコードして、CSCore.IWaveStream オブジェクトを生成する。
///
internal class Decoder : CSCore.IWaveSource
{
///
/// シークは常にサポートする。
///
public bool CanSeek => ( true );
///
/// デコード後のオーディオデータの長さ[byte]。
///
public long Length
{
get { return this._EncodedWaveData.Length; }
}
///
/// 現在の位置。
/// 先頭からのオフセット[byte]で表す。
///
public long Position
{
get { return this._Position; }
set
{
if( ( 0 > value ) || ( this.Length <= value ) )
throw new ArgumentOutOfRangeException();
this._Position = value;
}
}
///
/// デコード後のオーディオデータのフォーマット。
///
public CSCore.WaveFormat WaveFormat
{
get;
protected set;
}
///
/// メディアファイル(動画、音楽)をデコードする。
///
/// メディアファイル(MediaFoundation でデコードできるもの)
/// デコード先のフォーマット。
public Decoder( string path, CSCore.WaveFormat waveFormat )
{
//if( ( waveFormat.WaveFormatTag != AudioEncoding.IeeeFloat ) &&
// ( !CSCore.WaveFormatExtensible.SubTypeFromWaveFormat( waveFormat ).Equals( SharpDX.MediaFoundation.AudioFormatGuids.Float ) ) )
//{
// throw new NotSupportedException( "IEEE Float 以外の形式のフォーマットはサポートしません。" );
//}
this.WaveFormat = waveFormat;
this._初期化する( path );
}
public void Dispose()
{
this._解放する();
}
///
/// 連続した要素を読み込み、this.Position を読み込んだ要素の数だけ進める。
///
///
/// 読み込んだ要素を格納するための配列。
/// このメソッドから戻ると、buffer には offset ~ (offset + count - 1) の数の要素が格納されている。
///
///
/// buffer に格納を始める位置。
///
///
/// 読み込む最大の要素数。
///
///
/// buffer に読み込んだ要素の総数。
///
public int Read( byte[] buffer, int offset, int count )
{
// 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
if( ( null == this._EncodedWaveData ) && ( null == buffer ) )
return 0;
// offset は、0~buffer.Length-1 に収める。
offset = Math.Max( 0, Math.Min( buffer.Length - 1, offset ) );
// count は、_EncodeWaveData.Length-Position, buffer.Length-offset, count のうちの最小値とする。
count = Math.Min( Math.Min( this._EncodedWaveData.Length - (int) this._Position, count ), buffer.Length - offset );
if( 0 < count )
{
Array.Copy(
sourceArray: this._EncodedWaveData,
sourceIndex: this._Position,
destinationArray: buffer,
destinationIndex: offset,
length: count );
this._Position += count;
}
return count;
}
private SharpDX.MediaFoundation.MediaType _MediaType = null;
private byte[] _EncodedWaveData = null;
private long _Position = 0;
private void _初期化する( string path )
{
try
{
using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( path ) )
using( var waveStream = new System.IO.MemoryStream() )
{
#region " 最初のオーディオストリームを選択し、その他のすべてのストリームを非選択にする。"
//----------------
sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true );
//----------------
#endregion
#region " デコードフォーマットを持つ MediaType を作成し、SourceReader に登録する。"
//----------------
using( var partialMediaType = new SharpDX.MediaFoundation.MediaType() )
{
// 部分メディアタイプを作成し、オーディオフォーマットを設定する。 (オーディオフォーマットは IEEE FLOAT で固定。)
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Audio );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.AudioFormatGuids.Float );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioNumChannels, this.WaveFormat.Channels );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerSecond, this.WaveFormat.SampleRate );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBlockAlignment, this.WaveFormat.BlockAlign );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioAvgBytesPerSecond, this.WaveFormat.BytesPerSecond );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBitsPerSample, this.WaveFormat.BitsPerSample );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AllSamplesIndependent, 1 ); // TRUE
if( this.WaveFormat.WaveFormatTag == AudioEncoding.Extensible )
{
var wfmEx = this.WaveFormat as CSCore.WaveFormatExtensible;
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioChannelMask, (int) wfmEx.ChannelMask );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerBlock, wfmEx.SamplesPerBlock );
partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioValidBitsPerSample, wfmEx.ValidBitsPerSample );
}
// 作成したメディアタイプを sourceReader にセットする。必要なデコーダが見つからなかったら、ここで例外が発生する。
sourceReader.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, partialMediaType );
// 完成されたメディアタイプを取得する。
this._MediaType = sourceReader.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream );
// 最初のオーディオストリームが選択されていることを保証する。
sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true );
}
//----------------
#endregion
#region " sourceReader からサンプルを取得してデコードし、waveStream へ書き込んだのち、byte[] _EncodedWaveData へ出力する。"
//-----------------
using( var pcmWriter = new System.IO.BinaryWriter( waveStream ) )
{
while( true )
{
// 次のサンプルを読み込む。
int dwActualStreamIndexRef = 0;
var dwStreamFlagsRef = SharpDX.MediaFoundation.SourceReaderFlags.None;
Int64 llTimestampRef = 0;
using( var sample = sourceReader.ReadSample(
SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream,
SharpDX.MediaFoundation.SourceReaderControlFlags.None,
out dwActualStreamIndexRef,
out dwStreamFlagsRef,
out llTimestampRef ) )
{
if( null == sample )
break; // EndOfStream やエラーのときも null になる。
// sample をロックし、オーディオデータへのポインタを取得する。
int cbMaxLengthRef = 0;
int cbCurrentLengthRef = 0;
using( var mediaBuffer = sample.ConvertToContiguousBuffer() )
{
// オーディオデータをメモリストリームに書き込む。
var audioData = mediaBuffer.Lock( out cbMaxLengthRef, out cbCurrentLengthRef );
try
{
byte[] dstData = new byte[ cbCurrentLengthRef ];
System.Runtime.InteropServices.Marshal.Copy( audioData, dstData, 0, cbCurrentLengthRef );
pcmWriter.Write( dstData, 0, cbCurrentLengthRef );
}
finally
{
mediaBuffer.Unlock();
}
}
}
}
// ストリームの内容を byte 配列に出力。
this._EncodedWaveData = waveStream.ToArray();
}
//-----------------
#endregion
}
}
catch
{
this._EncodedWaveData = new byte[] { };
}
this._Position = 0;
}
private void _解放する()
{
FDK.Utilities.解放する( ref this._MediaType );
}
#region " Win32 "
//----------------
private const int MF_E_INVALIDMEDIATYPE = unchecked((int) 0xC00D36B4);
//----------------
#endregion
}
}