2010-12-03 14:33:08 +01:00
|
|
|
/*
|
|
|
|
* QuickTime Toolkit decoder filter for video
|
|
|
|
*
|
|
|
|
* Copyright 2010 Aric Stewart, 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 "config.h"
|
|
|
|
|
|
|
|
#define ULONG CoreFoundation_ULONG
|
|
|
|
#define HRESULT CoreFoundation_HRESULT
|
|
|
|
|
|
|
|
#define LoadResource __carbon_LoadResource
|
|
|
|
#define CompareString __carbon_CompareString
|
|
|
|
#define GetCurrentThread __carbon_GetCurrentThread
|
|
|
|
#define GetCurrentProcess __carbon_GetCurrentProcess
|
|
|
|
#define AnimatePalette __carbon_AnimatePalette
|
|
|
|
#define EqualRgn __carbon_EqualRgn
|
|
|
|
#define FillRgn __carbon_FillRgn
|
|
|
|
#define FrameRgn __carbon_FrameRgn
|
|
|
|
#define GetPixel __carbon_GetPixel
|
|
|
|
#define InvertRgn __carbon_InvertRgn
|
|
|
|
#define LineTo __carbon_LineTo
|
|
|
|
#define OffsetRgn __carbon_OffsetRgn
|
|
|
|
#define PaintRgn __carbon_PaintRgn
|
|
|
|
#define Polygon __carbon_Polygon
|
|
|
|
#define ResizePalette __carbon_ResizePalette
|
|
|
|
#define SetRectRgn __carbon_SetRectRgn
|
|
|
|
|
|
|
|
#define CheckMenuItem __carbon_CheckMenuItem
|
|
|
|
#define DeleteMenu __carbon_DeleteMenu
|
|
|
|
#define DrawMenuBar __carbon_DrawMenuBar
|
|
|
|
#define EnableMenuItem __carbon_EnableMenuItem
|
|
|
|
#define EqualRect __carbon_EqualRect
|
|
|
|
#define FillRect __carbon_FillRect
|
|
|
|
#define FrameRect __carbon_FrameRect
|
|
|
|
#define GetCursor __carbon_GetCursor
|
|
|
|
#define GetMenu __carbon_GetMenu
|
|
|
|
#define InvertRect __carbon_InvertRect
|
|
|
|
#define IsWindowVisible __carbon_IsWindowVisible
|
|
|
|
#define MoveWindow __carbon_MoveWindow
|
|
|
|
#define OffsetRect __carbon_OffsetRect
|
|
|
|
#define PtInRect __carbon_PtInRect
|
|
|
|
#define SetCursor __carbon_SetCursor
|
|
|
|
#define SetRect __carbon_SetRect
|
|
|
|
#define ShowCursor __carbon_ShowCursor
|
|
|
|
#define ShowWindow __carbon_ShowWindow
|
|
|
|
#define UnionRect __carbon_UnionRect
|
|
|
|
|
|
|
|
#include <QuickTime/ImageCompression.h>
|
|
|
|
#include <CoreVideo/CVPixelBuffer.h>
|
|
|
|
|
|
|
|
#undef LoadResource
|
|
|
|
#undef CompareString
|
|
|
|
#undef GetCurrentThread
|
|
|
|
#undef _CDECL
|
|
|
|
#undef GetCurrentProcess
|
|
|
|
#undef AnimatePalette
|
|
|
|
#undef EqualRgn
|
|
|
|
#undef FillRgn
|
|
|
|
#undef FrameRgn
|
|
|
|
#undef GetPixel
|
|
|
|
#undef InvertRgn
|
|
|
|
#undef LineTo
|
|
|
|
#undef OffsetRgn
|
|
|
|
#undef PaintRgn
|
|
|
|
#undef Polygon
|
|
|
|
#undef ResizePalette
|
|
|
|
#undef SetRectRgn
|
|
|
|
#undef CheckMenuItem
|
|
|
|
#undef DeleteMenu
|
|
|
|
#undef DrawMenuBar
|
|
|
|
#undef EnableMenuItem
|
|
|
|
#undef EqualRect
|
|
|
|
#undef FillRect
|
|
|
|
#undef FrameRect
|
|
|
|
#undef GetCursor
|
|
|
|
#undef GetMenu
|
|
|
|
#undef InvertRect
|
|
|
|
#undef IsWindowVisible
|
|
|
|
#undef MoveWindow
|
|
|
|
#undef OffsetRect
|
|
|
|
#undef PtInRect
|
|
|
|
#undef SetCursor
|
|
|
|
#undef SetRect
|
|
|
|
#undef ShowCursor
|
|
|
|
#undef ShowWindow
|
|
|
|
#undef UnionRect
|
|
|
|
|
|
|
|
#undef ULONG
|
|
|
|
#undef HRESULT
|
|
|
|
#undef STDMETHODCALLTYPE
|
|
|
|
|
|
|
|
#define COBJMACROS
|
|
|
|
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "wtypes.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "dshow.h"
|
|
|
|
|
|
|
|
#include "uuids.h"
|
|
|
|
#include "amvideo.h"
|
|
|
|
#include "strmif.h"
|
|
|
|
#include "vfwmsgs.h"
|
|
|
|
#include "vfw.h"
|
|
|
|
#include "dvdmedia.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "wine/unicode.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "wine/strmbase.h"
|
|
|
|
|
2011-04-05 19:55:53 +02:00
|
|
|
#include "qtprivate.h"
|
2020-03-08 20:05:10 +01:00
|
|
|
#include "wineqtdecoder_classes.h"
|
2011-04-05 19:55:53 +02:00
|
|
|
|
2010-12-03 14:33:08 +01:00
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(qtdecoder);
|
|
|
|
|
|
|
|
typedef struct QTVDecoderImpl
|
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
struct strmbase_filter filter;
|
|
|
|
CRITICAL_SECTION stream_cs;
|
|
|
|
|
2020-03-14 20:10:25 +01:00
|
|
|
AM_MEDIA_TYPE mt;
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
struct strmbase_source source;
|
|
|
|
IUnknown *seeking;
|
|
|
|
|
|
|
|
struct strmbase_sink sink;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
ImageDescriptionHandle hImageDescription;
|
|
|
|
CFMutableDictionaryRef outputBufferAttributes;
|
|
|
|
ICMDecompressionSessionRef decompressionSession;
|
|
|
|
HRESULT decodeHR;
|
|
|
|
|
|
|
|
DWORD outputSize;
|
|
|
|
} QTVDecoderImpl;
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
static inline QTVDecoderImpl *impl_from_strmbase_filter(struct strmbase_filter *iface)
|
2012-03-26 21:05:53 +02:00
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
return CONTAINING_RECORD(iface, QTVDecoderImpl, filter);
|
2012-03-26 21:05:53 +02:00
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
static HRESULT video_decoder_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
|
2012-03-26 21:05:53 +02:00
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface->filter);
|
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IMemInputPin))
|
|
|
|
*out = &filter->sink.IMemInputPin_iface;
|
|
|
|
else
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2010-12-03 14:33:08 +01:00
|
|
|
static void trackingCallback(
|
|
|
|
void *decompressionTrackingRefCon,
|
|
|
|
OSStatus result,
|
|
|
|
ICMDecompressionTrackingFlags decompressionTrackingFlags,
|
|
|
|
CVPixelBufferRef pixelBuffer,
|
|
|
|
TimeValue64 displayTime,
|
|
|
|
TimeValue64 displayDuration,
|
|
|
|
ICMValidTimeFlags validTimeFlags,
|
|
|
|
void *reserved,
|
|
|
|
void *sourceFrameRefCon )
|
|
|
|
{
|
|
|
|
QTVDecoderImpl *This = (QTVDecoderImpl*)decompressionTrackingRefCon;
|
|
|
|
IMediaSample *pSample = (IMediaSample*)sourceFrameRefCon;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
IMediaSample* pOutSample = NULL;
|
|
|
|
LPBYTE pbDstStream;
|
|
|
|
DWORD cbDstStream;
|
|
|
|
|
|
|
|
if (result != noErr)
|
|
|
|
{
|
|
|
|
ERR("Error from Codec, no frame decompressed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pixelBuffer)
|
|
|
|
{
|
|
|
|
ERR("No pixel buffer, no frame decompressed\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
EnterCriticalSection(&This->stream_cs);
|
|
|
|
hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->source, &pOutSample, NULL, NULL, 0);
|
2010-12-03 14:33:08 +01:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
ERR("Unable to get delivery buffer (%x)\n", hr);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IMediaSample_SetActualDataLength(pOutSample, 0);
|
|
|
|
assert(hr == S_OK);
|
|
|
|
|
|
|
|
hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
ERR("Unable to get pointer to buffer (%x)\n", hr);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cbDstStream = IMediaSample_GetSize(pOutSample);
|
|
|
|
if (cbDstStream < This->outputSize) {
|
|
|
|
ERR("Sample size is too small %d < %d\n", cbDstStream, This->outputSize);
|
|
|
|
hr = E_FAIL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2011-04-05 19:55:53 +02:00
|
|
|
hr = AccessPixelBufferPixels(pixelBuffer, pbDstStream);
|
|
|
|
if (FAILED(hr))
|
|
|
|
goto error;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
IMediaSample_SetActualDataLength(pOutSample, This->outputSize);
|
|
|
|
|
|
|
|
IMediaSample_SetPreroll(pOutSample, (IMediaSample_IsPreroll(pSample) == S_OK));
|
|
|
|
IMediaSample_SetDiscontinuity(pOutSample, (IMediaSample_IsDiscontinuity(pSample) == S_OK));
|
|
|
|
IMediaSample_SetSyncPoint(pOutSample, (IMediaSample_IsSyncPoint(pSample) == S_OK));
|
|
|
|
|
|
|
|
if (!validTimeFlags)
|
|
|
|
IMediaSample_SetTime(pOutSample, NULL, NULL);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LONGLONG tStart, tStop;
|
|
|
|
|
|
|
|
if (validTimeFlags & kICMValidTime_DisplayTimeStampIsValid)
|
|
|
|
tStart = displayTime;
|
|
|
|
else
|
|
|
|
tStart = 0;
|
|
|
|
if (validTimeFlags & kICMValidTime_DisplayDurationIsValid)
|
|
|
|
tStop = tStart + displayDuration;
|
|
|
|
else
|
|
|
|
tStop = tStart;
|
|
|
|
|
|
|
|
IMediaSample_SetTime(pOutSample, &tStart, &tStop);
|
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
hr = IMemInputPin_Receive(This->source.pMemInputPin, pOutSample);
|
2010-12-03 14:33:08 +01:00
|
|
|
if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
|
|
|
|
ERR("Error sending sample (%x)\n", hr);
|
|
|
|
|
|
|
|
error:
|
2020-03-11 04:03:30 +01:00
|
|
|
LeaveCriticalSection(&This->stream_cs);
|
2010-12-03 14:33:08 +01:00
|
|
|
if (pOutSample)
|
|
|
|
IMediaSample_Release(pOutSample);
|
|
|
|
|
|
|
|
This->decodeHR = hr;
|
|
|
|
}
|
|
|
|
|
2020-03-14 20:10:25 +01:00
|
|
|
static HRESULT WINAPI video_decoder_sink_Receive(struct strmbase_sink *iface, IMediaSample *pSample)
|
2010-12-03 14:33:08 +01:00
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
QTVDecoderImpl *This = impl_from_strmbase_filter(iface->pin.filter);
|
2010-12-03 14:33:08 +01:00
|
|
|
HRESULT hr;
|
|
|
|
DWORD cbSrcStream;
|
|
|
|
LPBYTE pbSrcStream;
|
|
|
|
|
|
|
|
ICMFrameTimeRecord frameTime = {{0}};
|
|
|
|
TimeValue time = 1;
|
|
|
|
TimeScale timeScale = 1;
|
|
|
|
OSStatus err = noErr;
|
|
|
|
LONGLONG tStart, tStop;
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
EnterCriticalSection(&This->stream_cs);
|
|
|
|
|
2010-12-03 14:33:08 +01:00
|
|
|
hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
ERR("Cannot get pointer to sample data (%x)\n", hr);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cbSrcStream = IMediaSample_GetActualDataLength(pSample);
|
|
|
|
|
|
|
|
if (IMediaSample_GetTime(pSample, &tStart, &tStop) != S_OK)
|
|
|
|
tStart = tStop = 0;
|
|
|
|
|
|
|
|
time = tStart;
|
|
|
|
|
|
|
|
frameTime.recordSize = sizeof(ICMFrameTimeRecord);
|
|
|
|
*(TimeValue64 *)&frameTime.value = tStart;
|
|
|
|
frameTime.scale = 1;
|
|
|
|
frameTime.rate = fixed1;
|
|
|
|
frameTime.duration = tStop - tStart;
|
|
|
|
frameTime.frameNumber = 0;
|
|
|
|
frameTime.flags = icmFrameTimeIsNonScheduledDisplayTime;
|
|
|
|
|
|
|
|
err = ICMDecompressionSessionDecodeFrame(This->decompressionSession,
|
|
|
|
(UInt8 *)pbSrcStream, cbSrcStream, NULL, &frameTime, pSample);
|
|
|
|
|
|
|
|
if (err != noErr)
|
|
|
|
{
|
|
|
|
ERR("Error with ICMDecompressionSessionDecodeFrame\n");
|
|
|
|
hr = E_FAIL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ICMDecompressionSessionSetNonScheduledDisplayTime(This->decompressionSession, time, timeScale, 0);
|
|
|
|
ICMDecompressionSessionFlush(This->decompressionSession);
|
|
|
|
hr = This->decodeHR;
|
|
|
|
|
|
|
|
error:
|
2020-03-11 04:03:30 +01:00
|
|
|
LeaveCriticalSection(&This->stream_cs);
|
2010-12-03 14:33:08 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
static HRESULT video_decoder_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *pmt)
|
2010-12-03 14:33:08 +01:00
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
QTVDecoderImpl *This = impl_from_strmbase_filter(iface->pin.filter);
|
2010-12-03 14:33:08 +01:00
|
|
|
HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
|
|
|
|
OSErr err = noErr;
|
2020-03-11 04:03:30 +01:00
|
|
|
AM_MEDIA_TYPE *outpmt = &This->mt;
|
2010-12-03 14:33:08 +01:00
|
|
|
CFNumberRef n = NULL;
|
|
|
|
|
|
|
|
FreeMediaType(outpmt);
|
|
|
|
CopyMediaType(outpmt, pmt);
|
|
|
|
|
|
|
|
if (This->hImageDescription)
|
|
|
|
DisposeHandle((Handle)This->hImageDescription);
|
|
|
|
|
|
|
|
This->hImageDescription = (ImageDescriptionHandle)
|
|
|
|
NewHandleClear(sizeof(ImageDescription));
|
|
|
|
|
|
|
|
if (This->hImageDescription != NULL)
|
|
|
|
{
|
|
|
|
(**This->hImageDescription).idSize = sizeof(ImageDescription);
|
|
|
|
(**This->hImageDescription).spatialQuality = codecNormalQuality;
|
|
|
|
(**This->hImageDescription).frameCount = 1;
|
|
|
|
(**This->hImageDescription).clutID = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERR("Failed to create ImageDescription\n");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check root (GUID w/o FOURCC) */
|
|
|
|
if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) &&
|
|
|
|
(!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Video)+4, sizeof(GUID)-4)))
|
|
|
|
{
|
|
|
|
VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)outpmt->pbFormat;
|
|
|
|
VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)outpmt->pbFormat;
|
|
|
|
BITMAPINFOHEADER *bmi;
|
|
|
|
OSType fourCC;
|
|
|
|
DecompressorComponent dc;
|
|
|
|
OSType format;
|
2011-04-05 19:55:53 +02:00
|
|
|
DWORD outputWidth, outputHeight, outputDepth;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo))
|
|
|
|
bmi = &format1->bmiHeader;
|
|
|
|
else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2))
|
|
|
|
bmi = &format2->bmiHeader;
|
|
|
|
else
|
|
|
|
goto failed;
|
|
|
|
|
|
|
|
TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt->subtype.Data1, 4));
|
2011-03-28 18:22:46 +02:00
|
|
|
fourCC = ((const char *)&pmt->subtype.Data1)[3] |
|
|
|
|
(((const char *)&pmt->subtype.Data1)[2]<<8) |
|
|
|
|
(((const char *)&pmt->subtype.Data1)[1]<<16) |
|
|
|
|
(((const char *)&pmt->subtype.Data1)[0]<<24);
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
err = FindCodec(fourCC,NULL,NULL,&dc);
|
|
|
|
if (err != noErr || dc == 0x0)
|
|
|
|
{
|
|
|
|
TRACE("Codec not found\n");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
2011-04-05 19:55:53 +02:00
|
|
|
outputWidth = bmi->biWidth;
|
|
|
|
outputHeight = bmi->biHeight;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
(**This->hImageDescription).cType = fourCC;
|
2011-04-05 19:55:53 +02:00
|
|
|
(**This->hImageDescription).width = outputWidth;
|
|
|
|
(**This->hImageDescription).height = outputHeight;
|
2010-12-03 14:33:08 +01:00
|
|
|
(**This->hImageDescription).depth = bmi->biBitCount;
|
|
|
|
(**This->hImageDescription).hRes = 72<<16;
|
|
|
|
(**This->hImageDescription).vRes = 72<<16;
|
|
|
|
|
|
|
|
if (This->outputBufferAttributes)
|
|
|
|
CFRelease(This->outputBufferAttributes);
|
|
|
|
|
|
|
|
This->outputBufferAttributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
|
|
if (!This->outputBufferAttributes)
|
|
|
|
{
|
|
|
|
ERR("Failed to create outputBufferAttributes\n");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
2011-04-05 19:55:53 +02:00
|
|
|
n = CFNumberCreate(NULL, kCFNumberIntType, &outputWidth);
|
2010-12-03 14:33:08 +01:00
|
|
|
CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferWidthKey, n);
|
|
|
|
CFRelease(n);
|
|
|
|
|
2011-04-05 19:55:53 +02:00
|
|
|
n = CFNumberCreate(NULL, kCFNumberIntType, &outputHeight);
|
2010-12-03 14:33:08 +01:00
|
|
|
CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferHeightKey, n);
|
|
|
|
CFRelease(n);
|
|
|
|
|
|
|
|
/* yes this looks wrong. but 32ARGB is 24 RGB with an alpha channel */
|
|
|
|
format = k32ARGBPixelFormat;
|
|
|
|
n = CFNumberCreate(NULL, kCFNumberIntType, &format);
|
|
|
|
CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferPixelFormatTypeKey, n);
|
|
|
|
CFRelease(n);
|
|
|
|
|
|
|
|
CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
|
|
|
|
CFDictionaryAddValue(This->outputBufferAttributes, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
|
|
|
|
|
2011-04-05 19:55:53 +02:00
|
|
|
outputDepth = 3;
|
|
|
|
This->outputSize = outputWidth * outputHeight * outputDepth;
|
2010-12-03 14:33:08 +01:00
|
|
|
bmi->biCompression = BI_RGB;
|
|
|
|
bmi->biBitCount = 24;
|
|
|
|
outpmt->subtype = MEDIASUBTYPE_RGB24;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
failed:
|
|
|
|
if (This->hImageDescription)
|
|
|
|
{
|
|
|
|
DisposeHandle((Handle)This->hImageDescription);
|
|
|
|
This->hImageDescription = NULL;
|
|
|
|
}
|
|
|
|
if (This->outputBufferAttributes)
|
|
|
|
{
|
|
|
|
CFRelease(This->outputBufferAttributes);
|
|
|
|
This->outputBufferAttributes = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("Connection refused\n");
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
static void video_decoder_sink_disconnect(struct strmbase_sink *iface)
|
2010-12-03 14:33:08 +01:00
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
QTVDecoderImpl *This = impl_from_strmbase_filter(iface->pin.filter);
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
if (This->hImageDescription)
|
|
|
|
DisposeHandle((Handle)This->hImageDescription);
|
|
|
|
if (This->outputBufferAttributes)
|
|
|
|
CFRelease(This->outputBufferAttributes);
|
|
|
|
|
|
|
|
This->hImageDescription = NULL;
|
|
|
|
This->outputBufferAttributes = NULL;
|
2020-03-11 04:03:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct strmbase_sink_ops sink_ops =
|
|
|
|
{
|
|
|
|
.base.pin_query_interface = video_decoder_sink_query_interface,
|
|
|
|
.pfnReceive = video_decoder_sink_Receive,
|
|
|
|
.sink_connect = video_decoder_sink_connect,
|
|
|
|
.sink_disconnect = video_decoder_sink_disconnect,
|
|
|
|
};
|
|
|
|
|
|
|
|
static HRESULT video_decoder_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface->filter);
|
2010-12-03 14:33:08 +01:00
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
if (IsEqualGUID(iid, &IID_IMediaSeeking))
|
|
|
|
return IUnknown_QueryInterface(filter->seeking, iid, out);
|
|
|
|
else
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
2010-12-03 14:33:08 +01:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
static HRESULT video_decoder_source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
|
2010-12-03 14:33:08 +01:00
|
|
|
{
|
2020-03-11 04:03:30 +01:00
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface->filter);
|
|
|
|
|
|
|
|
if (IsEqualGUID(&mt->majortype, &filter->mt.majortype)
|
|
|
|
&& (IsEqualGUID(&mt->subtype, &filter->mt.subtype)
|
|
|
|
|| IsEqualGUID(&filter->mt.subtype, &GUID_NULL)))
|
|
|
|
return S_OK;
|
|
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT video_decoder_source_get_media_type(struct strmbase_pin *iface,
|
|
|
|
unsigned int index, AM_MEDIA_TYPE *mt)
|
|
|
|
{
|
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface->filter);
|
|
|
|
|
|
|
|
if (index)
|
|
|
|
return VFW_S_NO_MORE_ITEMS;
|
|
|
|
CopyMediaType(mt, &filter->mt);
|
|
|
|
return S_OK;
|
|
|
|
}
|
2010-12-03 14:33:08 +01:00
|
|
|
|
2020-03-14 20:10:25 +01:00
|
|
|
static HRESULT WINAPI video_decoder_source_DecideBufferSize(struct strmbase_source *iface,
|
2020-03-11 04:03:30 +01:00
|
|
|
IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
|
|
|
|
{
|
|
|
|
QTVDecoderImpl *This = impl_from_strmbase_filter(iface->pin.filter);
|
|
|
|
ALLOCATOR_PROPERTIES actual;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
if (!ppropInputRequest->cbAlign)
|
|
|
|
ppropInputRequest->cbAlign = 1;
|
|
|
|
|
|
|
|
if (ppropInputRequest->cbBuffer < This->outputSize)
|
2012-01-16 18:35:13 +01:00
|
|
|
ppropInputRequest->cbBuffer = This->outputSize + ppropInputRequest->cbAlign;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
|
|
|
if (!ppropInputRequest->cBuffers)
|
|
|
|
ppropInputRequest->cBuffers = 1;
|
|
|
|
|
|
|
|
return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
|
|
|
|
}
|
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
static const struct strmbase_source_ops source_ops =
|
|
|
|
{
|
|
|
|
.base.pin_query_interface = video_decoder_source_query_interface,
|
|
|
|
.base.pin_query_accept = video_decoder_source_query_accept,
|
|
|
|
.base.pin_get_media_type = video_decoder_source_get_media_type,
|
|
|
|
.pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
|
|
|
|
.pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator,
|
|
|
|
.pfnDecideBufferSize = video_decoder_source_DecideBufferSize,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct strmbase_pin *video_decoder_get_pin(struct strmbase_filter *iface, unsigned int index)
|
|
|
|
{
|
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (index == 0)
|
|
|
|
return &filter->sink.pin;
|
|
|
|
else if (index == 1)
|
|
|
|
return &filter->source.pin;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void video_decoder_destroy(struct strmbase_filter *iface)
|
|
|
|
{
|
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (filter->sink.pin.peer)
|
|
|
|
IPin_Disconnect(filter->sink.pin.peer);
|
|
|
|
IPin_Disconnect(&filter->sink.pin.IPin_iface);
|
|
|
|
|
|
|
|
if (filter->source.pin.peer)
|
|
|
|
IPin_Disconnect(filter->source.pin.peer);
|
|
|
|
IPin_Disconnect(&filter->source.pin.IPin_iface);
|
|
|
|
|
|
|
|
strmbase_sink_cleanup(&filter->sink);
|
|
|
|
strmbase_source_cleanup(&filter->source);
|
|
|
|
|
|
|
|
filter->stream_cs.DebugInfo->Spare[0] = 0;
|
|
|
|
DeleteCriticalSection(&filter->stream_cs);
|
|
|
|
FreeMediaType(&filter->mt);
|
|
|
|
IUnknown_Release(filter->seeking);
|
|
|
|
strmbase_filter_cleanup(&filter->filter);
|
|
|
|
free(filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT video_decoder_init_stream(struct strmbase_filter *iface)
|
|
|
|
{
|
2020-11-17 18:37:53 +01:00
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface);
|
|
|
|
HRESULT hr;
|
2020-03-11 04:03:30 +01:00
|
|
|
|
|
|
|
OSErr err = noErr;
|
|
|
|
ICMDecompressionSessionOptionsRef sessionOptions = NULL;
|
|
|
|
ICMDecompressionTrackingCallbackRecord trackingCallbackRecord;
|
|
|
|
|
|
|
|
trackingCallbackRecord.decompressionTrackingCallback = trackingCallback;
|
2020-11-17 18:37:53 +01:00
|
|
|
trackingCallbackRecord.decompressionTrackingRefCon = filter;
|
2020-03-11 04:03:30 +01:00
|
|
|
|
2020-11-17 18:37:53 +01:00
|
|
|
err = ICMDecompressionSessionCreate(NULL, filter->hImageDescription, sessionOptions,
|
|
|
|
filter->outputBufferAttributes, &trackingCallbackRecord, &filter->decompressionSession);
|
2020-03-11 04:03:30 +01:00
|
|
|
|
|
|
|
if (err != noErr)
|
|
|
|
{
|
|
|
|
ERR("Error with ICMDecompressionSessionCreate %i\n",err);
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
2020-11-17 18:37:53 +01:00
|
|
|
if (filter->source.pin.peer && FAILED(hr = IMemAllocator_Commit(filter->source.pAllocator)))
|
|
|
|
ERR("Failed to commit allocator, hr %#x.\n", hr);
|
|
|
|
|
|
|
|
return S_OK;
|
2020-03-11 04:03:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT video_decoder_cleanup_stream(struct strmbase_filter *iface)
|
|
|
|
{
|
2020-11-17 18:37:53 +01:00
|
|
|
QTVDecoderImpl *filter = impl_from_strmbase_filter(iface);
|
|
|
|
|
|
|
|
if (filter->decompressionSession)
|
|
|
|
ICMDecompressionSessionRelease(filter->decompressionSession);
|
|
|
|
filter->decompressionSession = NULL;
|
2020-03-11 04:03:30 +01:00
|
|
|
|
2020-11-17 18:37:53 +01:00
|
|
|
if (filter->source.pin.peer)
|
|
|
|
IMemAllocator_Decommit(filter->source.pAllocator);
|
2020-03-11 04:03:30 +01:00
|
|
|
|
2020-11-17 18:37:53 +01:00
|
|
|
return S_OK;
|
2020-03-11 04:03:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct strmbase_filter_ops filter_ops =
|
|
|
|
{
|
|
|
|
.filter_get_pin = video_decoder_get_pin,
|
|
|
|
.filter_destroy = video_decoder_destroy,
|
|
|
|
.filter_init_stream = video_decoder_init_stream,
|
|
|
|
.filter_cleanup_stream = video_decoder_cleanup_stream,
|
2010-12-03 14:33:08 +01:00
|
|
|
};
|
|
|
|
|
2020-03-08 20:05:10 +01:00
|
|
|
HRESULT video_decoder_create(IUnknown *outer, IUnknown **out)
|
2010-12-03 14:33:08 +01:00
|
|
|
{
|
2020-05-01 19:36:22 +02:00
|
|
|
static const WCHAR inW[] = { 'I','n',0 };
|
|
|
|
static const WCHAR outW[] = { 'O','u','t',0 };
|
2020-03-08 20:05:10 +01:00
|
|
|
QTVDecoderImpl *object;
|
2010-12-03 14:33:08 +01:00
|
|
|
HRESULT hr;
|
2020-03-14 20:10:25 +01:00
|
|
|
ISeekingPassThru *passthrough;
|
2010-12-03 14:33:08 +01:00
|
|
|
|
2020-03-11 04:03:30 +01:00
|
|
|
if (!(object = calloc(1, sizeof(*object))))
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
strmbase_filter_init(&object->filter, outer, &CLSID_QTVDecoder, &filter_ops);
|
|
|
|
|
|
|
|
InitializeCriticalSection(&object->stream_cs);
|
|
|
|
object->stream_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": QTVDecoderImpl.stream_cs");
|
|
|
|
|
2020-05-01 19:36:22 +02:00
|
|
|
strmbase_sink_init(&object->sink, &object->filter, inW, &sink_ops, NULL);
|
2020-03-11 04:03:30 +01:00
|
|
|
|
2020-05-01 19:36:22 +02:00
|
|
|
strmbase_source_init(&object->source, &object->filter, outW, &source_ops);
|
2020-03-11 04:03:30 +01:00
|
|
|
|
|
|
|
if (FAILED(hr = CoCreateInstance(&CLSID_SeekingPassThru,
|
|
|
|
(IUnknown *)&object->source.pin.IPin_iface, CLSCTX_INPROC_SERVER,
|
|
|
|
&IID_IUnknown, (void **)&object->seeking)))
|
|
|
|
{
|
|
|
|
strmbase_sink_cleanup(&object->sink);
|
|
|
|
strmbase_source_cleanup(&object->source);
|
|
|
|
strmbase_filter_cleanup(&object->filter);
|
|
|
|
free(object);
|
2020-03-08 20:05:10 +01:00
|
|
|
return hr;
|
2020-03-11 04:03:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
IUnknown_QueryInterface(object->seeking, &IID_ISeekingPassThru, (void **)&passthrough);
|
|
|
|
ISeekingPassThru_Init(passthrough, FALSE, &object->sink.pin.IPin_iface);
|
|
|
|
ISeekingPassThru_Release(passthrough);
|
2010-12-03 14:33:08 +01:00
|
|
|
|
2020-03-08 20:05:10 +01:00
|
|
|
TRACE("Created video decoder %p.\n", object);
|
2020-03-14 20:10:25 +01:00
|
|
|
*out = &object->filter.IUnknown_inner;
|
2020-03-08 20:05:10 +01:00
|
|
|
return S_OK;
|
2010-12-03 14:33:08 +01:00
|
|
|
}
|