Как использовать преобразователь частоты кадров DMO в приложении MF - PullRequest
4 голосов
/ 07 декабря 2011

Я хочу использовать конвертер частоты кадров DSP в моем приложении Media Foundation.Я использую SourceReader для чтения видеофайла.Может кто-нибудь сказать мне, где и как интегрировать DMO с MF для получения преобразования частоты кадров.Кажется, я не понимаю, какие сэмплы (сжатые / несжатые) подавать в DMO для получения новой частоты кадров.Как DMO меняет частоту кадров?Дает ли новая отметка времени новым образцам?Там нет примеров кода, демонстрирующих его использование.Пожалуйста, помогите, я застрял.

Спасибо, Моц

1 Ответ

0 голосов
/ 02 декабря 2015

Это старый вопрос.

Чтобы преобразовать частоту кадров с помощью SourceReader, нам нужно интегрировать DMO вручную.

Идея состоит в том, чтобы получить совместимый образец из SourceReader, скажем, подтипа видео, обрабатываемого DMO.

Как DMO меняет частоту кадров?

Согласно DSP преобразователя частоты кадров:

Этот DSP изменяет частоту кадров путем повторения или отбрасывания кадров.

.

Дает ли новая отметка времени новым образцам?

DMO изменяет время выборки и продолжительность выборки. Но если продолжительность видеофайла составляет 1 минуту, в конце он останется прежним.

Например, если ваш видеофайл имеет 1800 кадров, длительность 1 минута и частота кадров 30 кадров в секунду. Вам нужно 60 кадров в секунду, поэтому у вас будет 3600 кадров, а продолжительность не изменится (всегда 1 минута).

#pragma once
#define WIN32_LEAN_AND_MEAN
#define STRICT

#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "wmcodecdspuuid")

#include <WinSDKVer.h>
#include <new>
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <mferror.h>
#include <Wmcodecdsp.h>

template <class T> inline void SAFE_RELEASE(T*& p){

    if(p){
        p->Release();
        p = NULL;
    }
}

HRESULT ProcessConverter();
HRESULT InitDMO(IMFTransform**, IMFMediaType*);
HRESULT ProcessSample(IMFSourceReader*, IMFTransform*);
HRESULT ProcessDMO(IMFTransform*, IMFSample*, DWORD&, const UINT32);
HRESULT InitOutputDataBuffer(IMFTransform*, MFT_OUTPUT_DATA_BUFFER*, const UINT32);

void main(){

    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if(SUCCEEDED(hr)){

        hr = MFStartup(MF_VERSION, MFSTARTUP_LITE);

        if(SUCCEEDED(hr)){

            hr = ProcessConverter();

            hr = MFShutdown();
        }

        CoUninitialize();
    }
}

HRESULT ProcessConverter(){

    HRESULT hr;
    IMFSourceReader* pReader = NULL;

    // Change the URL
    if(FAILED(hr = MFCreateSourceReaderFromURL(L"Wildlife.wmv", NULL, &pReader))){
        return hr;
    }

    DWORD dwMediaTypeIndex = 0;
    IMFMediaType* pType = NULL;

    hr = pReader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, dwMediaTypeIndex, &pType);

    if(SUCCEEDED(hr)){

        // We must ask for a subtype compatible with DMO :
        // ARGB32 RGB24 RGB32 RGB555 RGB565 AYUV IYUV UYVY Y211 Y411 Y41P YUY2 YUYV YV12 YVYU
        hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);

        if(SUCCEEDED(hr)){
          hr = pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pType);
        }

        // We need this because we use the MediaType to initialize the Transform
        if(SUCCEEDED(hr)){
          SAFE_RELEASE(pType);
          hr = pReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pType);
        }

        if(SUCCEEDED(hr)){

            IMFTransform* pTransform = NULL;
            hr = InitDMO(&pTransform, pType);

            if(SUCCEEDED(hr)){

                hr = ProcessSample(pReader, pTransform);

                // Seems not really needed with the DMO
                /*hr = */ pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL);
                /*hr = */ pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL);
                /*hr = */ pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, NULL);
            }

            SAFE_RELEASE(pTransform);
        }
    }

    SAFE_RELEASE(pType);
    SAFE_RELEASE(pReader);

    return hr;
}

HRESULT InitDMO(IMFTransform** ppTransform, IMFMediaType* pType){

    HRESULT hr = CoCreateInstance(CLSID_CFrameRateConvertDmo, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, reinterpret_cast<void**>(ppTransform));

    if(SUCCEEDED(hr)){
        hr = (*ppTransform)->SetInputType(0, pType, 0);
    }

    if(SUCCEEDED(hr)){
        // Change the frame rate as needed, here num = 60000 and den = 1001
        hr = MFSetAttributeRatio(pType, MF_MT_FRAME_RATE, 60000, 1001);
    }

    if(SUCCEEDED(hr)){
        hr = (*ppTransform)->SetOutputType(0, pType, 0);
    }

    if(SUCCEEDED(hr)){
        hr = (*ppTransform)->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
    }

    if(SUCCEEDED(hr)){
        hr = (*ppTransform)->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL);
    }

    return hr;
}

HRESULT ProcessSample(IMFSourceReader* pReader, IMFTransform* pTransform){

    HRESULT hr;
    IMFMediaType* pType = NULL;

    if(FAILED(hr = pTransform->GetOutputCurrentType(0, &pType))){
        return hr;
    }

    // We need the frame size to create the sample buffer.
    UINT32 uiFrameSize = 0;
    hr = pType->GetUINT32(MF_MT_SAMPLE_SIZE, &uiFrameSize);

    SAFE_RELEASE(pType);

    if(FAILED(hr) || uiFrameSize == 0){
        return hr;
    }

    BOOL bProcess = TRUE;
    DWORD streamIndex;
    DWORD flags;
    LONGLONG llTimeStamp;
    IMFSample* pSample = NULL;
    DWORD dwReaderCount = 0;
    DWORD dwDMOCount = 0;

    while(bProcess){

        hr = pReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSample);

        if(FAILED(hr) || flags != 0){
            bProcess = FALSE;
        }
        else{

            hr = ProcessDMO(pTransform, pSample, dwDMOCount, uiFrameSize);

            // You can check timestamp from the SourceReader
            //hr = pSample->GetSampleDuration(&llTimeStamp);
            //hr = pSample->GetSampleTime(&llTimeStamp);

            SAFE_RELEASE(pSample);
            dwReaderCount++;
        }
    }

    // Todo : check dwReaderCount and dwDMOCount here.
    // For example with native frame rate = 30000/1001 and dwReaderCount = 900
    // DMO frame rate = 30000/1001 -> dwReaderCount = 900
    // DMO frame rate = 60000/1001 -> dwReaderCount = 1800
    // DMO frame rate = 25/1 -> dwReaderCount = 750

    SAFE_RELEASE(pSample);

    return hr;
}

HRESULT ProcessDMO(IMFTransform* pTransform, IMFSample* pSample, DWORD& dwDMOCount, const UINT32 uiFrameSize){

    HRESULT hr = S_OK;

    MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
    DWORD processOutputStatus = 0;

    // Todo : we should avoid recreating the buffer...
    hr = InitOutputDataBuffer(pTransform, &outputDataBuffer, uiFrameSize);

    while(hr == S_OK){

        hr = pTransform->ProcessOutput(0, 1, &outputDataBuffer, &processOutputStatus);

        if(hr == MF_E_TRANSFORM_NEED_MORE_INPUT){
            break;
        }

        // You can check new timestamp from the DMO
        /*if(outputDataBuffer.pSample != NULL){

            LONGLONG llTimeStamp = 0;
            hr = outputDataBuffer.pSample->GetSampleTime(&llTimeStamp);
            hr = outputDataBuffer.pSample->GetSampleDuration(&llTimeStamp);
        }*/

        dwDMOCount++;
    }

    if(hr == MF_E_TRANSFORM_NEED_MORE_INPUT){
        hr = pTransform->ProcessInput(0, pSample, 0);
    }

    if(outputDataBuffer.pSample != NULL){
        SAFE_RELEASE(outputDataBuffer.pSample);
    }

    return hr;
}

HRESULT InitOutputDataBuffer(IMFTransform* pMFTransform, MFT_OUTPUT_DATA_BUFFER* pOutputBuffer, const UINT32 uiFrameSize){

    MFT_OUTPUT_STREAM_INFO outputStreamInfo;
    DWORD outputStreamId = 0;

    ZeroMemory(&outputStreamInfo, sizeof(outputStreamInfo));
    ZeroMemory(pOutputBuffer, sizeof(*pOutputBuffer));

    HRESULT hr = pMFTransform->GetOutputStreamInfo(outputStreamId, &outputStreamInfo);

    if(SUCCEEDED(hr)){

        if((outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) == 0 &&
            (outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) == 0){

            IMFSample* pOutputSample = NULL;
            IMFMediaBuffer* pMediaBuffer = NULL;

            hr = MFCreateSample(&pOutputSample);

            if(SUCCEEDED(hr)){
                hr = MFCreateMemoryBuffer(uiFrameSize, &pMediaBuffer);
            }

            if(SUCCEEDED(hr)){
                hr = pOutputSample->AddBuffer(pMediaBuffer);
            }

            if(SUCCEEDED(hr)){
                pOutputBuffer->pSample = pOutputSample;
                pOutputBuffer->pSample->AddRef();
            }

            SAFE_RELEASE(pMediaBuffer);
            SAFE_RELEASE(pOutputSample);
        }
    }

    return hr;
}
...