--- trunk/xvidcore/dshow/src/CXvidDecoder.cpp 2010/10/16 12:20:30 1896 +++ trunk/xvidcore/dshow/src/CXvidDecoder.cpp 2010/10/17 18:31:52 1898 @@ -20,7 +20,7 @@ * along with this program ; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: CXvidDecoder.cpp,v 1.19 2010-10-16 12:20:30 Isibaar Exp $ + * $Id: CXvidDecoder.cpp,v 1.20 2010-10-17 18:31:46 Isibaar Exp $ * ****************************************************************************/ @@ -44,6 +44,7 @@ C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\Debug */ +//#define XVID_USE_MFT //#define XVID_USE_TRAYICON #include @@ -56,6 +57,14 @@ #endif #include // VIDEOINFOHEADER2 +#if defined(XVID_USE_MFT) +#define MFT_UNIQUE_METHOD_NAMES +#include +#include +#include +#include +#endif + #include #include // Xvid API @@ -192,12 +201,49 @@ STDAPI DllRegisterServer() { +#if defined(XVID_USE_MFT) + int inputs_num = sizeof(sudInputPinTypes) / sizeof(AMOVIESETUP_MEDIATYPE); + int outputs_num = sizeof(sudOutputPinTypes) / sizeof(AMOVIESETUP_MEDIATYPE); + MFT_REGISTER_TYPE_INFO * mft_bs = new MFT_REGISTER_TYPE_INFO[inputs_num]; + MFT_REGISTER_TYPE_INFO * mft_csp = new MFT_REGISTER_TYPE_INFO[outputs_num]; + + { + int i; + for(i=0;i(XVID_NAME_L), // Friendly name + 0, // Flags + inputs_num, // Number of input types + mft_bs, // Input types + outputs_num, // Number of output types + mft_csp, // Output types + NULL // Attributes (optional) + ); + + delete[] mft_bs; + delete[] mft_csp; +#endif /* XVID_USE_MFT */ + return AMovieDllRegisterServer2( TRUE ); } STDAPI DllUnregisterServer() { +#if defined(XVID_USE_MFT) + MFTUnregister(CLSID_XVID); +#endif return AMovieDllRegisterServer2( FALSE ); } @@ -231,6 +277,13 @@ return GetInterface((ISpecifyPropertyPages *) this, ppv); } +#if defined(XVID_USE_MFT) + if (riid == IID_IMFTransform) + { + return GetInterface((IMFTransform *) this, ppv); + } +#endif + return CVideoTransformFilter::NonDelegatingQueryInterface(riid, ppv); } @@ -252,6 +305,17 @@ MSG_hwnd = NULL; #endif +#if defined(XVID_USE_MFT) + InitializeCriticalSection(&m_mft_lock); + m_pInputType = NULL; + m_pOutputType = NULL; + m_rtFrame = 0; + m_duration = 0; + m_discont = 0; + m_frameRate.Denominator = 1; + m_frameRate.Numerator = 1; +#endif + LoadRegistryInfo(); *phr = OpenLib(); @@ -407,6 +471,10 @@ MSG_hwnd = NULL; } #endif + +#if defined(XVID_USE_MFT) + DeleteCriticalSection(&m_mft_lock); +#endif } @@ -488,28 +556,27 @@ if (stats.type == XVID_TYPE_VOL) { hdr->biWidth = stats.data.vol.width; hdr->biHeight = stats.data.vol.height; - } - } + } + } if (ret == XVID_ERR_MEMORY) return E_FAIL; - } + } } - else - { + else + { DPRINTF("Error: Unknown FormatType"); CloseLib(); return VFW_E_TYPE_NOT_ACCEPTED; - } - - if (hdr->biHeight < 0) - { - DPRINTF("colorspace: inverted input format not supported"); - } - - m_create.width = hdr->biWidth; - m_create.height = hdr->biHeight; - - switch(hdr->biCompression) - { + } + if (hdr->biHeight < 0) + { + DPRINTF("colorspace: inverted input format not supported"); + } + + m_create.width = hdr->biWidth; + m_create.height = hdr->biHeight; + + switch(hdr->biCompression) + { case FOURCC_mp4v: case FOURCC_MP4V: if (!(g_config.supported_4cc & SUPPORT_MP4V)) { @@ -696,14 +763,14 @@ { VIDEOINFOHEADER * vih = (VIDEOINFOHEADER * )format; biWidth = vih->bmiHeader.biWidth; - m_frame.output.stride[0] = CALC_BI_STRIDE(vih->bmiHeader.biWidth, vih->bmiHeader.biBitCount); + out_stride = CALC_BI_STRIDE(vih->bmiHeader.biWidth, vih->bmiHeader.biBitCount); rgb_flip = (vih->bmiHeader.biHeight < 0 ? 0 : XVID_CSP_VFLIP); } else if (formattype == FORMAT_VideoInfo2) { VIDEOINFOHEADER2 * vih2 = (VIDEOINFOHEADER2 * )format; biWidth = vih2->bmiHeader.biWidth; - m_frame.output.stride[0] = CALC_BI_STRIDE(vih2->bmiHeader.biWidth, vih2->bmiHeader.biBitCount); + out_stride = CALC_BI_STRIDE(vih2->bmiHeader.biWidth, vih2->bmiHeader.biBitCount); rgb_flip = (vih2->bmiHeader.biHeight < 0 ? 0 : XVID_CSP_VFLIP); } else @@ -716,14 +783,14 @@ DPRINTF("IYUV"); rgb_flip = 0; m_frame.output.csp = XVID_CSP_I420; - m_frame.output.stride[0] = CALC_BI_STRIDE(biWidth, 8); /* planar format fix */ + out_stride = CALC_BI_STRIDE(biWidth, 8); /* planar format fix */ } else if (subtype == MEDIASUBTYPE_YV12) { DPRINTF("YV12"); rgb_flip = 0; m_frame.output.csp = XVID_CSP_YV12; - m_frame.output.stride[0] = CALC_BI_STRIDE(biWidth, 8); /* planar format fix */ + out_stride = CALC_BI_STRIDE(biWidth, 8); /* planar format fix */ } else if (subtype == MEDIASUBTYPE_YUY2) { @@ -979,13 +1046,13 @@ m_frame.output.csp &= ~XVID_CSP_VFLIP; m_frame.output.csp |= rgb_flip^(g_config.nFlipVideo ? XVID_CSP_VFLIP : 0); + m_frame.output.stride[0] = out_stride; // Paranoid check. if (xvid_decore_func == NULL) return E_FAIL; - repeat : if (pIn->IsPreroll() != S_OK) @@ -1120,3 +1187,1053 @@ CoTaskMemFree(pPages->pElems); return S_OK; } + +/*=============================================================================== +// MFT Interface +//=============================================================================*/ +#if defined(XVID_USE_MFT) +#include // _I64_MAX +#define INVALID_TIME _I64_MAX + +HRESULT CXvidDecoder::MFTGetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum) +{ + DPRINTF("(MFT)GetStreamLimits"); + + if ((pdwInputMinimum == NULL) || (pdwInputMaximum == NULL) || (pdwOutputMinimum == NULL) || (pdwOutputMaximum == NULL)) + return E_POINTER; + + /* Just a fixed number of streams allowed */ + *pdwInputMinimum = *pdwInputMaximum = 1; + *pdwOutputMinimum = *pdwOutputMaximum = 1; + + return S_OK; +} + +HRESULT CXvidDecoder::MFTGetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams) +{ + DPRINTF("(MFT)GetStreamCount"); + + if ((pcInputStreams == NULL) || (pcOutputStreams == NULL)) + return E_POINTER; + + /* We have a fixed number of streams */ + *pcInputStreams = 1; + *pcOutputStreams = 1; + + return S_OK; +} + +HRESULT CXvidDecoder::MFTGetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs) +{ + DPRINTF("(MFT)GetStreamIDs"); + return E_NOTIMPL; /* We have fixed number of streams, so stream ID match stream index */ +} + +HRESULT CXvidDecoder::MFTGetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo) +{ + DPRINTF("(MFT)GetInputStreamInfo"); + + if (pStreamInfo == NULL) + return E_POINTER; + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&m_mft_lock); + + pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; + pStreamInfo->hnsMaxLatency = 0; + + pStreamInfo->cbSize = 1; /* Need atleast 1 byte input */ + pStreamInfo->cbMaxLookahead = 0; + pStreamInfo->cbAlignment = 1; + + LeaveCriticalSection(&m_mft_lock); + return S_OK; +} + +HRESULT CXvidDecoder::MFTGetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo) +{ + DPRINTF("(MFT)GetOutputStreamInfo"); + + if (pStreamInfo == NULL) + return E_POINTER; + + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&m_mft_lock); + + pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE | MFT_OUTPUT_STREAM_DISCARDABLE; + + if (m_pOutputType == NULL) { + pStreamInfo->cbSize = 0; + pStreamInfo->cbAlignment = 0; + } + else { + pStreamInfo->cbSize = m_create.width * abs(m_create.height) * 4; // XXX + pStreamInfo->cbAlignment = 1; + } + + LeaveCriticalSection(&m_mft_lock); + return S_OK; +} + +HRESULT CXvidDecoder::GetAttributes(IMFAttributes** pAttributes) +{ + DPRINTF("(MFT)GetAttributes"); + return E_NOTIMPL; /* We don't support any attributes */ +} + +HRESULT CXvidDecoder::GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **ppAttributes) +{ + DPRINTF("(MFT)GetInputStreamAttributes"); + return E_NOTIMPL; /* We don't support any attributes */ +} + +HRESULT CXvidDecoder::GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **ppAttributes) +{ + DPRINTF("(MFT)GetOutputStreamAttributes"); + return E_NOTIMPL; /* We don't support any attributes */ +} + +HRESULT CXvidDecoder::MFTDeleteInputStream(DWORD dwStreamID) +{ + DPRINTF("(MFT)DeleteInputStream"); + return E_NOTIMPL; /* We have a fixed number of streams */ +} + +HRESULT CXvidDecoder::MFTAddInputStreams(DWORD cStreams, DWORD *adwStreamIDs) +{ + DPRINTF("(MFT)AddInputStreams"); + return E_NOTIMPL; /* We have a fixed number of streams */ +} + +HRESULT CXvidDecoder::MFTGetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) +{ + DPRINTF("(MFT)GetInputAvailableType"); + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + DWORD i = 0; + GUID *bs_guid_table[8]; + + bs_guid_table[i++] = (GUID *)&CLSID_XVID; + bs_guid_table[i++] = (GUID *)&CLSID_XVID_UC; + + if (g_config.supported_4cc & SUPPORT_DX50) { + bs_guid_table[i++] = (GUID *)&CLSID_DX50; + bs_guid_table[i++] = (GUID *)&CLSID_DX50_UC; + } + if (g_config.supported_4cc & SUPPORT_DIVX) { + bs_guid_table[i++] = (GUID *)&CLSID_DIVX; + bs_guid_table[i++] = (GUID *)&CLSID_DIVX_UC; + } + if (g_config.supported_4cc & SUPPORT_MP4V) { + bs_guid_table[i++] = (GUID *)&CLSID_MP4V; + bs_guid_table[i++] = (GUID *)&CLSID_MP4V_UC; + } + + const GUID *subtype; + if (dwTypeIndex < i) { + subtype = bs_guid_table[dwTypeIndex]; + } + else { + return MF_E_NO_MORE_TYPES; + } + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + + if (ppType) { + IMFMediaType *pInputType = NULL; + hr = MFCreateMediaType(&pInputType); + + if (SUCCEEDED(hr)) + hr = pInputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + + if (SUCCEEDED(hr)) + hr = pInputType->SetGUID(MF_MT_SUBTYPE, *subtype); + + if (SUCCEEDED(hr)) { + *ppType = pInputType; + (*ppType)->AddRef(); + } + if (pInputType) pInputType->Release(); + } + + LeaveCriticalSection(&m_mft_lock); + + return hr; +} + +HRESULT CXvidDecoder::MFTGetOutputAvailableType(DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) +{ + DPRINTF("(MFT)GetOutputAvailableType"); + + if (ppType == NULL) + return E_INVALIDARG; + + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwTypeIndex < 0) return E_INVALIDARG; + + GUID csp; + int bitdepth = 8; + switch(dwTypeIndex) + { + case 0: +if ( USE_YUY2 ) +{ + csp = MFVideoFormat_YUY2; + bitdepth = 4; + break; +} + case 1 : +if ( USE_UYVY ) +{ + csp = MFVideoFormat_UYVY; + bitdepth = 4; + break; +} + case 2 : + if ( USE_IYUV ) +{ + csp = MFVideoFormat_IYUV; + bitdepth = 3; + break; +} + case 3 : +if ( USE_YV12 ) +{ + csp = MFVideoFormat_YV12; + bitdepth = 3; + break; +} + case 4 : +if ( USE_RGB32 ) +{ + csp = MFVideoFormat_RGB32; + bitdepth = 8; + break; +} + case 5 : +if ( USE_RGB24 ) +{ + csp = MFVideoFormat_RGB24; + bitdepth = 6; + break; +} + case 6 : +if ( USE_RG555 ) +{ + csp = MFVideoFormat_RGB555; + bitdepth = 4; + break; +} + case 7 : +if ( USE_RG565 ) +{ + csp = MFVideoFormat_RGB565; + bitdepth = 4; + break; +} + default : + return MF_E_NO_MORE_TYPES; + } + + if (m_pInputType == NULL) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + + IMFMediaType *pOutputType = NULL; + hr = MFCreateMediaType(&pOutputType); + + if (SUCCEEDED(hr)) { + hr = pOutputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + } + + if (SUCCEEDED(hr)) { + hr = pOutputType->SetGUID(MF_MT_SUBTYPE, csp); + } + + if (SUCCEEDED(hr)) { + hr = pOutputType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE); + } + + if (SUCCEEDED(hr)) { + hr = pOutputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + } + + if (SUCCEEDED(hr)) { + hr = pOutputType->SetUINT32(MF_MT_SAMPLE_SIZE, (m_create.height * m_create.width * bitdepth)>>1); + } + + if (SUCCEEDED(hr)) { + hr = MFSetAttributeSize(pOutputType, MF_MT_FRAME_SIZE, m_create.width, m_create.height); + } + + if (SUCCEEDED(hr)) { + hr = MFSetAttributeRatio(pOutputType, MF_MT_FRAME_RATE, m_frameRate.Numerator, m_frameRate.Denominator); + } + + if (SUCCEEDED(hr)) { + hr = pOutputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive); + } + + if (SUCCEEDED(hr)) { + hr = MFSetAttributeRatio(pOutputType, MF_MT_PIXEL_ASPECT_RATIO, ar_x, ar_y); + } + + if (SUCCEEDED(hr)) { + *ppType = pOutputType; + (*ppType)->AddRef(); + } + + if (pOutputType) pOutputType->Release(); + + LeaveCriticalSection(&m_mft_lock); + return hr; +} + +HRESULT CXvidDecoder::MFTSetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags) +{ + DPRINTF("(MFT)SetInputType"); + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY) + return E_INVALIDARG; + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + + /* Actually set the type or just test it? */ + BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); + + /* If we have samples pending the type can't be changed right now */ + if (HasPendingOutput()) + hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; + + if (SUCCEEDED(hr)) { + if (pType) { // /* Check the type */ + hr = OnCheckInputType(pType); + } + } + + if (SUCCEEDED(hr)) { + if (bReallySet) { /* Set the type if needed */ + hr = OnSetInputType(pType); + } + } + + LeaveCriticalSection(&m_mft_lock); + return hr; +} + +HRESULT CXvidDecoder::MFTSetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags) +{ + DPRINTF("(MFT)SetOutputType"); + + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwFlags & ~MFT_SET_TYPE_TEST_ONLY) + return E_INVALIDARG; + + HRESULT hr = S_OK; + + EnterCriticalSection(&m_mft_lock); + + /* Actually set the type or just test it? + BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); + + /* If we have samples pending the type can't be changed right now */ + if (HasPendingOutput()) + hr = MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; + + if (SUCCEEDED(hr)) { + if (pType) { /* Check the type */ + AM_MEDIA_TYPE *am; + hr = MFCreateAMMediaTypeFromMFMediaType(pType, GUID_NULL, &am); + + if (SUCCEEDED(hr)) { + if (FAILED(ChangeColorspace(am->subtype, am->formattype, am->pbFormat))) { + DPRINTF("(MFT)InternalCheckOutputType (MF_E_INVALIDTYPE)"); + return MF_E_INVALIDTYPE; + } + + CoTaskMemFree(am->pbFormat); + CoTaskMemFree(am); + } + } + } + + if (SUCCEEDED(hr)) { + if (bReallySet) { /* Set the type if needed */ + hr = OnSetOutputType(pType); + } + } + +#ifdef XVID_USE_TRAYICON + if (SUCCEEDED(hr) && MSG_hwnd == NULL) /* Create message passing window */ + { + WNDCLASSEX wc; + + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = msg_proc; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.cbWndExtra = 0; + wc.cbClsExtra = 0; + wc.hInstance = (HINSTANCE) g_xvid_hInst; + wc.hbrBackground = (HBRUSH) GetStockObject(NULL_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = "XVID_MSG_WINDOW"; + wc.hIcon = NULL; + wc.hIconSm = NULL; + wc.hCursor = NULL; + RegisterClassEx(&wc); + + MSG_hwnd = CreateWindowEx(0, "XVID_MSG_WINDOW", NULL, 0, CW_USEDEFAULT, + CW_USEDEFAULT, 0, 0, HWND_MESSAGE, NULL, (HINSTANCE) g_xvid_hInst, NULL); + + /* display the tray icon */ + NOTIFYICONDATA nid; + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = MSG_hwnd; + nid.uID = 100; + nid.uVersion = NOTIFYICON_VERSION; + nid.uCallbackMessage = WM_ICONMESSAGE; + nid.hIcon = LoadIcon(g_xvid_hInst, MAKEINTRESOURCE(IDI_ICON)); + strcpy_s(nid.szTip, 19, "Xvid Video Decoder"); + nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; + + Shell_NotifyIcon(NIM_ADD, &nid); + } +#endif + + LeaveCriticalSection(&m_mft_lock); + return hr; +} + +HRESULT CXvidDecoder::MFTGetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType) +{ + DPRINTF("(MFT)GetInputCurrentType"); + + if (ppType == NULL) + return E_POINTER; + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + + if (!m_pInputType) + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + + if (SUCCEEDED(hr)) { + *ppType = m_pInputType; + (*ppType)->AddRef(); + } + + LeaveCriticalSection(&m_mft_lock); + return hr; +} + +HRESULT CXvidDecoder::MFTGetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType) +{ + DPRINTF("(MFT)GetOutputCurrentType"); + + if (ppType == NULL) + return E_POINTER; + + if (dwOutputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + + if (!m_pOutputType) + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + + if (SUCCEEDED(hr)) { + *ppType = m_pOutputType; + (*ppType)->AddRef(); + } + + LeaveCriticalSection(&m_mft_lock); + return hr; +} + +HRESULT CXvidDecoder::MFTGetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags) +{ + DPRINTF("(MFT)GetInputStatus"); + + if (pdwFlags == NULL) + return E_POINTER; + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&m_mft_lock); + + /* If there's pending output sampels we don't accept new + input data until ProcessOutput() or Flush() was called */ + if (!HasPendingOutput()) { + *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; + } + else { + *pdwFlags = 0; + } + + LeaveCriticalSection(&m_mft_lock); + + return S_OK; +} + +HRESULT CXvidDecoder::MFTGetOutputStatus(DWORD *pdwFlags) +{ + DPRINTF("(MFT)GetOutputStatus"); + + if (pdwFlags == NULL) + return E_POINTER; + + EnterCriticalSection(&m_mft_lock); + + /* We can render an output sample only after we + have decoded one */ + if (HasPendingOutput()) { + *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; + } + else { + *pdwFlags = 0; + } + + LeaveCriticalSection(&m_mft_lock); + + return S_OK; +} + +HRESULT CXvidDecoder::MFTSetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound) +{ + DPRINTF("(MFT)SetOutputBounds"); + return E_NOTIMPL; +} + +HRESULT CXvidDecoder::MFTProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent) +{ + DPRINTF("(MFT)ProcessEvent"); + return E_NOTIMPL; /* We don't handle any stream events */ +} + +HRESULT CXvidDecoder::MFTProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) +{ + DPRINTF("(MFT)ProcessMessage"); + HRESULT hr = S_OK; + + EnterCriticalSection(&m_mft_lock); + + switch (eMessage) + { + case MFT_MESSAGE_COMMAND_FLUSH: + if (m_create.handle != NULL) { + DPRINTF("(MFT)CommandFlush"); + + xvid_dec_stats_t stats; + int used_bytes; + + memset(&stats, 0, sizeof(stats)); + stats.version = XVID_VERSION; + + int csp = m_frame.output.csp; + + m_frame.output.csp = XVID_CSP_INTERNAL; + m_frame.bitstream = NULL; + m_frame.length = -1; + m_frame.general = XVID_LOWDELAY; + + do { + used_bytes = xvid_decore_func(m_create.handle, XVID_DEC_DECODE, &m_frame, &stats); + } while(used_bytes>=0 && stats.type <= 0); + + m_frame.output.csp = csp; + m_frame.output.plane[1] = NULL; /* Don't display flushed samples */ + + //m_timestamp = INVALID_TIME; + //m_timelength = INVALID_TIME; + //m_rtFrame = 0; + } + break; + + case MFT_MESSAGE_COMMAND_DRAIN: + m_discont = 1; /* Set discontinuity flag */ + m_rtFrame = 0; + break; + + case MFT_MESSAGE_SET_D3D_MANAGER: + hr = E_NOTIMPL; + break; + + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + case MFT_MESSAGE_NOTIFY_END_STREAMING: + break; + + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + break; + } + + LeaveCriticalSection(&m_mft_lock); + + return hr; +} + +HRESULT CXvidDecoder::MFTProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags) +{ + DPRINTF("(MFT)ProcessInput"); + + if (pSample == NULL) + return E_POINTER; + + if (dwInputStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (dwFlags != 0) + return E_INVALIDARG; + + if (!m_pInputType || !m_pOutputType) { + return MF_E_NOTACCEPTING; /* Must have set input and output types */ + } + else if (HasPendingOutput()) { + return MF_E_NOTACCEPTING; /* We still have output samples to render */ + } + + xvid_dec_stats_t stats; + int length; + + memset(&stats, 0, sizeof(stats)); + stats.version = XVID_VERSION; + + if (m_create.handle == NULL) + { + if (xvid_decore_func == NULL) + return E_FAIL; + if (xvid_decore_func(0, XVID_DEC_CREATE, &m_create, 0) < 0) + { + DPRINTF("*** XVID_DEC_CREATE error"); + return E_FAIL; + } + } + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + IMFMediaBuffer *pBuffer; + + if (SUCCEEDED(hr)) { + hr = pSample->ConvertToContiguousBuffer(&pBuffer); + } + + if (SUCCEEDED(hr)) { + hr = pBuffer->Lock((BYTE**)&m_frame.bitstream, NULL, (DWORD *)&m_frame.length); + } + + m_frame.general = XVID_LOWDELAY; + + if (m_discont == 1) { + m_frame.general |= XVID_DISCONTINUITY; + m_discont = 0; + } + + if (g_config.nDeblock_Y) + m_frame.general |= XVID_DEBLOCKY; + + if (g_config.nDeblock_UV) + m_frame.general |= XVID_DEBLOCKUV; + + if (g_config.nDering_Y) + m_frame.general |= XVID_DERINGY; + + if (g_config.nDering_UV) + m_frame.general |= XVID_DERINGUV; + + if (g_config.nFilmEffect) + m_frame.general |= XVID_FILMEFFECT; + + m_frame.brightness = g_config.nBrightness; + + m_frame.output.csp &= ~XVID_CSP_VFLIP; + m_frame.output.csp |= rgb_flip^(g_config.nFlipVideo ? XVID_CSP_VFLIP : 0); + + int csp = m_frame.output.csp; + m_frame.output.csp = XVID_CSP_INTERNAL; + + // Paranoid check. + if (xvid_decore_func == NULL) { + hr = E_FAIL; + goto END_LOOP; + } + +repeat : + length = xvid_decore_func(m_create.handle, XVID_DEC_DECODE, &m_frame, &stats); + + if (length == XVID_ERR_MEMORY) { + hr = E_FAIL; + goto END_LOOP; + } + else if (length < 0) + { + DPRINTF("*** XVID_DEC_DECODE"); + goto END_LOOP; + } + + if (stats.type == XVID_TYPE_NOTHING && length > 0) { + DPRINTF(" B-Frame decoder lag"); + m_frame.output.plane[1] = NULL; + goto END_LOOP; + } + + if (stats.type == XVID_TYPE_VOL) + { + if (stats.data.vol.width != m_create.width || + stats.data.vol.height != m_create.height) + { + DPRINTF("TODO: auto-resize"); + m_frame.output.plane[1] = NULL; + hr = E_FAIL; + } + + if (g_config.aspect_ratio == 0 || g_config.aspect_ratio == 1) { /* auto */ + int par_x, par_y; + if (stats.data.vol.par == XVID_PAR_EXT) { + par_x = stats.data.vol.par_width; + par_y = stats.data.vol.par_height; + } else { + par_x = PARS[stats.data.vol.par-1][0]; + par_y = PARS[stats.data.vol.par-1][1]; + } + + ar_x = par_x * stats.data.vol.width; + ar_y = par_y * stats.data.vol.height; + } + + m_frame.bitstream = (BYTE*)m_frame.bitstream + length; + m_frame.length -= length; + goto repeat; + } + +END_LOOP: + m_frame.output.csp = csp; + + if (pBuffer) { + pBuffer->Unlock(); + pBuffer->Release(); + } + + if (SUCCEEDED(hr)) { + /* Try to get a timestamp */ + if (FAILED(pSample->GetSampleTime(&m_timestamp))) + m_timestamp = INVALID_TIME; + + if (FAILED(pSample->GetSampleDuration(&m_timelength))) { + m_timelength = INVALID_TIME; + } + } + + LeaveCriticalSection(&m_mft_lock); + + return hr; +} + +HRESULT CXvidDecoder::MFTProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus) +{ + DPRINTF("(MFT)ProcessOutput"); + + /* Preroll in MFT ?? + Flags ?? -> TODO... */ + if (dwFlags != 0) + return E_INVALIDARG; + + if (pOutputSamples == NULL || pdwStatus == NULL) + return E_POINTER; + + if (cOutputBufferCount != 1) /* Must be exactly one output buffer */ + return E_INVALIDARG; + + if (pOutputSamples[0].pSample == NULL) /* Must have a sample */ + return E_INVALIDARG; + + if (!HasPendingOutput()) { /* If there's no sample we need to decode one first */ + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + EnterCriticalSection(&m_mft_lock); + + HRESULT hr = S_OK; + + BYTE *Dst = NULL; + DWORD buffer_size; + + IMFMediaBuffer *pOutput = NULL; + + if (SUCCEEDED(hr)) { + hr = pOutputSamples[0].pSample->GetBufferByIndex(0, &pOutput); /* Get output buffer */ + } + + if (SUCCEEDED(hr)) { + hr = pOutput->GetMaxLength(&buffer_size); + } + + if (SUCCEEDED(hr)) + hr = pOutput->Lock(&Dst, NULL, NULL); + + if (SUCCEEDED(hr)) { + xvid_gbl_convert_t convert; + + memset(&convert, 0, sizeof(convert)); + convert.version = XVID_VERSION; + + convert.input.csp = XVID_CSP_INTERNAL; + convert.input.plane[0] = m_frame.output.plane[0]; + convert.input.plane[1] = m_frame.output.plane[1]; + convert.input.plane[2] = m_frame.output.plane[2]; + convert.input.stride[0] = m_frame.output.stride[0]; + convert.input.stride[1] = m_frame.output.stride[1]; + convert.input.stride[2] = m_frame.output.stride[2]; + + convert.output.csp = m_frame.output.csp; + convert.output.plane[0] = Dst; + convert.output.stride[0] = out_stride; + + convert.width = m_create.width; + convert.height = m_create.height; + convert.interlacing = 0; + + if (m_frame.output.plane[1] != NULL && Dst != NULL && xvid_global_func != NULL) + if (xvid_global_func(0, XVID_GBL_CONVERT, &convert, NULL) < 0) /* CSP convert into output buffer */ + hr = E_FAIL; + + m_frame.output.plane[1] = NULL; + } + + *pdwStatus = 0; + + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) + hr = pOutputSamples[0].pSample->SetUINT32(MFSampleExtension_CleanPoint, TRUE); // key frame + + if (SUCCEEDED(hr)) { /* Set timestamp of output sample */ + if (m_timestamp != INVALID_TIME) + hr = pOutputSamples[0].pSample->SetSampleTime(m_timestamp); + else + hr = pOutputSamples[0].pSample->SetSampleTime(m_rtFrame); + + if (m_timelength != INVALID_TIME) + hr = pOutputSamples[0].pSample->SetSampleDuration(m_timelength); + else + hr = pOutputSamples[0].pSample->SetSampleDuration(m_duration); + + m_rtFrame += m_duration; + } + + if (SUCCEEDED(hr)) + hr = pOutput->SetCurrentLength(m_create.width * abs(m_create.height) * 4); // XXX + } + + if (pOutput) { + pOutput->Unlock(); + pOutput->Release(); + } + + LeaveCriticalSection(&m_mft_lock); + + return hr; +} + +HRESULT CXvidDecoder::OnCheckInputType(IMFMediaType *pmt) +{ + DPRINTF("(MFT)CheckInputType"); + + HRESULT hr = S_OK; + + /* Check if input type is already set. Reject any type that is not identical */ + if (m_pInputType) { + DWORD dwFlags = 0; + if (S_OK == m_pInputType->IsEqual(pmt, &dwFlags)) { + return S_OK; + } + else { + return MF_E_INVALIDTYPE; + } + } + + GUID majortype = {0}, subtype = {0}; + UINT32 width = 0, height = 0; + + hr = pmt->GetMajorType(&majortype); + + if (SUCCEEDED(hr)) { + if (majortype != MFMediaType_Video) { /* Must be Video */ + hr = MF_E_INVALIDTYPE; + } + } + + if (m_hdll == NULL) { + HRESULT hr = OpenLib(); + + if (FAILED(hr) || (m_hdll == NULL)) // Paranoid checks. + hr = MF_E_INVALIDTYPE; + } + + if (SUCCEEDED(hr)) { + hr = MFGetAttributeSize(pmt, MF_MT_FRAME_SIZE, &width, &height); + } + + /* Check the frame size */ + if (SUCCEEDED(hr)) { + if (width > 4096 || height > 4096) { + hr = MF_E_INVALIDTYPE; + } + } + m_create.width = width; + m_create.height = height; + + if (SUCCEEDED(hr)) { + if (g_config.aspect_ratio == 0 || g_config.aspect_ratio == 1) { + hr = MFGetAttributeRatio(pmt, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&ar_x, (UINT32*)&ar_y); + } + } + + /* TODO1: Make sure there really is a frame rate after all! + TODO2: Use the framerate for something! */ + MFRatio fps = {0}; + if (SUCCEEDED(hr)) { + hr = MFGetAttributeRatio(pmt, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator); + } + + if (SUCCEEDED(hr)) { + hr = pmt->GetGUID(MF_MT_SUBTYPE, &subtype); + } + + if (subtype == CLSID_MP4V || subtype == CLSID_MP4V_UC) { + if (!(g_config.supported_4cc & SUPPORT_MP4V)) { + CloseLib(); + hr = MF_E_INVALIDTYPE; + } + else m_create.fourcc = FOURCC_MP4V; + } + else if (subtype == CLSID_DIVX || subtype == CLSID_DIVX_UC) { + if (!(g_config.supported_4cc & SUPPORT_DIVX)) { + CloseLib(); + hr = MF_E_INVALIDTYPE; + } + else m_create.fourcc = FOURCC_DIVX; + } + else if (subtype == CLSID_DX50 || subtype == CLSID_DX50_UC) { + if (!(g_config.supported_4cc & SUPPORT_DX50)) { + CloseLib(); + hr = MF_E_INVALIDTYPE; + } + else m_create.fourcc = FOURCC_DX50; + } + else if (subtype == CLSID_XVID || subtype == CLSID_XVID_UC) { + m_create.fourcc = FOURCC_XVID; + } + else { + DPRINTF("Unknown subtype!"); + CloseLib(); + hr = MF_E_INVALIDTYPE; + } + + /* haali media splitter reports VOL information in the format header */ + if (SUCCEEDED(hr)) + { + UINT32 cbSeqHeader = 0; + + (void)pmt->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &cbSeqHeader); + + if (cbSeqHeader>0) { + xvid_dec_stats_t stats; + memset(&stats, 0, sizeof(stats)); + stats.version = XVID_VERSION; + + if (m_create.handle == NULL) { + if (xvid_decore_func == NULL) + hr = E_FAIL; + if (xvid_decore_func(0, XVID_DEC_CREATE, &m_create, 0) < 0) { + DPRINTF("*** XVID_DEC_CREATE error"); + hr = E_FAIL; + } + } + + if (SUCCEEDED(hr)) { + (void)pmt->GetAllocatedBlob(MF_MT_MPEG_SEQUENCE_HEADER, (UINT8 **)&m_frame.bitstream, (UINT32 *)&m_frame.length); + m_frame.general = 0; + m_frame.output.csp = XVID_CSP_NULL; + + int ret = 0; + if ((ret=xvid_decore_func(m_create.handle, XVID_DEC_DECODE, &m_frame, &stats)) >= 0) { + /* honour video dimensions reported in VOL header */ + if (stats.type == XVID_TYPE_VOL) { + m_create.width = stats.data.vol.width; + m_create.height = stats.data.vol.height; + } + } + + if (ret == XVID_ERR_MEMORY) hr = E_FAIL; + CoTaskMemFree(m_frame.bitstream); + } + } + } + + return hr; +} + +HRESULT CXvidDecoder::OnSetInputType(IMFMediaType *pmt) +{ + HRESULT hr = S_OK; + UINT32 w, h; + + if (m_pInputType) m_pInputType->Release(); + + hr = MFGetAttributeSize(pmt, MF_MT_FRAME_SIZE, &w, &h); + m_create.width = w; m_create.height = h; + + if (SUCCEEDED(hr)) + hr = MFGetAttributeRatio(pmt, MF_MT_FRAME_RATE, (UINT32*)&m_frameRate.Numerator, (UINT32*)&m_frameRate.Denominator); + + if (SUCCEEDED(hr)) { /* Store frame duration, derived from the frame rate */ + hr = MFFrameRateToAverageTimePerFrame(m_frameRate.Numerator, m_frameRate.Denominator, &m_duration); + } + + if (SUCCEEDED(hr)) { + m_pInputType = pmt; + m_pInputType->AddRef(); + } + + return hr; +} + +HRESULT CXvidDecoder::OnSetOutputType(IMFMediaType *pmt) +{ + if (m_pOutputType) m_pOutputType->Release(); + + m_pOutputType = pmt; + m_pOutputType->AddRef(); + + return S_OK; +} + +#endif /* XVID_USE_MFT */