MediaFoundation --- 動画書き出し

下記記事にてMediaFoundationで動画を読み込む方法を調べました。
MediaFoundation --- 動画の読み込み - 何でもプログラミング

今回は書き出しについて調べたいと思います。

尚、見通し優先のため、エラー処理は省いてあります。

アプリケーションコード

動画はOpenCVのサンプル、vtest.aviを利用します。

動画を読み込んでH.264で出力します。

動画の読み込みにはOpenCVを利用しています。

ConfigureVideoEncoder、Writeは後述いたします。

#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "mfreadwrite.lib")

#include <opencv2\opencv.hpp>
#pragma comment(lib, "opencv_world320d.lib")

int main()
{
    CoInitialize(NULL);
    MFStartup(MF_VERSION);

    // 動画読み込み
    cv::VideoCapture capture("c:\\lib\\opencv3.2\\sources\\samples\\data\\vtest.avi");
    int width  = (int)capture.get(CV_CAP_PROP_FRAME_WIDTH);
    int height = (int)capture.get(CV_CAP_PROP_FRAME_HEIGHT);
    double fps = capture.get(CV_CAP_PROP_FPS);

    // Writer準備
    IMFSinkWriter* writer;
    MFCreateSinkWriterFromURL(L"output.mp4", NULL, NULL, &writer);

    int streamIndex = ConfigureVideoEncoder(writer, MFVideoFormat_RGB24, MFVideoFormat_H264, width, height, fps, width * height);

    // 書き出し
    writer->BeginWriting();
    while (true)
    {
        cv::Mat frame;
        capture >> frame;
        if (frame.empty())
            break;

        cv::flip(frame, frame, 0); // H264で出力する際、上下反転が必要
        Write(writer, streamIndex, frame.data, frame.elemSize() * frame.total());
    }
    writer->Finalize();

    // 後処理
    writer->Release();
    MFShutdown();
    CoUninitialize();
    return 0;
}

f:id:any-programming:20170309225151p:plain

ConfigureVideoEncoder

入力フォーマット、エンコーダ、画像サイズ、fps、平均bitrate、以上を渡してエンコーダをセットアップします。

DWORD ConfigureVideoEncoder(IMFSinkWriter* writer, GUID inputFormat, GUID outputFormat, int width, int height, double fps, int bitrate)
{
    auto createType = [&](GUID subtype)
    {
        IMFMediaType* type;
        MFCreateMediaType(&type);
        type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
        type->SetGUID(MF_MT_SUBTYPE, subtype);
        type->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
        MFSetAttributeSize(type, MF_MT_FRAME_SIZE, width, height);
        MFSetAttributeRatio(type, MF_MT_FRAME_RATE, (int)(fps * 10000000), 10000000);
        return type;
    };

    // OutputType
    IMFMediaType* outputType = createType(outputFormat);
    DWORD streamIndex;
    outputType->SetUINT32(MF_MT_AVG_BITRATE, bitrate);
    writer->AddStream(outputType, &streamIndex);
    outputType->Release();

    // InputType
    IMFMediaType* inputType = createType(inputFormat);
    writer->SetInputMediaType(streamIndex, inputType, NULL);
    inputType->Release();

    return streamIndex;
}


Write

WMVで出力したい場合は、IMFSampleのSetSampleTimeとSetSampleDurationを設定する必要があります。

void Write(IMFSinkWriter* writer, int streamIndex, BYTE* data, int size)
{
    // buffer作成
    IMFMediaBuffer* buffer;
    MFCreateMemoryBuffer(size, &buffer);

    BYTE* bufferPtr;
    buffer->Lock(&bufferPtr, NULL, NULL);
    memcpy(bufferPtr, data, size);
    buffer->Unlock();
    buffer->SetCurrentLength(size);

    // Sample作成
    IMFSample* sample;
    MFCreateSample(&sample);
    sample->AddBuffer(buffer);

    // 書き出し
    writer->WriteSample(streamIndex, sample);

    sample->Release();
    buffer->Release();
}