--- trunk/xvidcore/dshow/src/CXvidDecoder.cpp 2004/04/18 07:55:11 1437 +++ trunk/xvidcore/dshow/src/CXvidDecoder.cpp 2011/07/06 13:50:28 2022 @@ -1,9 +1,10 @@ /***************************************************************************** * * XVID MPEG-4 VIDEO CODEC - * - XviD Decoder part of the DShow Filter - + * - Xvid Decoder part of the DShow Filter - * - * Copyright(C) 2002-2004 Peter Ross + * Copyright(C) 2002-2011 Peter Ross + * 2003-2011 Michael Militzer * * This program is free software ; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,15 +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.7 2004-04-18 07:55:11 syskin Exp $ - * - ****************************************************************************/ - -/**************************************************************************** - * - * 2003/12/11 - added some additional options, mainly to make the deblocking - * code from xvidcore available. Most of the new code is taken - * from Nic's dshow filter, (C) Nic, http://nic.dnsalias.com + * $Id$ * ****************************************************************************/ @@ -36,14 +29,16 @@ place these paths at the top of the Tools|Options|Directories list headers: - C:\DXVCSDK\include - C:\DXVCSDK\samples\Multimedia\DirectShow\BaseClasses + C:\DX90SDK\Include + C:\DX90SDK\Samples\C++\DirectShow\BaseClasses - libraries (optional): - C:\DXVCSDK\samples\Multimedia\DirectShow\BaseClasses\Release + C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\Release + C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\Debug */ - +#ifdef ENABLE_MFT +#define XVID_USE_MFT +#endif #include @@ -55,7 +50,19 @@ #endif #include // VIDEOINFOHEADER2 -#include // XviD API +#if defined(XVID_USE_MFT) +#define MFT_UNIQUE_METHOD_NAMES +#include +#include +#include +#include +#endif + +#include + +#include // Xvid API + +#include "resource.h" #include "IXvidDecoder.h" #include "CXvidDecoder.h" @@ -81,7 +88,24 @@ { &MEDIATYPE_Video, &CLSID_DIVX_UC }, { &MEDIATYPE_Video, &CLSID_DX50 }, { &MEDIATYPE_Video, &CLSID_DX50_UC }, + { &MEDIATYPE_Video, &CLSID_3IVX }, + { &MEDIATYPE_Video, &CLSID_3IVX_UC }, + { &MEDIATYPE_Video, &CLSID_3IV0 }, + { &MEDIATYPE_Video, &CLSID_3IV0_UC }, + { &MEDIATYPE_Video, &CLSID_3IV1 }, + { &MEDIATYPE_Video, &CLSID_3IV1_UC }, + { &MEDIATYPE_Video, &CLSID_3IV2 }, + { &MEDIATYPE_Video, &CLSID_3IV2_UC }, + { &MEDIATYPE_Video, &CLSID_LMP4 }, + { &MEDIATYPE_Video, &CLSID_LMP4_UC }, + { &MEDIATYPE_Video, &CLSID_RMP4 }, + { &MEDIATYPE_Video, &CLSID_RMP4_UC }, + { &MEDIATYPE_Video, &CLSID_SMP4 }, + { &MEDIATYPE_Video, &CLSID_SMP4_UC }, + { &MEDIATYPE_Video, &CLSID_HDX4 }, + { &MEDIATYPE_Video, &CLSID_HDX4_UC }, { &MEDIATYPE_Video, &CLSID_MP4V }, + { &MEDIATYPE_Video, &CLSID_MP4V_UC }, }; const AMOVIESETUP_MEDIATYPE sudOutputPinTypes[] = @@ -121,7 +145,7 @@ { &CLSID_XVID, // Filter CLSID XVID_NAME_L, // Filter name - MERIT_PREFERRED, // Its merit + MERIT_PREFERRED+2, // Its merit sizeof(psudPins) / sizeof(AMOVIESETUP_PIN), // Number of pins psudPins // Pin details }; @@ -148,19 +172,96 @@ }; - /* note: g_cTemplates must be global; used by strmbase.lib(dllentry.cpp,dllsetup.cpp) */ int g_cTemplates = sizeof(g_Templates) / sizeof(CFactoryTemplate); +extern HINSTANCE g_xvid_hInst; + +static int GUI_Page = 0; +static int Tray_Icon = 0; +extern "C" void CALLBACK Configure(HWND hWndParent, HINSTANCE hInstParent, LPSTR lpCmdLine, int nCmdShow ); + +LRESULT CALLBACK msg_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch ( uMsg ) + { + case WM_ICONMESSAGE: + switch(lParam) + { + case WM_LBUTTONDBLCLK: + if (!GUI_Page) { + GUI_Page = 1; + Configure(hwnd, g_xvid_hInst, "", 1); + GUI_Page = 0; + } + break; + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + }; + break; + + case WM_DESTROY: + NOTIFYICONDATA nid; + ZeroMemory(&nid,sizeof(NOTIFYICONDATA)); + + nid.cbSize = NOTIFYICONDATA_V1_SIZE; + nid.hWnd = hwnd; + nid.uID = 1456; + + Shell_NotifyIcon(NIM_DELETE, &nid); + Tray_Icon = 0; + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return TRUE; /* ok */ +} 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 ); } @@ -194,6 +295,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); } @@ -201,53 +309,105 @@ /* constructor */ -#define XVID_DLL_NAME "xvidcore.dll" - CXvidDecoder::CXvidDecoder(LPUNKNOWN punk, HRESULT *phr) : - CVideoTransformFilter(NAME("CXvidDecoder"), punk, CLSID_XVID) + CVideoTransformFilter(TEXT("CXvidDecoder"), punk, CLSID_XVID), m_hdll (NULL) { DPRINTF("Constructor"); + xvid_decore_func = NULL; // Hmm, some strange errors appearing if I try to initialize... + xvid_global_func = NULL; // ...this in constructor's init-list. So, they assigned here. + +#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(); +} + +HRESULT CXvidDecoder::OpenLib() +{ + DPRINTF("OpenLib"); + + if (m_hdll != NULL) + return E_UNEXPECTED; // Seems, that library already opened. + xvid_gbl_init_t init; memset(&init, 0, sizeof(init)); init.version = XVID_VERSION; + init.cpu_flags = g_config.cpu; - ar_x = ar_y = 0; + xvid_gbl_info_t info; + memset(&info, 0, sizeof(info)); + info.version = XVID_VERSION; m_hdll = LoadLibrary(XVID_DLL_NAME); if (m_hdll == NULL) { DPRINTF("dll load failed"); - MessageBox(0, XVID_DLL_NAME " not found","Error", 0); - return; + MessageBox(0, XVID_DLL_NAME TEXT(" not found"), TEXT("Error"), MB_TOPMOST); + return E_FAIL; } xvid_global_func = (int (__cdecl *)(void *, int, void *, void *))GetProcAddress(m_hdll, "xvid_global"); if (xvid_global_func == NULL) { - MessageBox(0, "xvid_global() not found", "Error", 0); - return; + FreeLibrary(m_hdll); + m_hdll = NULL; + MessageBox(0, TEXT("xvid_global() not found"), TEXT("Error"), MB_TOPMOST); + return E_FAIL; } xvid_decore_func = (int (__cdecl *)(void *, int, void *, void *))GetProcAddress(m_hdll, "xvid_decore"); if (xvid_decore_func == NULL) { - MessageBox(0, "xvid_decore() not found", "Error", 0); - return; + xvid_global_func = NULL; + FreeLibrary(m_hdll); + m_hdll = NULL; + MessageBox(0, TEXT("xvid_decore() not found"), TEXT("Error"), MB_TOPMOST); + return E_FAIL; } if (xvid_global_func(0, XVID_GBL_INIT, &init, NULL) < 0) { - MessageBox(0, "xvid_global() failed", "Error", 0); - return; + xvid_global_func = NULL; + xvid_decore_func = NULL; + FreeLibrary(m_hdll); + m_hdll = NULL; + MessageBox(0, TEXT("xvid_global() failed"), TEXT("Error"), MB_TOPMOST); + return E_FAIL; + } + + if (xvid_global_func(0, XVID_GBL_INFO, &info, NULL) < 0) + { + xvid_global_func = NULL; + xvid_decore_func = NULL; + FreeLibrary(m_hdll); + m_hdll = NULL; + MessageBox(0, TEXT("xvid_global() failed"), TEXT("Error"), MB_TOPMOST); + return E_FAIL; } memset(&m_create, 0, sizeof(m_create)); m_create.version = XVID_VERSION; m_create.handle = NULL; + /* Decoder threads */ + if (g_config.cpu & XVID_CPU_FORCE) { + m_create.num_threads = g_config.num_threads; + } + else { + m_create.num_threads = info.num_threads; /* Autodetect */ + g_config.num_threads = info.num_threads; + } memset(&m_frame, 0, sizeof(m_frame)); m_frame.version = XVID_VERSION; - LoadRegistryInfo(); - USE_IYUV = false; USE_YV12 = false; USE_YUY2 = false; @@ -285,13 +445,35 @@ USE_RGB32 = true; break; } + + switch (g_config.aspect_ratio) + { + case 0: + case 1: + break; + case 2: + ar_x = 4; + ar_y = 3; + break; + case 3: + ar_x = 16; + ar_y = 9; + break; + case 4: + ar_x = 47; + ar_y = 20; + break; + } + + return S_OK; } void CXvidDecoder::CloseLib() { - DPRINTF("Destructor"); + DPRINTF("CloseLib"); - if (m_create.handle != NULL) { + if ((m_create.handle != NULL) && (xvid_decore_func != NULL)) + { xvid_decore_func(m_create.handle, XVID_DEC_DESTROY, 0, 0); m_create.handle = NULL; } @@ -300,13 +482,34 @@ FreeLibrary(m_hdll); m_hdll = NULL; } + xvid_decore_func = NULL; + xvid_global_func = NULL; } /* destructor */ CXvidDecoder::~CXvidDecoder() { + DPRINTF("Destructor"); + + if (Tray_Icon) { /* Destroy tray icon */ + NOTIFYICONDATA nid; + ZeroMemory(&nid,sizeof(NOTIFYICONDATA)); + + nid.cbSize = NOTIFYICONDATA_V1_SIZE; + nid.hWnd = MSG_hwnd; + nid.uID = 1456; + + Shell_NotifyIcon(NIM_DELETE, &nid); + Tray_Icon = 0; + } + + /* Close xvidcore library */ CloseLib(); + +#if defined(XVID_USE_MFT) + DeleteCriticalSection(&m_mft_lock); +#endif } @@ -317,6 +520,8 @@ { DPRINTF("CheckInputType"); BITMAPINFOHEADER * hdr; + + ar_x = ar_y = 0; if (*mtIn->Type() != MEDIATYPE_Video) { @@ -325,60 +530,125 @@ return VFW_E_TYPE_NOT_ACCEPTED; } + if (m_hdll == NULL) + { + HRESULT hr = OpenLib(); + + if (FAILED(hr) || (m_hdll == NULL)) // Paranoid checks. + return VFW_E_TYPE_NOT_ACCEPTED; + } + if (*mtIn->FormatType() == FORMAT_VideoInfo) { VIDEOINFOHEADER * vih = (VIDEOINFOHEADER *) mtIn->Format(); hdr = &vih->bmiHeader; - /* PAR (x:y) is (1/ppm_X):(1/ppm_Y) where ppm is pixels-per-meter - which is equal to ppm_Y:ppm_X */ - ar_x = vih->bmiHeader.biYPelsPerMeter * abs(hdr->biWidth); - ar_y = vih->bmiHeader.biXPelsPerMeter * abs(hdr->biHeight); - DPRINTF("VIDEOINFOHEADER PAR: %d:%d -> AR %d:%d", - vih->bmiHeader.biYPelsPerMeter,vih->bmiHeader.biXPelsPerMeter, ar_x, ar_y); } else if (*mtIn->FormatType() == FORMAT_VideoInfo2) { VIDEOINFOHEADER2 * vih2 = (VIDEOINFOHEADER2 *) mtIn->Format(); hdr = &vih2->bmiHeader; - ar_x = vih2->dwPictAspectRatioX; - ar_y = vih2->dwPictAspectRatioY; + if (g_config.aspect_ratio == 0 || g_config.aspect_ratio == 1) { + ar_x = vih2->dwPictAspectRatioX; + ar_y = vih2->dwPictAspectRatioY; + } DPRINTF("VIDEOINFOHEADER2 AR: %d:%d", ar_x, ar_y); } - else - { - DPRINTF("Error: Unknown FormatType"); - CloseLib(); - return VFW_E_TYPE_NOT_ACCEPTED; - } - - if (hdr->biHeight < 0) - { - DPRINTF("colorspace: inverted input format not supported"); - } + else if (*mtIn->FormatType() == FORMAT_MPEG2Video) { + MPEG2VIDEOINFO * mpgvi = (MPEG2VIDEOINFO*)mtIn->Format(); + VIDEOINFOHEADER2 * vih2 = &mpgvi->hdr; + hdr = &vih2->bmiHeader; + if (g_config.aspect_ratio == 0 || g_config.aspect_ratio == 1) { + ar_x = vih2->dwPictAspectRatioX; + ar_y = vih2->dwPictAspectRatioY; + } + DPRINTF("VIDEOINFOHEADER2 AR: %d:%d", ar_x, ar_y); - m_create.width = hdr->biWidth; - m_create.height = hdr->biHeight; + /* haali media splitter reports VOL information in the format header */ - switch(hdr->biCompression) - { + if (mpgvi->cbSequenceHeader>0) { - case FOURCC_MP4V: + xvid_dec_stats_t stats; + 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; + } + } + + m_frame.general = 0; + m_frame.bitstream = (void*)mpgvi->dwSequenceHeader; + m_frame.length = mpgvi->cbSequenceHeader; + 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) { + hdr->biWidth = stats.data.vol.width; + hdr->biHeight = stats.data.vol.height; + } + } + if (ret == XVID_ERR_MEMORY) return E_FAIL; + } + } + 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) + { + case FOURCC_mp4v : + case FOURCC_MP4V : + case FOURCC_lmp4 : + case FOURCC_LMP4 : + case FOURCC_rmp4 : + case FOURCC_RMP4 : + case FOURCC_smp4 : + case FOURCC_SMP4 : + case FOURCC_hdx4 : + case FOURCC_HDX4 : if (!(g_config.supported_4cc & SUPPORT_MP4V)) { CloseLib(); return VFW_E_TYPE_NOT_ACCEPTED; } break; + case FOURCC_divx : case FOURCC_DIVX : + case FOURCC_dx50 : + case FOURCC_DX50 : if (!(g_config.supported_4cc & SUPPORT_DIVX)) { CloseLib(); return VFW_E_TYPE_NOT_ACCEPTED; } break; - case FOURCC_DX50 : - if (!(g_config.supported_4cc & SUPPORT_DX50)) { + case FOURCC_3ivx : + case FOURCC_3IVX : + case FOURCC_3iv0 : + case FOURCC_3IV0 : + case FOURCC_3iv1 : + case FOURCC_3IV1 : + case FOURCC_3iv2 : + case FOURCC_3IV2 : + if (!(g_config.supported_4cc & SUPPORT_3IVX)) { CloseLib(); return VFW_E_TYPE_NOT_ACCEPTED; } + case FOURCC_xvid : case FOURCC_XVID : break; @@ -393,6 +663,9 @@ CloseLib(); return VFW_E_TYPE_NOT_ACCEPTED; } + + m_create.fourcc = hdr->biCompression; + return S_OK; } @@ -421,11 +694,12 @@ if (ar_x != 0 && ar_y != 0) { vih->dwPictAspectRatioX = ar_x; vih->dwPictAspectRatioY = ar_y; + forced_ar = true; } else { // just to be safe vih->dwPictAspectRatioX = m_create.width; vih->dwPictAspectRatioY = abs(m_create.height); - } - + forced_ar = false; + } } else { VIDEOINFOHEADER * vih = (VIDEOINFOHEADER *) mtOut->ReallocFormatBuffer(sizeof(VIDEOINFOHEADER)); @@ -533,19 +807,24 @@ /* (internal function) change colorspace */ +#define CALC_BI_STRIDE(width,bitcount) ((((width * bitcount) + 31) & ~31) >> 3) -HRESULT CXvidDecoder::ChangeColorspace(GUID subtype, GUID formattype, void * format) +HRESULT CXvidDecoder::ChangeColorspace(GUID subtype, GUID formattype, void * format, int noflip) { + DWORD biWidth; + if (formattype == FORMAT_VideoInfo) { VIDEOINFOHEADER * vih = (VIDEOINFOHEADER * )format; - m_frame.output.stride[0] = (((vih->bmiHeader.biWidth * vih->bmiHeader.biBitCount) + 31) & ~31) >> 3; + biWidth = vih->bmiHeader.biWidth; + 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; - m_frame.output.stride[0] = (((vih2->bmiHeader.biWidth * vih2->bmiHeader.biBitCount) + 31) & ~31) >> 3; + biWidth = vih2->bmiHeader.biWidth; + out_stride = CALC_BI_STRIDE(vih2->bmiHeader.biWidth, vih2->bmiHeader.biBitCount); rgb_flip = (vih2->bmiHeader.biHeight < 0 ? 0 : XVID_CSP_VFLIP); } else @@ -553,19 +832,21 @@ return S_FALSE; } + if (noflip) rgb_flip = 0; + if (subtype == CLSID_MEDIASUBTYPE_IYUV) { DPRINTF("IYUV"); rgb_flip = 0; m_frame.output.csp = XVID_CSP_I420; - m_frame.output.stride[0] = (m_frame.output.stride[0] * 2) / 3; /* 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] = (m_frame.output.stride[0] * 2) / 3; /* planar format fix */ + out_stride = CALC_BI_STRIDE(biWidth, 8); /* planar format fix */ } else if (subtype == MEDIASUBTYPE_YUY2) { @@ -626,7 +907,7 @@ if (direction == PINDIR_OUTPUT) { - return ChangeColorspace(*pmt->Subtype(), *pmt->FormatType(), pmt->Format()); + return ChangeColorspace(*pmt->Subtype(), *pmt->FormatType(), pmt->Format(), 0); } return S_OK; @@ -638,9 +919,65 @@ HRESULT CXvidDecoder::CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut) { DPRINTF("CheckTransform"); + + return S_OK; +} + +/* input/output pin connection complete */ + +HRESULT CXvidDecoder::CompleteConnect(PIN_DIRECTION direction, IPin *pReceivePin) +{ + DPRINTF("CompleteConnect"); + + if ((direction == PINDIR_OUTPUT) && (Tray_Icon == 0) && (g_config.bTrayIcon != 0)) + { + 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 = TEXT("XVID_MSG_WINDOW"); + wc.hIcon = NULL; + wc.hIconSm = NULL; + wc.hCursor = NULL; + RegisterClassEx(&wc); + + MSG_hwnd = CreateWindowEx(0, TEXT("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; + ZeroMemory(&nid,sizeof(NOTIFYICONDATA)); + + nid.cbSize = NOTIFYICONDATA_V1_SIZE; + nid.hWnd = MSG_hwnd; + nid.uID = 1456; + nid.uCallbackMessage = WM_ICONMESSAGE; + nid.hIcon = LoadIcon(g_xvid_hInst, MAKEINTRESOURCE(IDI_ICON)); + lstrcpy(nid.szTip, TEXT("Xvid Video Decoder")); + nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; + + Shell_NotifyIcon(NIM_ADD, &nid); + + DestroyIcon(nid.hIcon); + Tray_Icon = 1; + } + return S_OK; } +/* input/output pin disconnected */ +HRESULT CXvidDecoder::BreakConnect(PIN_DIRECTION direction) +{ + DPRINTF("BreakConnect"); + + return S_OK; +} /* alloc output buffer */ @@ -689,10 +1026,13 @@ 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 S_FALSE; + return E_FAIL; } } @@ -702,7 +1042,7 @@ { HRESULT result; - result = ChangeColorspace(mtOut->subtype, mtOut->formattype, mtOut->pbFormat); + result = ChangeColorspace(mtOut->subtype, mtOut->formattype, mtOut->pbFormat, 0); DeleteMediaType(mtOut); if (result != S_OK) @@ -747,17 +1087,49 @@ 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) { length = xvid_decore_func(m_create.handle, XVID_DEC_DECODE, &m_frame, &stats); - - if (length < 0) + + if (length == XVID_ERR_MEMORY) + return E_FAIL; + else if (length < 0) { DPRINTF("*** XVID_DEC_DECODE"); return S_FALSE; + } else + if (g_config.aspect_ratio == 0 || g_config.aspect_ratio == 1 && forced_ar == false) { + + if (stats.type != XVID_TYPE_NOTHING) { /* dont attempt to set vmr aspect ratio if no frame was returned by decoder */ + // inspired by minolta! works for VMR 7 + 9 + IMediaSample2 *pOut2 = NULL; + AM_SAMPLE2_PROPERTIES outProp2; + if (SUCCEEDED(pOut->QueryInterface(IID_IMediaSample2, (void **)&pOut2)) && + SUCCEEDED(pOut2->GetProperties(FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&outProp2))) + { + CMediaType mtOut2 = m_pOutput->CurrentMediaType(); + VIDEOINFOHEADER2* vihOut2 = (VIDEOINFOHEADER2*)mtOut2.Format(); + + if (*mtOut2.FormatType() == FORMAT_VideoInfo2 && + vihOut2->dwPictAspectRatioX != ar_x && vihOut2->dwPictAspectRatioY != ar_y) + { + vihOut2->dwPictAspectRatioX = ar_x; + vihOut2->dwPictAspectRatioY = ar_y; + pOut2->SetMediaType(&mtOut2); + m_pOutput->SetMediaType(&mtOut2); + } + pOut2->Release(); + } + } } } else @@ -770,11 +1142,13 @@ /* Disable postprocessing to speed-up seeking */ m_frame.general &= ~XVID_DEBLOCKY; m_frame.general &= ~XVID_DEBLOCKUV; -/* m_frame.general &= ~XVID_DERING; */ + /*m_frame.general &= ~XVID_DERING;*/ m_frame.general &= ~XVID_FILMEFFECT; length = xvid_decore_func(m_create.handle, XVID_DEC_DECODE, &m_frame, &stats); - if (length < 0) + if (length == XVID_ERR_MEMORY) + return E_FAIL; + else if (length < 0) { DPRINTF("*** XVID_DEC_DECODE"); return S_FALSE; @@ -798,10 +1172,23 @@ DPRINTF("TODO: auto-resize"); return S_FALSE; } - -// pOut->SetDiscontinuity(TRUE); + pOut->SetSyncPoint(TRUE); + 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; @@ -841,3 +1228,1087 @@ 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_3IVX) { + bs_guid_table[i++] = (GUID *)&CLSID_3IVX; + bs_guid_table[i++] = (GUID *)&CLSID_3IVX_UC; + bs_guid_table[i++] = (GUID *)&CLSID_3IV0; + bs_guid_table[i++] = (GUID *)&CLSID_3IV0_UC; + bs_guid_table[i++] = (GUID *)&CLSID_3IV1; + bs_guid_table[i++] = (GUID *)&CLSID_3IV1_UC; + bs_guid_table[i++] = (GUID *)&CLSID_3IV2; + bs_guid_table[i++] = (GUID *)&CLSID_3IV2_UC; + } + if (g_config.supported_4cc & SUPPORT_DIVX) { + bs_guid_table[i++] = (GUID *)&CLSID_DIVX; + bs_guid_table[i++] = (GUID *)&CLSID_DIVX_UC; + bs_guid_table[i++] = (GUID *)&CLSID_DX50; + bs_guid_table[i++] = (GUID *)&CLSID_DX50_UC; + } + if (g_config.supported_4cc & SUPPORT_MP4V) { + bs_guid_table[i++] = (GUID *)&CLSID_MP4V; + bs_guid_table[i++] = (GUID *)&CLSID_MP4V_UC; + bs_guid_table[i++] = (GUID *)&CLSID_LMP4; + bs_guid_table[i++] = (GUID *)&CLSID_LMP4_UC; + bs_guid_table[i++] = (GUID *)&CLSID_RMP4; + bs_guid_table[i++] = (GUID *)&CLSID_RMP4_UC; + bs_guid_table[i++] = (GUID *)&CLSID_SMP4; + bs_guid_table[i++] = (GUID *)&CLSID_SMP4_UC; + bs_guid_table[i++] = (GUID *)&CLSID_HDX4; + bs_guid_table[i++] = (GUID *)&CLSID_HDX4_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, 1))) { + 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); + } + } + + if (SUCCEEDED(hr) && (Tray_Icon == 0) && (g_config.bTrayIcon != 0)) /* 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 = TEXT("XVID_MSG_WINDOW"); + wc.hIcon = NULL; + wc.hIconSm = NULL; + wc.hCursor = NULL; + RegisterClassEx(&wc); + + MSG_hwnd = CreateWindowEx(0, TEXT("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; + ZeroMemory(&nid,sizeof(NOTIFYICONDATA)); + + nid.cbSize = NOTIFYICONDATA_V1_SIZE; + nid.hWnd = MSG_hwnd; + nid.uID = 1456; + nid.uCallbackMessage = WM_ICONMESSAGE; + nid.hIcon = LoadIcon(g_xvid_hInst, MAKEINTRESOURCE(IDI_ICON)); + lstrcpy(nid.szTip, TEXT("Xvid Video Decoder")); + nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; + + Shell_NotifyIcon(NIM_ADD, &nid); + + DestroyIcon(nid.hIcon); + Tray_Icon = 1; + } + + 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 = NULL; + + 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; + } + if (m_timestamp != INVALID_TIME && stats.type == XVID_TYPE_IVOP) { + m_rtFrame = m_timestamp; + } + } + + 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 hr2 = OpenLib(); + + if (FAILED(hr2) || (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 || + subtype == CLSID_LMP4 || subtype == CLSID_LMP4_UC || + subtype == CLSID_RMP4 || subtype == CLSID_RMP4_UC || + subtype == CLSID_SMP4 || subtype == CLSID_SMP4_UC || + subtype == CLSID_HDX4 || subtype == CLSID_HDX4_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_DIVX)) { + CloseLib(); + hr = MF_E_INVALIDTYPE; + } + else m_create.fourcc = FOURCC_DX50; + } + else if (subtype == CLSID_3IVX || subtype == CLSID_3IVX_UC || + subtype == CLSID_3IV0 || subtype == CLSID_3IV0_UC || + subtype == CLSID_3IV1 || subtype == CLSID_3IV1_UC || + subtype == CLSID_3IV2 || subtype == CLSID_3IV2_UC) { + if (!(g_config.supported_4cc & SUPPORT_3IVX)) { + CloseLib(); + hr = MF_E_INVALIDTYPE; + } + else m_create.fourcc = FOURCC_3IVX; + } + 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 */