none
Media foundationでH.264、main profile以上でエンコードするとstartの時間が1フレームずれる RRS feed

  • 質問

  • お世話になっております。


    MediaFoundationの下記サンプルコードを参考にH.264の動画ファイルをmain Profileで出力してみたのですが出力された動画ファイルを確認するとstartの時間が1フレーム分ずれてしまいます。

    具体的には作成された動画ファイルをffmpegで確認すると30fpsならばstart timeが0.03333のように1フレーム分進んだ状態になっております。

    chromeなどで開いて、シークを先頭にもってくるとシークが1フレーム分勝手に移動してしまうので困っています。


    baselineでのプロファイルでは問題なく、main, highでは起こるようです。


    https://docs.microsoft.com/ja-jp/windows/desktop/medfound/tutorial--using-the-sink-writer-to-encode-video


    サンプルコードからの変更点は下記の2点です。

    mainプロファイルの場合は設定に特別なもの必要でしょうか?


    宜しくお願い致します。

    //①
    const GUID   VIDEO_ENCODING_FORMAT = MFVideoFormat_WMV3;
       ↓↓↓
    const GUID   VIDEO_ENCODING_FORMAT = MFVideoFormat_H264;
    
    //②
    HRESULT hr = MFCreateSinkWriterFromURL(L"output.wmv", NULL, NULL, &pSinkWriter);
       ↓↓↓
    HRESULT hr = MFCreateSinkWriterFromURL(L"output.mp4", NULL, NULL, &pSinkWriter);
    
    //③
    if (SUCCEEDED(hr))
    {
    		hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
    }
      ↓↓↓
    if (SUCCEEDED(hr))
    {
    		hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
    }
    if (SUCCEEDED(hr))
    {
    		hr = pMediaTypeOut->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main);
    }

    ~追記~

    コード全体を記載いたします。

    #include <Windows.h>
    #include <mfapi.h>
    #include <mfidl.h>
    #include <Mfreadwrite.h>
    #include <mferror.h>
    #include <codecapi.h>
    
    #pragma comment(lib, "mfreadwrite")
    #pragma comment(lib, "mfplat")
    #pragma comment(lib, "mfuuid")
    
    template <class T> void SafeRelease(T **ppT)
    {
    	if (*ppT)
    	{
    		(*ppT)->Release();
    		*ppT = NULL;
    	}
    }
    
    // Format constants
    const UINT32 VIDEO_WIDTH = 640;
    const UINT32 VIDEO_HEIGHT = 480;
    const UINT32 VIDEO_FPS = 30;
    const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;
    const UINT32 VIDEO_BIT_RATE = 800000;
    const GUID   VIDEO_ENCODING_FORMAT = MFVideoFormat_H264; //MFVideoFormat_H264 //MFVideoFormat_WMV3
    const GUID   VIDEO_INPUT_FORMAT = MFVideoFormat_RGB32;
    const UINT32 VIDEO_PELS = VIDEO_WIDTH * VIDEO_HEIGHT;
    const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;
    
    // Buffer to hold the video frame data.
    DWORD videoFrameBuffer[VIDEO_PELS];
    
    HRESULT InitializeSinkWriter(IMFSinkWriter **ppWriter, DWORD *pStreamIndex)
    {
    	*ppWriter = NULL;
    	*pStreamIndex = NULL;
    
    	IMFSinkWriter   *pSinkWriter = NULL;
    	IMFMediaType    *pMediaTypeOut = NULL;
    	IMFMediaType    *pMediaTypeIn = NULL;
    	DWORD           streamIndex;
    
    	HRESULT hr = MFCreateSinkWriterFromURL(L"output.mp4", NULL, NULL, &pSinkWriter);
    
    	// Set the output media type.
    	if (SUCCEEDED(hr))
    	{
    		hr = MFCreateMediaType(&pMediaTypeOut);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeOut->SetUINT32(MF_MT_MPEG2_PROFILE, eAVEncH264VProfile_Main);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);
    	}
    
    	// Set the input media type.
    	if (SUCCEEDED(hr))
    	{
    		hr = MFCreateMediaType(&pMediaTypeIn);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
    	}
    
    	// Tell the sink writer to start accepting data.
    	if (SUCCEEDED(hr))
    	{
    		hr = pSinkWriter->BeginWriting();
    	}
    
    	// Return the pointer to the caller.
    	if (SUCCEEDED(hr))
    	{
    		*ppWriter = pSinkWriter;
    		(*ppWriter)->AddRef();
    		*pStreamIndex = streamIndex;
    	}
    
    	SafeRelease(&pSinkWriter);
    	SafeRelease(&pMediaTypeOut);
    	SafeRelease(&pMediaTypeIn);
    	return hr;
    }
    
    HRESULT WriteFrame(
    	IMFSinkWriter *pWriter,
    	DWORD streamIndex,
    	const LONGLONG& rtStart        // Time stamp.
    )
    {
    	IMFSample *pSample = NULL;
    	IMFMediaBuffer *pBuffer = NULL;
    
    	const LONG cbWidth = 4 * VIDEO_WIDTH;
    	const DWORD cbBuffer = cbWidth * VIDEO_HEIGHT;
    
    	BYTE *pData = NULL;
    
    	// Create a new memory buffer.
    	HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);
    
    	// Lock the buffer and copy the video frame to the buffer.
    	if (SUCCEEDED(hr))
    	{
    		hr = pBuffer->Lock(&pData, NULL, NULL);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = MFCopyImage(
    			pData,                      // Destination buffer.
    			cbWidth,                    // Destination stride.
    			(BYTE*)videoFrameBuffer,    // First row in source image.
    			cbWidth,                    // Source stride.
    			cbWidth,                    // Image width in bytes.
    			VIDEO_HEIGHT                // Image height in pixels.
    		);
    	}
    	if (pBuffer)
    	{
    		pBuffer->Unlock();
    	}
    
    	// Set the data length of the buffer.
    	if (SUCCEEDED(hr))
    	{
    		hr = pBuffer->SetCurrentLength(cbBuffer);
    	}
    
    	// Create a media sample and add the buffer to the sample.
    	if (SUCCEEDED(hr))
    	{
    		hr = MFCreateSample(&pSample);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pSample->AddBuffer(pBuffer);
    	}
    
    	// Set the time stamp and the duration.
    	if (SUCCEEDED(hr))
    	{
    		hr = pSample->SetSampleTime(rtStart);
    	}
    	if (SUCCEEDED(hr))
    	{
    		hr = pSample->SetSampleDuration(VIDEO_FRAME_DURATION);
    	}
    
    	// Send the sample to the Sink Writer.
    	if (SUCCEEDED(hr))
    	{
    		hr = pWriter->WriteSample(streamIndex, pSample);
    	}
    
    	SafeRelease(&pSample);
    	SafeRelease(&pBuffer);
    	return hr;
    }
    
    void main()
    {
    	// Set all pixels to green
    	for (DWORD i = 0; i < VIDEO_PELS; ++i)
    	{
    		videoFrameBuffer[i] = 0x0000FF00;
    	}
    
    	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    	if (SUCCEEDED(hr))
    	{
    		hr = MFStartup(MF_VERSION);
    		if (SUCCEEDED(hr))
    		{
    			IMFSinkWriter *pSinkWriter = NULL;
    			DWORD stream;
    
    			hr = InitializeSinkWriter(&pSinkWriter, &stream);
    			if (SUCCEEDED(hr))
    			{
    				// Send frames to the sink writer.
    				LONGLONG rtStart = 0;
    
    
    				for (DWORD i = 0; i < VIDEO_FRAME_COUNT; ++i)
    				{
    					
    					hr = WriteFrame(pSinkWriter, stream, rtStart);
    					if (FAILED(hr))
    					{
    						break;
    					}
    					rtStart += VIDEO_FRAME_DURATION;
    				}
    			}
    			if (SUCCEEDED(hr))
    			{
    				hr = pSinkWriter->Finalize();
    			}
    			SafeRelease(&pSinkWriter);
    			MFShutdown();
    		}
    		CoUninitialize();
    	}
    }



    2018年12月13日 2:47