그것은 오래된 질문이다.
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;
}