mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 11:26:10 +00:00
79ce998e79
Fixes upside-down videos in multiple games (e.g. Hard Truck 2, Firestarter).
417 lines
12 KiB
C
417 lines
12 KiB
C
/*
|
|
* Intel Indeo 5 Video Decoder
|
|
* Copyright 2023 Shaun Ren for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "commdlg.h"
|
|
#include "vfw.h"
|
|
#include "initguid.h"
|
|
#include "ir50_private.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(ir50_32);
|
|
|
|
static HINSTANCE IR50_32_hModule;
|
|
|
|
#define IV50_MAGIC mmioFOURCC('I','V','5','0')
|
|
#define compare_fourcc(fcc1, fcc2) (((fcc1)^(fcc2))&~0x20202020)
|
|
|
|
DEFINE_MEDIATYPE_GUID(MFVideoFormat_IV50, MAKEFOURCC('I','V','5','0'));
|
|
|
|
static inline UINT64
|
|
make_uint64( UINT32 high, UINT32 low )
|
|
{
|
|
return ((UINT64)high << 32) | low;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
IV50_Open( const ICINFO *icinfo )
|
|
{
|
|
IMFTransform *decoder = NULL;
|
|
|
|
TRACE("DRV_OPEN %p\n", icinfo);
|
|
|
|
if ( icinfo && compare_fourcc( icinfo->fccType, ICTYPE_VIDEO ) )
|
|
return 0;
|
|
|
|
if ( FAILED(winegstreamer_create_video_decoder( &decoder )) )
|
|
return 0;
|
|
|
|
return (LRESULT)decoder;
|
|
}
|
|
|
|
static LRESULT
|
|
IV50_DecompressQuery( LPBITMAPINFO in, LPBITMAPINFO out )
|
|
{
|
|
TRACE("ICM_DECOMPRESS_QUERY %p %p\n", in, out);
|
|
|
|
TRACE("in->planes = %d\n", in->bmiHeader.biPlanes);
|
|
TRACE("in->bpp = %d\n", in->bmiHeader.biBitCount);
|
|
TRACE("in->height = %ld\n", in->bmiHeader.biHeight);
|
|
TRACE("in->width = %ld\n", in->bmiHeader.biWidth);
|
|
TRACE("in->compr = %#lx\n", in->bmiHeader.biCompression);
|
|
|
|
if ( in->bmiHeader.biCompression != IV50_MAGIC )
|
|
{
|
|
TRACE("can't do %#lx compression\n", in->bmiHeader.biCompression);
|
|
return ICERR_BADFORMAT;
|
|
}
|
|
|
|
/* output must be same dimensions as input */
|
|
if ( out )
|
|
{
|
|
TRACE("out->planes = %d\n", out->bmiHeader.biPlanes);
|
|
TRACE("out->bpp = %d\n", out->bmiHeader.biBitCount);
|
|
TRACE("out->height = %ld\n", out->bmiHeader.biHeight);
|
|
TRACE("out->width = %ld\n", out->bmiHeader.biWidth);
|
|
TRACE("out->compr = %#lx\n", out->bmiHeader.biCompression);
|
|
|
|
if ( out->bmiHeader.biCompression != BI_RGB )
|
|
{
|
|
TRACE("incompatible compression requested\n");
|
|
return ICERR_BADFORMAT;
|
|
}
|
|
|
|
if ( out->bmiHeader.biBitCount != 32 && out->bmiHeader.biBitCount != 24 && out->bmiHeader.biBitCount != 16 )
|
|
{
|
|
TRACE("incompatible depth requested\n");
|
|
return ICERR_BADFORMAT;
|
|
}
|
|
|
|
if ( in->bmiHeader.biPlanes != out->bmiHeader.biPlanes ||
|
|
in->bmiHeader.biHeight != abs(out->bmiHeader.biHeight) ||
|
|
in->bmiHeader.biWidth != out->bmiHeader.biWidth )
|
|
{
|
|
TRACE("incompatible output dimensions requested\n");
|
|
return ICERR_BADFORMAT;
|
|
}
|
|
}
|
|
|
|
return ICERR_OK;
|
|
}
|
|
|
|
static LRESULT
|
|
IV50_DecompressGetFormat( LPBITMAPINFO in, LPBITMAPINFO out )
|
|
{
|
|
DWORD size;
|
|
|
|
TRACE("ICM_DECOMPRESS_GETFORMAT %p %p\n", in, out);
|
|
|
|
if ( !in )
|
|
return ICERR_BADPARAM;
|
|
|
|
if ( in->bmiHeader.biCompression != IV50_MAGIC )
|
|
return ICERR_BADFORMAT;
|
|
|
|
size = in->bmiHeader.biSize;
|
|
if ( out )
|
|
{
|
|
memcpy( out, in, size );
|
|
out->bmiHeader.biHeight = abs(in->bmiHeader.biHeight);
|
|
out->bmiHeader.biCompression = BI_RGB;
|
|
out->bmiHeader.biBitCount = 32;
|
|
out->bmiHeader.biSizeImage = out->bmiHeader.biWidth * out->bmiHeader.biHeight * 4;
|
|
return ICERR_OK;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static LRESULT IV50_DecompressBegin( IMFTransform *decoder, LPBITMAPINFO in, LPBITMAPINFO out )
|
|
{
|
|
IMFMediaType *input_type, *output_type;
|
|
const GUID *output_subtype;
|
|
LRESULT r = ICERR_INTERNAL;
|
|
unsigned int stride;
|
|
|
|
TRACE("ICM_DECOMPRESS_BEGIN %p %p %p\n", decoder, in, out);
|
|
|
|
if ( !decoder )
|
|
return ICERR_BADPARAM;
|
|
|
|
if ( out->bmiHeader.biBitCount == 32 )
|
|
output_subtype = &MFVideoFormat_RGB32;
|
|
else if ( out->bmiHeader.biBitCount == 24 )
|
|
output_subtype = &MFVideoFormat_RGB24;
|
|
else if ( out->bmiHeader.biBitCount == 16 )
|
|
output_subtype = &MFVideoFormat_RGB555;
|
|
else
|
|
return ICERR_BADFORMAT;
|
|
|
|
stride = (out->bmiHeader.biWidth + 3) & ~3;
|
|
if (out->bmiHeader.biHeight >= 0)
|
|
stride = -stride;
|
|
|
|
if ( FAILED(MFCreateMediaType( &input_type )) )
|
|
return ICERR_INTERNAL;
|
|
|
|
if ( FAILED(MFCreateMediaType( &output_type )) )
|
|
{
|
|
IMFMediaType_Release( input_type );
|
|
return ICERR_INTERNAL;
|
|
}
|
|
|
|
if ( FAILED(IMFMediaType_SetGUID( input_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video )) ||
|
|
FAILED(IMFMediaType_SetGUID( input_type, &MF_MT_SUBTYPE, &MFVideoFormat_IV50 )) )
|
|
goto done;
|
|
if ( FAILED(IMFMediaType_SetUINT64(
|
|
input_type, &MF_MT_FRAME_SIZE,
|
|
make_uint64( in->bmiHeader.biWidth, in->bmiHeader.biHeight ) )) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFMediaType_SetGUID( output_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video )) ||
|
|
FAILED(IMFMediaType_SetGUID( output_type, &MF_MT_SUBTYPE, output_subtype )) )
|
|
goto done;
|
|
if ( FAILED(IMFMediaType_SetUINT64(
|
|
output_type, &MF_MT_FRAME_SIZE,
|
|
make_uint64( out->bmiHeader.biWidth, abs(out->bmiHeader.biHeight) ) )) )
|
|
goto done;
|
|
if ( FAILED(IMFMediaType_SetUINT32( output_type, &MF_MT_DEFAULT_STRIDE, stride)) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFTransform_SetInputType( decoder, 0, input_type, 0 )) ||
|
|
FAILED(IMFTransform_SetOutputType( decoder, 0, output_type, 0 )) )
|
|
goto done;
|
|
|
|
r = ICERR_OK;
|
|
|
|
done:
|
|
IMFMediaType_Release( input_type );
|
|
IMFMediaType_Release( output_type );
|
|
return r;
|
|
}
|
|
|
|
static LRESULT IV50_Decompress( IMFTransform *decoder, ICDECOMPRESS *icd, DWORD size )
|
|
{
|
|
IMFSample *in_sample = NULL, *out_sample = NULL;
|
|
IMFMediaBuffer *in_buf = NULL, *out_buf = NULL;
|
|
MFT_OUTPUT_DATA_BUFFER mft_buf;
|
|
DWORD mft_status;
|
|
BYTE *data;
|
|
HRESULT hr;
|
|
LRESULT r = ICERR_INTERNAL;
|
|
|
|
TRACE("ICM_DECOMPRESS %p %p %lu\n", decoder, icd, size);
|
|
|
|
if ( FAILED(MFCreateSample( &in_sample )) )
|
|
return ICERR_INTERNAL;
|
|
|
|
if ( FAILED(MFCreateMemoryBuffer( icd->lpbiInput->biSizeImage, &in_buf )) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFSample_AddBuffer( in_sample, in_buf )) )
|
|
goto done;
|
|
|
|
if ( FAILED(MFCreateSample( &out_sample )) )
|
|
goto done;
|
|
|
|
if ( FAILED(MFCreateMemoryBuffer( icd->lpbiOutput->biSizeImage, &out_buf )) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFSample_AddBuffer( out_sample, out_buf )) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFMediaBuffer_Lock( in_buf, &data, NULL, NULL )))
|
|
goto done;
|
|
|
|
memcpy( data, icd->lpInput, icd->lpbiInput->biSizeImage );
|
|
|
|
if ( FAILED(IMFMediaBuffer_Unlock( in_buf )) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFMediaBuffer_SetCurrentLength( in_buf, icd->lpbiInput->biSizeImage )) )
|
|
goto done;
|
|
|
|
if ( FAILED(IMFTransform_ProcessInput( decoder, 0, in_sample, 0 )) )
|
|
goto done;
|
|
|
|
memset( &mft_buf, 0, sizeof(mft_buf) );
|
|
mft_buf.pSample = out_sample;
|
|
|
|
hr = IMFTransform_ProcessOutput( decoder, 0, 1, &mft_buf, &mft_status );
|
|
if ( SUCCEEDED(hr) && (mft_status & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) )
|
|
hr = IMFTransform_ProcessOutput( decoder, 0, 1, &mft_buf, &mft_status );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
LONG width = icd->lpbiOutput->biWidth * (icd->lpbiOutput->biBitCount / 8);
|
|
LONG height = abs( icd->lpbiOutput->biHeight );
|
|
LONG stride = (width + 3) & ~3;
|
|
BYTE *output = (BYTE *)icd->lpOutput;
|
|
|
|
if ( FAILED(IMFMediaBuffer_Lock( out_buf, &data, NULL, NULL )))
|
|
goto done;
|
|
|
|
MFCopyImage( output, stride, data, stride, width, height );
|
|
|
|
IMFMediaBuffer_Unlock( out_buf );
|
|
r = ICERR_OK;
|
|
}
|
|
else if ( hr == MF_E_TRANSFORM_NEED_MORE_INPUT )
|
|
{
|
|
TRACE("no output received.\n");
|
|
r = ICERR_OK;
|
|
}
|
|
|
|
done:
|
|
if ( in_buf )
|
|
IMFMediaBuffer_Release( in_buf );
|
|
if ( in_sample )
|
|
IMFSample_Release( in_sample );
|
|
if ( out_buf )
|
|
IMFMediaBuffer_Release( out_buf );
|
|
if ( out_sample )
|
|
IMFSample_Release( out_sample );
|
|
|
|
return r;
|
|
}
|
|
|
|
static LRESULT IV50_GetInfo( ICINFO *icinfo, DWORD dwSize )
|
|
{
|
|
TRACE("ICM_GETINFO %p %lu\n", icinfo, dwSize);
|
|
|
|
if ( !icinfo ) return sizeof(ICINFO);
|
|
if ( dwSize < sizeof(ICINFO) ) return 0;
|
|
|
|
icinfo->dwSize = sizeof(ICINFO);
|
|
icinfo->fccType = ICTYPE_VIDEO;
|
|
icinfo->fccHandler = IV50_MAGIC;
|
|
icinfo->dwFlags = 0;
|
|
icinfo->dwVersion = ICVERSION;
|
|
icinfo->dwVersionICM = ICVERSION;
|
|
|
|
LoadStringW( IR50_32_hModule, IDS_NAME, icinfo->szName, ARRAY_SIZE(icinfo->szName) );
|
|
LoadStringW( IR50_32_hModule, IDS_DESCRIPTION, icinfo->szDescription, ARRAY_SIZE(icinfo->szDescription) );
|
|
/* msvfw32 will fill icinfo->szDriver for us */
|
|
|
|
return sizeof(ICINFO);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DriverProc (IR50_32.@)
|
|
*/
|
|
LRESULT WINAPI IV50_DriverProc( DWORD_PTR dwDriverId, HDRVR hdrvr, UINT msg,
|
|
LPARAM lParam1, LPARAM lParam2 )
|
|
{
|
|
IMFTransform *decoder = (IMFTransform *) dwDriverId;
|
|
LRESULT r = ICERR_UNSUPPORTED;
|
|
|
|
TRACE("%Id %p %04x %08Ix %08Ix\n", dwDriverId, hdrvr, msg, lParam1, lParam2);
|
|
|
|
switch( msg )
|
|
{
|
|
case DRV_LOAD:
|
|
TRACE("DRV_LOAD\n");
|
|
r = 1;
|
|
break;
|
|
|
|
case DRV_OPEN:
|
|
r = IV50_Open((ICINFO *)lParam2);
|
|
break;
|
|
|
|
case DRV_CLOSE:
|
|
TRACE("DRV_CLOSE\n");
|
|
if ( decoder )
|
|
IMFTransform_Release( decoder );
|
|
r = 1;
|
|
break;
|
|
|
|
case DRV_ENABLE:
|
|
case DRV_DISABLE:
|
|
case DRV_FREE:
|
|
break;
|
|
|
|
case ICM_GETINFO:
|
|
r = IV50_GetInfo( (ICINFO *) lParam1, (DWORD) lParam2 );
|
|
break;
|
|
|
|
case ICM_DECOMPRESS_QUERY:
|
|
r = IV50_DecompressQuery( (LPBITMAPINFO) lParam1, (LPBITMAPINFO) lParam2 );
|
|
break;
|
|
|
|
case ICM_DECOMPRESS_GET_FORMAT:
|
|
r = IV50_DecompressGetFormat( (LPBITMAPINFO) lParam1, (LPBITMAPINFO) lParam2 );
|
|
break;
|
|
|
|
case ICM_DECOMPRESS_GET_PALETTE:
|
|
FIXME("ICM_DECOMPRESS_GET_PALETTE\n");
|
|
break;
|
|
|
|
case ICM_DECOMPRESS:
|
|
r = IV50_Decompress( decoder, (ICDECOMPRESS *) lParam1, (DWORD) lParam2 );
|
|
break;
|
|
|
|
case ICM_DECOMPRESS_BEGIN:
|
|
r = IV50_DecompressBegin( decoder, (LPBITMAPINFO) lParam1, (LPBITMAPINFO) lParam2 );
|
|
break;
|
|
|
|
case ICM_DECOMPRESS_END:
|
|
r = ICERR_UNSUPPORTED;
|
|
break;
|
|
|
|
case ICM_DECOMPRESSEX_QUERY:
|
|
FIXME("ICM_DECOMPRESSEX_QUERY\n");
|
|
break;
|
|
|
|
case ICM_DECOMPRESSEX:
|
|
FIXME("ICM_DECOMPRESSEX\n");
|
|
break;
|
|
|
|
case ICM_COMPRESS_QUERY:
|
|
r = ICERR_BADFORMAT;
|
|
/* fall through */
|
|
case ICM_COMPRESS_GET_FORMAT:
|
|
case ICM_COMPRESS_END:
|
|
case ICM_COMPRESS:
|
|
FIXME("compression not implemented\n");
|
|
break;
|
|
|
|
case ICM_CONFIGURE:
|
|
break;
|
|
|
|
default:
|
|
FIXME("Unknown message: %04x %Id %Id\n", msg, lParam1, lParam2);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* DllMain
|
|
*/
|
|
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
|
|
{
|
|
TRACE("(%p,%lu,%p)\n", hModule, dwReason, lpReserved);
|
|
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls(hModule);
|
|
IR50_32_hModule = hModule;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|