1
0
mirror of synced 2024-12-21 03:46:04 +01:00
OpenTaiko/FDK19/コード/05.DirectShow/CDStoWAVFileImage.cs

199 lines
6.9 KiB
C#
Raw Normal View History

2021-09-21 00:16:38 +02:00
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
}
}