199 lines
6.9 KiB
C#
199 lines
6.9 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text;
|
||
using System.IO;
|
||
using System.Runtime.InteropServices;
|
||
using System.Diagnostics;
|
||
using DirectShowLib;
|
||
using SharpDX.Multimedia;
|
||
|
||
namespace FDK
|
||
{
|
||
public class CDStoWAVFileImage
|
||
{
|
||
/// <summary>
|
||
/// <para>指定された動画ファイルから音声のみをエンコードし、WAVファイルイメージを作成して返す。</para>
|
||
/// </summary>
|
||
public static void t変換( string fileName, out byte[] wavFileImage )
|
||
{
|
||
int hr = 0;
|
||
|
||
IGraphBuilder graphBuilder = null;
|
||
|
||
try
|
||
{
|
||
graphBuilder = (IGraphBuilder) new FilterGraph();
|
||
|
||
#region [ オーディオ用サンプルグラバの作成と追加。]
|
||
//-----------------
|
||
ISampleGrabber sampleGrabber = null;
|
||
try
|
||
{
|
||
sampleGrabber = (ISampleGrabber) new SampleGrabber();
|
||
|
||
|
||
// サンプルグラバのメディアタイプの設定。
|
||
|
||
var mediaType = new AMMediaType() {
|
||
majorType = MediaType.Audio,
|
||
subType = MediaSubType.PCM,
|
||
formatType = FormatType.WaveEx,
|
||
};
|
||
try
|
||
{
|
||
hr = sampleGrabber.SetMediaType( mediaType );
|
||
DsError.ThrowExceptionForHR( hr );
|
||
}
|
||
finally
|
||
{
|
||
if( mediaType != null )
|
||
DsUtils.FreeAMMediaType( mediaType );
|
||
}
|
||
|
||
|
||
// サンプルグラバのバッファリングを有効にする。
|
||
|
||
hr = sampleGrabber.SetBufferSamples( true );
|
||
DsError.ThrowExceptionForHR( hr );
|
||
|
||
|
||
// サンプルグラバにコールバックを追加する。
|
||
|
||
sampleGrabberProc = new CSampleGrabberCallBack();
|
||
hr = sampleGrabber.SetCallback( sampleGrabberProc, 1 ); // 1:コールバックの BufferCB() メソッドの方を呼び出す。
|
||
|
||
|
||
// サンプルグラバをグラフに追加する。
|
||
|
||
hr = graphBuilder.AddFilter( (IBaseFilter) sampleGrabber, "SampleGrabber for Audio/PCM" );
|
||
DsError.ThrowExceptionForHR( hr );
|
||
}
|
||
finally
|
||
{
|
||
C共通.tCOMオブジェクトを解放する( ref sampleGrabber );
|
||
}
|
||
//-----------------
|
||
#endregion
|
||
|
||
var e = new DirectShowLib.DsROTEntry( graphBuilder );
|
||
|
||
// fileName からグラフを自動生成。
|
||
|
||
hr = graphBuilder.RenderFile( fileName, null ); // IMediaControl.RenderFile() は推奨されない
|
||
DsError.ThrowExceptionForHR( hr );
|
||
|
||
|
||
// ビデオレンダラを除去。
|
||
|
||
CDirectShow.tビデオレンダラをグラフから除去する( graphBuilder ); // オーディオレンダラをNullに変えるより前に実行すること。(CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する() の中で一度再生するので、そのときにActiveウィンドウが表示されてしまうため。)
|
||
|
||
|
||
// オーディオレンダラを NullRenderer に置換。
|
||
|
||
WaveFormat wfx;
|
||
byte[] wfx拡張領域;
|
||
CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する( graphBuilder, out wfx, out wfx拡張領域 );
|
||
|
||
|
||
// 基準クロックを NULL(最高速)に設定する。
|
||
|
||
IMediaFilter mediaFilter = graphBuilder as IMediaFilter;
|
||
mediaFilter.SetSyncSource( null );
|
||
mediaFilter = null;
|
||
|
||
|
||
// メモリストリームにデコードデータを出力する。
|
||
|
||
sampleGrabberProc.MemoryStream = new MemoryStream(); // CDirectShow.tオーディオレンダラをNullレンダラに変えてフォーマットを取得する() で一度再生しているので、ストリームをクリアする。
|
||
var ms = sampleGrabberProc.MemoryStream;
|
||
var bw = new BinaryWriter( ms );
|
||
bw.Write( new byte[] { 0x52, 0x49, 0x46, 0x46 } ); // 'RIFF'
|
||
bw.Write( (UInt32) 0 ); // ファイルサイズ - 8 [byte];今は不明なので後で上書きする。
|
||
bw.Write( new byte[] { 0x57, 0x41, 0x56, 0x45 } ); // 'WAVE'
|
||
bw.Write( new byte[] { 0x66, 0x6D, 0x74, 0x20 } ); // 'fmt '
|
||
bw.Write( (UInt32) ( 16 + ( ( wfx拡張領域.Length > 0 ) ? ( 2/*sizeof(WAVEFORMATEX.cbSize)*/ + wfx拡張領域.Length ) : 0 ) ) ); // fmtチャンクのサイズ[byte]
|
||
bw.Write( (UInt16) wfx.Encoding); // フォーマットID(リニアPCMなら1)
|
||
bw.Write( (UInt16) wfx.Channels ); // チャンネル数
|
||
bw.Write( (UInt32) wfx.SampleRate); // サンプリングレート
|
||
bw.Write( (UInt32) wfx.AverageBytesPerSecond ); // データ速度
|
||
bw.Write( (UInt16) wfx.BlockAlign); // ブロックサイズ
|
||
bw.Write( (UInt16) wfx.BitsPerSample ); // サンプルあたりのビット数
|
||
if( wfx拡張領域.Length > 0 )
|
||
{
|
||
bw.Write( (UInt16) wfx拡張領域.Length ); // 拡張領域のサイズ[byte]
|
||
bw.Write( wfx拡張領域 ); // 拡張データ
|
||
}
|
||
bw.Write( new byte[] { 0x64, 0x61, 0x74, 0x61 } ); // 'data'
|
||
int nDATAチャンクサイズ位置 = (int) ms.Position;
|
||
bw.Write( (UInt32) 0 ); // dataチャンクのサイズ[byte];今は不明なので後で上書きする。
|
||
|
||
#region [ 再生を開始し、終了を待つ。- 再生中、sampleGrabberProc.MemoryStream に PCM データが蓄積されていく。]
|
||
//-----------------
|
||
IMediaControl mediaControl = graphBuilder as IMediaControl;
|
||
mediaControl.Run(); // 再生開始
|
||
|
||
IMediaEvent mediaEvent = graphBuilder as IMediaEvent;
|
||
EventCode eventCode;
|
||
hr = mediaEvent.WaitForCompletion( -1, out eventCode );
|
||
DsError.ThrowExceptionForHR( hr );
|
||
if( eventCode != EventCode.Complete )
|
||
throw new Exception( "再生待ちに失敗しました。" );
|
||
|
||
mediaControl.Stop();
|
||
mediaEvent = null;
|
||
mediaControl = null;
|
||
//-----------------
|
||
#endregion
|
||
|
||
bw.Seek( 4, SeekOrigin.Begin );
|
||
bw.Write( (UInt32) ms.Length - 8 ); // ファイルサイズ - 8 [byte]
|
||
|
||
bw.Seek( nDATAチャンクサイズ位置, SeekOrigin.Begin );
|
||
bw.Write( (UInt32) ms.Length - ( nDATAチャンクサイズ位置 + 4 ) ); // dataチャンクサイズ [byte]
|
||
|
||
|
||
// 出力その2を作成。
|
||
|
||
wavFileImage = ms.ToArray();
|
||
|
||
|
||
// 終了処理。
|
||
|
||
bw.Close();
|
||
sampleGrabberProc.Dispose(); // ms.Close()
|
||
}
|
||
finally
|
||
{
|
||
C共通.tCOMオブジェクトを解放する( ref graphBuilder );
|
||
}
|
||
}
|
||
|
||
#region [ private ]
|
||
//-----------------
|
||
private class CSampleGrabberCallBack : ISampleGrabberCB, IDisposable
|
||
{
|
||
public MemoryStream MemoryStream = new MemoryStream();
|
||
|
||
public int BufferCB( double SampleTime, IntPtr pBuffer, int BufferLen )
|
||
{
|
||
var bytes = new byte[ BufferLen ];
|
||
Marshal.Copy( pBuffer, bytes, 0, BufferLen ); // unmanage → manage
|
||
this.MemoryStream.Write( bytes, 0, BufferLen ); // byte[] → Stream
|
||
return CWin32.S_OK;
|
||
}
|
||
public int SampleCB( double SampleTime, IMediaSample pSample )
|
||
{
|
||
throw new NotImplementedException( "実装されていません。" );
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
this.MemoryStream.Close();
|
||
}
|
||
}
|
||
private static CSampleGrabberCallBack sampleGrabberProc = null;
|
||
//-----------------
|
||
#endregion
|
||
}
|
||
}
|