Sink Writer

音声や映像ファイルをエンコードする部品です。Sink Writerは動画のリサイズやフレームレートの変換、それに音声のリサンプリングを提供しません。

Sink Writerの作成

MFCreateSinkWriterFromURL()

HRESULT MFCreateSinkWriterFromURL(
  LPCWSTR       pwszOutputURL, // 出力ファイルのURLを含む文字列
  IMFByteStream *pByteStream,  // 
  IMFAttributes *pAttributes,  // 
  IMFSinkWriter **ppSinkWriter // 
);
MFCreateSinkWriterFromURL function (mfreadwrite.h) | Microsoft Learn

戻り値でERROR_PATH_NOT_FOUND (0x80070003) が返されるときは、pwszOutputURLのパスが存在しているか確認します。

初期化

AddStream()

出力形式をPCMとした場合、作成されるファイルのRIFFチャンクの後には、JUNKチャンクが続きます。

BeginWriting()

書き込むために、Sink Writerを初期化します。これはWriteSample()などの前に呼び出す必要があります。

HRESULT BeginWriting();
IMFSinkWriter::BeginWriting (mfreadwrite.h) | Microsoft Learn

Sink Writerに追加したStreamに適切なMedia Typeが設定されていないと、MF_E_INVALIDMEDIATYPE (0xC00D36B4) が返されます。

戻り値でMF_E_INVALIDMEDIATYPE (0xC00D36B4) が返されるときは、Sink Writerに追加するStreamに、適切なMedia Typeを設定します。

データの準備

WriteSample()

Sink WriterへMedia Sampleを送ります。

HRESULT WriteSample(
  DWORD     dwStreamIndex,
  IMFSample *pSample
);
IMFSinkWriter::WriteSample (mfreadwrite.h) | Microsoft Learn

サンプルコード

1000Hz、8bit、モノラルの3秒間の矩形波の音声は、次のように作成できます。

UINT32 sampleRate = 1000; // 1000 Hz
UINT32 bitsPerSample = 8; // 8 bit
UINT32 channels = 1;      // 1 channel
UINT32 blockAlign = channels * (bitsPerSample / 8);
UINT32 bytesPerSecond = blockAlign * sampleRate;

UINT32 dataSecond = 3; // 3秒
DWORD cbDataLength = dataSecond * bytesPerSecond;

// 書き込むデータを用意する
BYTE* pSrc = new BYTE[cbDataLength];
for (int i = 0; i < cbDataLength; i++)
{
    pSrc[i] = (i % 2) * 0xff;
}


HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
hr = MFStartup(MF_VERSION);


// Sink Writerを初期化する
LPCWSTR pwszOutputURL = L"C:\\sample.wav";
IMFSinkWriter* pSinkWriter = NULL;
hr = MFCreateSinkWriterFromURL(pwszOutputURL, NULL, NULL, &pSinkWriter);

// 出力用のMediaTypeを設定する
IMFMediaType* pMediaTypeOut = NULL;
hr = MFCreateMediaType(&pMediaTypeOut);
hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
hr = pMediaTypeOut->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels);
hr = pMediaTypeOut->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate);
hr = pMediaTypeOut->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign);
hr = pMediaTypeOut->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond);
hr = pMediaTypeOut->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
hr = pMediaTypeOut->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);

DWORD streamIndex;
hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);

// 入力用のMediaTypeを設定する
IMFMediaType* pMediaTypeIn = pMediaTypeOut; // 出力側と同一ならば、それを用いる

hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);

// Sink writerに、データの受け入れを開始するように指示する
hr = pSinkWriter->BeginWriting();


// 新しいメモリ バッファを作成する
IMFMediaBuffer* pBuffer = NULL;
hr = MFCreateMemoryBuffer(cbDataLength, &pBuffer);

// バッファをロックし、バッファへデータをコピーする
BYTE* pData = NULL;
hr = pBuffer->Lock(&pData, NULL, NULL);

memcpy_s(pData, cbDataLength, pSrc, cbDataLength);

hr = pBuffer->Unlock();

// バッファのデータ長を設定する
hr = pBuffer->SetCurrentLength(cbDataLength);

// Media Sampleを作成し、それにバッファを追加する
IMFSample* pSample = NULL;
hr = MFCreateSample(&pSample);
hr = pSample->AddBuffer(pBuffer);

// タイムスタンプを設定する
LONGLONG hnsSampleTime = 0;
hr = pSample->SetSampleTime(hnsSampleTime);

// Sink WriterへMedia Sampleを送る
hr = pSinkWriter->WriteSample(streamIndex, pSample);
hr = pSinkWriter->Finalize();

delete[] pSrc;

SafeRelease(pBuffer);
SafeRelease(pSample);

SafeRelease(&pSinkWriter);
SafeRelease(&pMediaTypeOut);
// SafeRelease(&pMediaTypeIn);


MFShutdown();
CoUninitialize();
Example Code - Tutorial: Using the Sink Writer to Encode Video - Win32 apps | Microsoft Learn

終了処理

Finalize()

Sink Writerのすべての書き込み処理を完了させられます。

HRESULT Finalize();
IMFSinkWriter::Finalize (mfreadwrite.h) | Microsoft Learn

戻り値でMF_E_NO_SAMPLE_TIMESTAMP (0xC00D36C8) が返されるときには、Sink Writerへ送るMedia Sampleに、IMFSample::SetSampleTime()でPresentation Timeを設定します。

Microsoft Learnから検索