Signed-off-by: Zebediah Figura z.figura12@gmail.com --- As might be implied by this patch, I also have patches to replace the AVI splitter and the MPEG-1 splitter with avidemux and mpegaudioparse, respectively.
This of course means requiring GStreamer to decode all media files. I'm not particularly inclined to see this as a blocking problem, especially since the required plugins are all in the "good" set, which as far as I'm aware has full multiarch handling in all major distributions.
dlls/quartz/Makefile.in | 3 +- dlls/quartz/main.c | 1 - dlls/quartz/quartz_private.h | 1 - dlls/quartz/quartz_strmif.idl | 7 - dlls/quartz/regsvr.c | 19 -- dlls/quartz/tests/waveparser.c | 13 +- dlls/quartz/waveparser.c | 440 ------------------------------- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/gstdemux.c | 268 +++++++++++++------ dlls/winegstreamer/main.c | 50 ++++ 10 files changed, 257 insertions(+), 546 deletions(-) delete mode 100644 dlls/quartz/waveparser.c
diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index 3824efbfa98..7963e212fc5 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -23,8 +23,7 @@ C_SRCS = \ regsvr.c \ systemclock.c \ videorenderer.c \ - vmr9.c \ - waveparser.c + vmr9.c
RC_SRCS = quartz.rc
diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c index 2c7284de13b..115fc806fc7 100644 --- a/dlls/quartz/main.c +++ b/dlls/quartz/main.c @@ -80,7 +80,6 @@ static const struct object_creation_info object_creation[] = { &CLSID_AVIDec, AVIDec_create }, { &CLSID_SystemClock, QUARTZ_CreateSystemClock }, { &CLSID_ACMWrapper, ACMWrapper_create }, - { &CLSID_WAVEParser, WAVEParser_create } };
static HRESULT WINAPI DSCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj) diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h index 874b29f2b12..d43e445bd04 100644 --- a/dlls/quartz/quartz_private.h +++ b/dlls/quartz/quartz_private.h @@ -63,7 +63,6 @@ HRESULT VideoRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN HRESULT VideoRendererDefault_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN; HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN; HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN; -HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN; HRESULT VMR7Impl_create(IUnknown *pUnkOuter, LPVOID *ppv) DECLSPEC_HIDDEN; HRESULT VMR9Impl_create(IUnknown *pUnkOuter, LPVOID *ppv) DECLSPEC_HIDDEN;
diff --git a/dlls/quartz/quartz_strmif.idl b/dlls/quartz/quartz_strmif.idl index ab1af89ac42..c84e3aea5cb 100644 --- a/dlls/quartz/quartz_strmif.idl +++ b/dlls/quartz/quartz_strmif.idl @@ -140,13 +140,6 @@ coclass VideoRendererDefault { interface IBaseFilter; } ] coclass ACMWrapper { interface IBaseFilter; }
-[ - helpstring("Wave Parser"), - threading(both), - uuid(d51bd5a1-7548-11cf-a520-0080c77ef58a) -] -coclass WAVEParser { interface IBaseFilter; } - [ helpstring("Video Mixing Renderer"), threading(both), diff --git a/dlls/quartz/regsvr.c b/dlls/quartz/regsvr.c index 44b2f60d138..eebca1c00a6 100644 --- a/dlls/quartz/regsvr.c +++ b/dlls/quartz/regsvr.c @@ -354,25 +354,6 @@ static struct regsvr_filter const filter_list[] = { { 0xFFFFFFFF }, } }, - { &CLSID_WAVEParser, - &CLSID_LegacyAmFilterCategory, - {'W','a','v','e',' ','P','a','r','s','e','r',0}, - 0x400000, - { { 0, - { { &MEDIATYPE_Stream, &MEDIASUBTYPE_WAVE }, - { &MEDIATYPE_Stream, &MEDIASUBTYPE_AU }, - { &MEDIATYPE_Stream, &MEDIASUBTYPE_AIFF }, - { NULL } - }, - }, - { REG_PINFLAG_B_OUTPUT, - { { &MEDIATYPE_Audio, &GUID_NULL }, - { NULL } - }, - }, - { 0xFFFFFFFF }, - } - }, { NULL } /* list terminator */ };
diff --git a/dlls/quartz/tests/waveparser.c b/dlls/quartz/tests/waveparser.c index 4bfa17a9e5f..2e4cc914618 100644 --- a/dlls/quartz/tests/waveparser.c +++ b/dlls/quartz/tests/waveparser.c @@ -148,7 +148,7 @@ static void test_interfaces(void) check_interface(pin, &IID_IKsPropertySet, FALSE); check_interface(pin, &IID_IMemInputPin, FALSE); check_interface(pin, &IID_IMediaPosition, FALSE); - todo_wine check_interface(pin, &IID_IMediaSeeking, FALSE); + check_interface(pin, &IID_IMediaSeeking, FALSE);
IPin_Release(pin);
@@ -157,6 +157,7 @@ static void test_interfaces(void) todo_wine check_interface(pin, &IID_IMediaPosition, TRUE); check_interface(pin, &IID_IMediaSeeking, TRUE); check_interface(pin, &IID_IPin, TRUE); + check_interface(pin, &IID_IQualityControl, TRUE); check_interface(pin, &IID_IUnknown, TRUE);
check_interface(pin, &IID_IAsyncReader, FALSE); @@ -762,8 +763,18 @@ static void test_enum_media_types(void)
START_TEST(waveparser) { + IBaseFilter *filter; + CoInitialize(NULL);
+ if (FAILED(CoCreateInstance(&CLSID_WAVEParser, NULL, CLSCTX_INPROC_SERVER, + &IID_IBaseFilter, (void **)&filter))) + { + skip("Failed to create WAVE parser.\n"); + return; + } + IBaseFilter_Release(filter); + test_interfaces(); test_aggregation(); test_enum_pins(); diff --git a/dlls/quartz/waveparser.c b/dlls/quartz/waveparser.c deleted file mode 100644 index 43bfff060b5..00000000000 --- a/dlls/quartz/waveparser.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * WAVE Parser Filter - * - * Copyright 2005 Christian Costa - * - * 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 "quartz_private.h" -#include "pin.h" - -#include "uuids.h" -#include "aviriff.h" -#include "vfwmsgs.h" -#include "mmsystem.h" - -#include "wine/debug.h" - -#include <math.h> -#include <assert.h> - -#include "parser.h" - -WINE_DEFAULT_DEBUG_CHANNEL(quartz); - -static const WCHAR outputW[] = {'o','u','t','p','u','t',0}; - -typedef struct WAVEParserImpl -{ - ParserImpl Parser; - LONGLONG StartOfFile; /* in media time */ - LONGLONG EndOfFile; - DWORD nAvgBytesPerSec; - DWORD nBlockAlign; -} WAVEParserImpl; - -static inline WAVEParserImpl *impl_from_IMediaSeeking( IMediaSeeking *iface ) -{ - return CONTAINING_RECORD(iface, WAVEParserImpl, Parser.sourceSeeking.IMediaSeeking_iface); -} - -static inline WAVEParserImpl *impl_from_strmbase_filter(struct strmbase_filter *iface) -{ - return CONTAINING_RECORD(iface, WAVEParserImpl, Parser.filter); -} - -static LONGLONG bytepos_to_duration(WAVEParserImpl *This, LONGLONG bytepos) -{ - LONGLONG duration = BYTES_FROM_MEDIATIME(bytepos - This->StartOfFile); - duration *= 10000000; - duration /= This->nAvgBytesPerSec; - - return duration; -} - -static LONGLONG duration_to_bytepos(WAVEParserImpl *This, LONGLONG duration) -{ - LONGLONG bytepos; - - bytepos = This->nAvgBytesPerSec; - bytepos *= duration; - bytepos /= 10000000; - bytepos -= bytepos % This->nBlockAlign; - bytepos += BYTES_FROM_MEDIATIME(This->StartOfFile); - - return MEDIATIME_FROM_BYTES(bytepos); -} - -static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie) -{ - WAVEParserImpl *This = iface; - LPBYTE pbSrcStream = NULL; - ULONG cbSrcStream = 0; - REFERENCE_TIME tStart, tStop; - HRESULT hr; - IMediaSample *newsample = NULL; - Parser_OutputPin *pOutputPin; - PullPin *pin = This->Parser.pInputPin; - - IMediaSample_GetPointer(pSample, &pbSrcStream); - hr = IMediaSample_GetTime(pSample, &tStart, &tStop); - - cbSrcStream = IMediaSample_GetActualDataLength(pSample); - - /* Flush occurring */ - if (cbSrcStream == 0) - { - TRACE(".. Why do I need you?\n"); - return S_OK; - } - - pOutputPin = This->Parser.sources[0]; - - if (SUCCEEDED(hr)) - hr = IMemAllocator_GetBuffer(pin->pAlloc, &newsample, NULL, NULL, 0); - - if (SUCCEEDED(hr)) - { - LONGLONG rtSampleStart = pin->rtNext; - /* Add 4 for the next header, which should hopefully work */ - LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(IMediaSample_GetSize(newsample)); - - if (rtSampleStop > pin->rtStop) - rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); - - IMediaSample_SetTime(newsample, &rtSampleStart, &rtSampleStop); - - pin->rtCurrent = pin->rtNext; - pin->rtNext = rtSampleStop; - - IMediaSample_SetPreroll(newsample, FALSE); - IMediaSample_SetDiscontinuity(newsample, FALSE); - IMediaSample_SetSyncPoint(newsample, TRUE); - - hr = IAsyncReader_Request(pin->pReader, newsample, 0); - } - - if (SUCCEEDED(hr)) - { - REFERENCE_TIME tAviStart, tAviStop; - - IMediaSample_SetSyncPoint(pSample, TRUE); - pOutputPin->dwSamplesProcessed++; - - tAviStart = bytepos_to_duration(This, tStart); - tAviStop = bytepos_to_duration(This, tStart + MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(pSample))); - - IMediaSample_SetTime(pSample, &tAviStart, &tAviStop); - - hr = BaseOutputPinImpl_Deliver(&pOutputPin->pin, pSample); - if (hr != S_OK && hr != S_FALSE && hr != VFW_E_WRONG_STATE) - ERR("Error sending sample (%x)\n", hr); - else if (hr != S_OK) - /* Unset progression if denied! */ - This->Parser.pInputPin->rtCurrent = tStart; - } - - if (tStop >= This->EndOfFile || (bytepos_to_duration(This, tStop) >= This->Parser.sourceSeeking.llStop) || hr == VFW_E_NOT_CONNECTED) - { - unsigned int i; - - TRACE("End of file reached\n"); - - for (i = 0; i < This->Parser.cStreams; i++) - { - IPin* ppin; - HRESULT hr; - - TRACE("Send End Of Stream to output pin %u\n", i); - - hr = IPin_ConnectedTo(&This->Parser.sources[i]->pin.pin.IPin_iface, &ppin); - if (SUCCEEDED(hr)) - { - hr = IPin_EndOfStream(ppin); - IPin_Release(ppin); - } - if (FAILED(hr)) - { - ERR("%x\n", hr); - break; - } - } - - /* Force the pullpin thread to stop */ - hr = S_FALSE; - } - - return hr; -} - -static HRESULT WAVEParser_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt) -{ - if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream)) - return S_FALSE; - if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_WAVE)) - return S_OK; - if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AU) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AIFF)) - FIXME("AU and AIFF files not supported yet!\n"); - return S_FALSE; -} - -static HRESULT WINAPI WAVEParserImpl_seek(IMediaSeeking *iface) -{ - WAVEParserImpl *This = impl_from_IMediaSeeking(iface); - PullPin *pPin = This->Parser.pInputPin; - LONGLONG newpos, curpos, endpos, bytepos; - IPin *peer; - - newpos = This->Parser.sourceSeeking.llCurrent; - curpos = bytepos_to_duration(This, pPin->rtCurrent); - endpos = bytepos_to_duration(This, This->EndOfFile); - bytepos = duration_to_bytepos(This, newpos); - - if (newpos > endpos) - { - WARN("Requesting position %s beyond end of stream %s\n", - wine_dbgstr_longlong(newpos), wine_dbgstr_longlong(endpos)); - return E_INVALIDARG; - } - - if (curpos/1000000 == newpos/1000000) - { - TRACE("Requesting position %s same as current position %s\n", - wine_dbgstr_longlong(newpos), wine_dbgstr_longlong(curpos)); - return S_OK; - } - - TRACE("Moving sound to %08u bytes!\n", (DWORD)BYTES_FROM_MEDIATIME(bytepos)); - - EnterCriticalSection(&pPin->thread_lock); - IPin_BeginFlush(&pPin->pin.IPin_iface); - - /* Make sure this is done while stopped, BeginFlush takes care of this */ - EnterCriticalSection(&This->Parser.filter.csFilter); - - if ((peer = This->Parser.sources[0]->pin.pin.pConnectedTo)) - IPin_NewSegment(peer, newpos, endpos, pPin->dRate); - - pPin->rtStart = pPin->rtCurrent = bytepos; - This->Parser.sources[0]->dwSamplesProcessed = 0; - LeaveCriticalSection(&This->Parser.filter.csFilter); - - TRACE("Done flushing\n"); - IPin_EndFlush(&pPin->pin.IPin_iface); - LeaveCriticalSection(&pPin->thread_lock); - - return S_OK; -} - -static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props) -{ - PullPin *This = impl_PullPin_from_IPin(iface); - HRESULT hr; - RIFFLIST list; - RIFFCHUNK chunk; - LONGLONG pos = 0; /* in bytes */ - AM_MEDIA_TYPE amt; - WAVEParserImpl *pWAVEParser = impl_from_strmbase_filter(This->pin.filter); - - hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list); - pos += sizeof(list); - - if (list.fcc != FOURCC_RIFF) - { - ERR("Input stream not a RIFF file\n"); - return E_FAIL; - } - if (list.cb > 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */ - { - ERR("Input stream violates RIFF spec\n"); - return E_FAIL; - } - if (list.fccListType != mmioFOURCC('W','A','V','E')) - { - ERR("Input stream not an WAVE RIFF file\n"); - return E_FAIL; - } - - hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk); - pos += sizeof(chunk); - if (chunk.fcc != mmioFOURCC('f','m','t',' ')) - { - ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR)&chunk.fcc); - return E_FAIL; - } - - amt.majortype = MEDIATYPE_Audio; - amt.formattype = FORMAT_WaveFormatEx; - amt.bFixedSizeSamples = TRUE; - amt.bTemporalCompression = FALSE; - amt.lSampleSize = 1; - amt.pUnk = NULL; - amt.cbFormat = max(chunk.cb, sizeof(WAVEFORMATEX)); - amt.pbFormat = CoTaskMemAlloc(amt.cbFormat); - memset(amt.pbFormat, 0, amt.cbFormat); - IAsyncReader_SyncRead(This->pReader, pos, chunk.cb, amt.pbFormat); - amt.subtype = MEDIATYPE_Audio; - amt.subtype.Data1 = ((WAVEFORMATEX*)amt.pbFormat)->wFormatTag; - - pos += chunk.cb; - hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk); - while (chunk.fcc != mmioFOURCC('d','a','t','a')) - { - FIXME("Ignoring %s chunk.\n", debugstr_fourcc(chunk.fcc)); - pos += sizeof(chunk) + chunk.cb; - hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk); - if (hr != S_OK) - return E_FAIL; - } - - pWAVEParser->StartOfFile = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFCHUNK)); - pWAVEParser->EndOfFile = MEDIATIME_FROM_BYTES(pos + chunk.cb + sizeof(RIFFCHUNK)); - - props->cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign; - props->cbPrefix = 0; - props->cbBuffer = 4096; - props->cBuffers = 3; - pWAVEParser->nBlockAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign; - pWAVEParser->nAvgBytesPerSec = ((WAVEFORMATEX*)amt.pbFormat)->nAvgBytesPerSec; - hr = Parser_AddPin(&pWAVEParser->Parser, outputW, props, &amt); - CoTaskMemFree(amt.pbFormat); - - pWAVEParser->Parser.sourceSeeking.llCurrent = 0; - pWAVEParser->Parser.sourceSeeking.llStop = pWAVEParser->Parser.sourceSeeking.llDuration = bytepos_to_duration(pWAVEParser, pWAVEParser->EndOfFile); - TRACE("Duration: %u seconds\n", (DWORD)(pWAVEParser->Parser.sourceSeeking.llDuration / (LONGLONG)10000000)); - - This->rtStop = pWAVEParser->EndOfFile; - This->rtStart = pWAVEParser->StartOfFile; - - TRACE("WAVE File ok\n"); - - return hr; -} - -static HRESULT WAVEParser_Cleanup(LPVOID iface) -{ - WAVEParserImpl *This = iface; - - TRACE("(%p)->()\n", This); - - return S_OK; -} - -static HRESULT WAVEParser_first_request(LPVOID iface) -{ - WAVEParserImpl *This = iface; - PullPin *pin = This->Parser.pInputPin; - HRESULT hr; - IMediaSample *sample; - - if (pin->rtCurrent >= pin->rtStop) - { - /* Last sample has already been queued, request nothing more */ - TRACE("Done!\n"); - return S_OK; - } - - hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0); - - pin->rtNext = pin->rtCurrent; - if (SUCCEEDED(hr)) - { - LONGLONG rtSampleStart = pin->rtNext; - /* Add 4 for the next header, which should hopefully work */ - LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(IMediaSample_GetSize(sample)); - - if (rtSampleStop > pin->rtStop) - rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); - - IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop); - - pin->rtCurrent = pin->rtNext; - pin->rtNext = rtSampleStop; - - IMediaSample_SetPreroll(sample, FALSE); - if (!This->Parser.sources[0]->dwSamplesProcessed++) - IMediaSample_SetDiscontinuity(sample, TRUE); - else - IMediaSample_SetDiscontinuity(sample, FALSE); - - hr = IAsyncReader_Request(pin->pReader, sample, 0); - } - if (FAILED(hr)) - ERR("Horsemen of the apocalypse came to bring error 0x%08x %p\n", hr, sample); - - return hr; -} - -static HRESULT WAVEParser_disconnect(LPVOID iface) -{ - /* TODO: Find and plug memory leaks */ - return S_OK; -} - -static const IBaseFilterVtbl WAVEParser_Vtbl = -{ - BaseFilterImpl_QueryInterface, - BaseFilterImpl_AddRef, - BaseFilterImpl_Release, - BaseFilterImpl_GetClassID, - Parser_Stop, - Parser_Pause, - Parser_Run, - Parser_GetState, - Parser_SetSyncSource, - BaseFilterImpl_GetSyncSource, - BaseFilterImpl_EnumPins, - BaseFilterImpl_FindPin, - BaseFilterImpl_QueryFilterInfo, - BaseFilterImpl_JoinFilterGraph, - BaseFilterImpl_QueryVendorInfo, -}; - -static void wave_parser_destroy(struct strmbase_filter *iface) -{ - WAVEParserImpl *filter = impl_from_strmbase_filter(iface); - Parser_Destroy(&filter->Parser); -} - -static const struct strmbase_filter_ops filter_ops = -{ - .filter_get_pin = parser_get_pin, - .filter_destroy = wave_parser_destroy, -}; - -HRESULT WAVEParser_create(IUnknown *outer, void **out) -{ - static const WCHAR sink_name[] = {'i','n','p','u','t',' ','p','i','n',0}; - HRESULT hr; - WAVEParserImpl * This; - - *out = NULL; - - /* Note: This memory is managed by the transform filter once created */ - This = CoTaskMemAlloc(sizeof(WAVEParserImpl)); - - hr = Parser_Create(&This->Parser, &WAVEParser_Vtbl, outer, &CLSID_WAVEParser, - &filter_ops, sink_name, WAVEParser_Sample, WAVEParser_QueryAccept, - WAVEParser_InputPin_PreConnect, WAVEParser_Cleanup, WAVEParser_disconnect, - WAVEParser_first_request, NULL, NULL, WAVEParserImpl_seek, NULL); - - if (FAILED(hr)) - return hr; - - *out = &This->Parser.filter.IUnknown_inner; - - return hr; -} diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 18d6eb8a8e8..392cfa849d3 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -43,6 +43,7 @@ IUnknown * CALLBACK Gstreamer_Mp3_create(IUnknown *pUnkOuter, HRESULT *phr); IUnknown * CALLBACK Gstreamer_YUV2RGB_create(IUnknown *pUnkOuter, HRESULT *phr); IUnknown * CALLBACK Gstreamer_YUV2ARGB_create(IUnknown *pUnkOuter, HRESULT *phr); IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *pUnkOuter, HRESULT *phr); +IUnknown * CALLBACK wave_parser_create(IUnknown *outer, HRESULT *phr) DECLSPEC_HIDDEN;
BOOL init_gstreamer(void) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 2ad4b08a13b..1b4857a2a18 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -68,6 +68,8 @@ struct gstdemux HANDLE no_more_pads_event;
HANDLE push_thread; + + BOOL (*init_gst)(struct gstdemux *filter); };
struct gstdemux_source @@ -104,7 +106,7 @@ static const IPinVtbl GST_InputPin_Vtbl; static const IBaseFilterVtbl GST_Vtbl; static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl;
-static BOOL create_pin(struct gstdemux *filter, const WCHAR *name); +static struct gstdemux_source *create_pin(struct gstdemux *filter, const WCHAR *name); static HRESULT GST_RemoveOutputPins(struct gstdemux *This); static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface); static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface); @@ -775,10 +777,8 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux * char *name; GstCaps *caps; GstStructure *arg; - GstPad *mypad; struct gstdemux_source *pin; int ret; - gchar my_name[1024]; WCHAR nameW[128];
TRACE("%p %p %p\n", This, bin, pad); @@ -787,8 +787,6 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux * MultiByteToWideChar(CP_UNIXCP, 0, name, -1, nameW, ARRAY_SIZE(nameW) - 1); nameW[ARRAY_SIZE(nameW) - 1] = 0; TRACE("Name: %s\n", name); - strcpy(my_name, "qz_sink_"); - strcat(my_name, name); g_free(name);
caps = gst_pad_query_caps(pad, NULL); @@ -796,29 +794,18 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux * arg = gst_caps_get_structure(caps, 0); typename = gst_structure_get_name(arg);
- mypad = gst_pad_new(my_name, GST_PAD_SINK); - gst_pad_set_chain_function(mypad, got_data_sink_wrapper); - gst_pad_set_event_function(mypad, event_sink_wrapper); - gst_pad_set_query_function(mypad, query_sink_wrapper); - if (strcmp(typename, "audio/x-raw") && strcmp(typename, "video/x-raw")) { FIXME("Unknown type '%s'\n", typename); return; }
- if (!create_pin(This, nameW)) + if (!(pin = create_pin(This, nameW))) { ERR("Failed to allocate memory.\n"); return; }
- pin = This->ppPins[This->cStreams - 1]; - gst_pad_set_element_private(mypad, pin); - pin->my_sink = mypad; - - gst_segment_init(pin->segment, GST_FORMAT_TIME); - if (!strcmp(typename, "video/x-raw")) { GstElement *vconv; @@ -889,9 +876,9 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux * goto exit; } } else - ret = gst_pad_link(pad, mypad); + ret = gst_pad_link(pad, pin->my_sink);
- gst_pad_set_active(mypad, 1); + gst_pad_set_active(pin->my_sink, 1);
exit: TRACE("Linking: %i\n", ret); @@ -1083,14 +1070,12 @@ static void unknown_type(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer u
static HRESULT GST_Connect(struct gstdemux *This, IPin *pConnectPin, ALLOCATOR_PROPERTIES *props) { - int ret, i; - LONGLONG avail, duration; + LONGLONG avail; GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( "quartz_src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); - GstElement *gstfilter;
This->props = *props; IAsyncReader_Length(This->reader, &This->filesize, &avail); @@ -1103,68 +1088,21 @@ static HRESULT GST_Connect(struct gstdemux *This, IPin *pConnectPin, ALLOCATOR_P This->container = gst_bin_new(NULL); gst_element_set_bus(This->container, This->bus);
- gstfilter = gst_element_factory_make("decodebin", NULL); - if (!gstfilter) { - ERR("Could not make source filter, are gstreamer-plugins-* installed for %u bits?\n", - 8 * (int)sizeof(void*)); - return E_FAIL; - } - - gst_bin_add(GST_BIN(This->container), gstfilter); - - g_signal_connect(gstfilter, "pad-added", G_CALLBACK(existing_new_pad_wrapper), This); - g_signal_connect(gstfilter, "pad-removed", G_CALLBACK(removed_decoded_pad_wrapper), This); - g_signal_connect(gstfilter, "autoplug-select", G_CALLBACK(autoplug_blacklist_wrapper), This); - g_signal_connect(gstfilter, "unknown-type", G_CALLBACK(unknown_type_wrapper), This); - This->my_src = gst_pad_new_from_static_template(&src_template, "quartz-src"); gst_pad_set_getrange_function(This->my_src, request_buffer_src_wrapper); gst_pad_set_query_function(This->my_src, query_function_wrapper); gst_pad_set_activatemode_function(This->my_src, activate_mode_wrapper); gst_pad_set_event_function(This->my_src, event_src_wrapper); gst_pad_set_element_private (This->my_src, This); - This->their_sink = gst_element_get_static_pad(gstfilter, "sink");
- g_signal_connect(gstfilter, "no-more-pads", G_CALLBACK(no_more_pads_wrapper), This); - ret = gst_pad_link(This->my_src, This->their_sink); - if (ret < 0) { - ERR("Returns: %i\n", ret); - return E_FAIL; - } This->start = This->nextofs = This->nextpullofs = This->stop = 0;
- /* Add initial pins */ This->initial = TRUE; - ResetEvent(This->no_more_pads_event); - gst_element_set_state(This->container, GST_STATE_PLAYING); - ret = gst_element_get_state(This->container, NULL, NULL, -1); - - if (ret == GST_STATE_CHANGE_FAILURE) - { - ERR("GStreamer failed to play stream\n"); + if (!This->init_gst(This)) return E_FAIL; - } - - WaitForSingleObject(This->no_more_pads_event, INFINITE); - - gst_pad_query_duration(This->ppPins[0]->their_src, GST_FORMAT_TIME, &duration); - for (i = 0; i < This->cStreams; ++i) - { - This->ppPins[i]->seek.llDuration = This->ppPins[i]->seek.llStop = duration / 100; - This->ppPins[i]->seek.llCurrent = 0; - if (!This->ppPins[i]->seek.llDuration) - This->ppPins[i]->seek.dwCapabilities = 0; - WaitForSingleObject(This->ppPins[i]->caps_event, INFINITE); - } - *props = This->props; - - This->ignore_flush = TRUE; - gst_element_set_state(This->container, GST_STATE_READY); - gst_element_get_state(This->container, NULL, NULL, -1); - This->ignore_flush = FALSE; - This->initial = FALSE;
+ *props = This->props; This->nextofs = This->nextpullofs = 0; return S_OK; } @@ -1238,6 +1176,67 @@ static const BasePinFuncTable sink_ops = .pfnGetMediaType = BasePinImpl_GetMediaType, };
+static BOOL gstdecoder_init_gst(struct gstdemux *filter) +{ + GstElement *element = gst_element_factory_make("decodebin", NULL); + LONGLONG duration; + unsigned int i; + int ret; + + if (!element) + { + ERR("Failed to create decodebin; are %u-bit GStreamer "base" plugins installed?\n", + 8 * (int)sizeof(void*)); + return FALSE; + } + + gst_bin_add(GST_BIN(filter->container), element); + + g_signal_connect(element, "pad-added", G_CALLBACK(existing_new_pad_wrapper), filter); + g_signal_connect(element, "pad-removed", G_CALLBACK(removed_decoded_pad_wrapper), filter); + g_signal_connect(element, "autoplug-select", G_CALLBACK(autoplug_blacklist_wrapper), filter); + g_signal_connect(element, "unknown-type", G_CALLBACK(unknown_type_wrapper), filter); + g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads_wrapper), filter); + + filter->their_sink = gst_element_get_static_pad(element, "sink"); + ResetEvent(filter->no_more_pads_event); + + if ((ret = gst_pad_link(filter->my_src, filter->their_sink)) < 0) + { + ERR("Failed to link pads, error %d.\n", ret); + return FALSE; + } + + gst_element_set_state(filter->container, GST_STATE_PLAYING); + ret = gst_element_get_state(filter->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play stream.\n"); + return FALSE; + } + + WaitForSingleObject(filter->no_more_pads_event, INFINITE); + + gst_pad_query_duration(filter->ppPins[0]->their_src, GST_FORMAT_TIME, &duration); + for (i = 0; i < filter->cStreams; ++i) + { + struct gstdemux_source *pin = filter->ppPins[i]; + + pin->seek.llDuration = pin->seek.llStop = duration / 100; + pin->seek.llCurrent = 0; + if (!pin->seek.llDuration) + pin->seek.dwCapabilities = 0; + WaitForSingleObject(pin->caps_event, INFINITE); + } + + filter->ignore_flush = TRUE; + gst_element_set_state(filter->container, GST_STATE_READY); + gst_element_get_state(filter->container, NULL, NULL, -1); + filter->ignore_flush = FALSE; + + return TRUE; +} + IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *outer, HRESULT *phr) { struct gstdemux *object; @@ -1264,6 +1263,7 @@ IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *outer, HRESULT *phr) lstrcpynW(object->sink.name, wcsInputPinName, ARRAY_SIZE(object->sink.name)); object->sink.IPin_iface.lpVtbl = &GST_InputPin_Vtbl; object->sink.pFuncsTable = &sink_ops; + object->init_gst = gstdecoder_init_gst; *phr = S_OK;
TRACE("Created GStreamer demuxer %p.\n", object); @@ -1784,28 +1784,37 @@ static const struct strmbase_source_ops source_ops = GSTOutPin_DecideAllocator, };
-static BOOL create_pin(struct gstdemux *filter, const WCHAR *name) +static struct gstdemux_source *create_pin(struct gstdemux *filter, const WCHAR *name) { struct gstdemux_source *pin, **new_array; + char pad_name[19];
if (!(new_array = heap_realloc(filter->ppPins, (filter->cStreams + 1) * sizeof(*new_array)))) - return FALSE; + return NULL; filter->ppPins = new_array;
if (!(pin = heap_alloc_zero(sizeof(*pin)))) - return FALSE; + return NULL;
strmbase_source_init(&pin->pin, &GST_OutputPin_Vtbl, &filter->filter, name, &source_ops); pin->caps_event = CreateEventW(NULL, FALSE, FALSE, NULL); pin->segment = gst_segment_new(); + gst_segment_init(pin->segment, GST_FORMAT_TIME); pin->IQualityControl_iface.lpVtbl = &GSTOutPin_QualityControl_Vtbl; SourceSeeking_Init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop, GST_ChangeCurrent, GST_ChangeRate, &filter->filter.csFilter); BaseFilterImpl_IncrementPinVersion(&filter->filter);
+ sprintf(pad_name, "qz_sink_%u", filter->cStreams); + pin->my_sink = gst_pad_new(pad_name, GST_PAD_SINK); + gst_pad_set_element_private(pin->my_sink, pin); + gst_pad_set_chain_function(pin->my_sink, got_data_sink_wrapper); + gst_pad_set_event_function(pin->my_sink, event_sink_wrapper); + gst_pad_set_query_function(pin->my_sink, query_sink_wrapper); + filter->ppPins[filter->cStreams++] = pin; - return TRUE; + return pin; }
static HRESULT GST_RemoveOutputPins(struct gstdemux *This) @@ -2163,3 +2172,112 @@ void start_dispatch_thread(void) pthread_key_create(&wine_gst_key, NULL); CloseHandle(CreateThread(NULL, 0, &dispatch_thread, NULL, 0, NULL)); } + +static HRESULT WINAPI wave_parser_sink_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE *mt) +{ + if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream)) + return S_FALSE; + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_WAVE)) + return S_OK; + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_AU) || IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_AIFF)) + FIXME("AU and AIFF files are not yet supported.\n"); + return S_FALSE; +} + +static const BasePinFuncTable wave_parser_sink_ops = +{ + .pfnCheckMediaType = wave_parser_sink_CheckMediaType, + .pfnGetMediaType = BasePinImpl_GetMediaType, +}; + +static BOOL wave_parser_init_gst(struct gstdemux *filter) +{ + static const WCHAR source_name[] = {'o','u','t','p','u','t',0}; + struct gstdemux_source *pin; + GstElement *element; + LONGLONG duration; + int ret; + + if (!(element = gst_element_factory_make("wavparse", NULL))) + { + ERR("Failed to create wavparse; are %u-bit GStreamer "good" plugins installed?\n", + 8 * (int)sizeof(void*)); + return FALSE; + } + + gst_bin_add(GST_BIN(filter->container), element); + + filter->their_sink = gst_element_get_static_pad(element, "sink"); + if ((ret = gst_pad_link(filter->my_src, filter->their_sink)) < 0) + { + ERR("Failed to link sink pads, error %d.\n", ret); + return FALSE; + } + + if (!(pin = create_pin(filter, source_name))) + return FALSE; + pin->their_src = gst_element_get_static_pad(element, "src"); + gst_object_ref(pin->their_src); + if ((ret = gst_pad_link(pin->their_src, pin->my_sink)) < 0) + { + ERR("Failed to link source pads, error %d.\n", ret); + return FALSE; + } + + gst_pad_set_active(pin->my_sink, 1); + gst_element_set_state(filter->container, GST_STATE_PAUSED); + ret = gst_element_get_state(filter->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play stream.\n"); + return FALSE; + } + + gst_pad_query_duration(pin->their_src, GST_FORMAT_TIME, &duration); + pin->seek.llDuration = pin->seek.llStop = duration / 100; + pin->seek.llCurrent = 0; + if (!pin->seek.llDuration) + pin->seek.dwCapabilities = 0; + + WaitForSingleObject(pin->caps_event, INFINITE); + + filter->ignore_flush = TRUE; + gst_element_set_state(filter->container, GST_STATE_READY); + gst_element_get_state(filter->container, NULL, NULL, -1); + filter->ignore_flush = FALSE; + + return TRUE; +} + +IUnknown * CALLBACK wave_parser_create(IUnknown *outer, HRESULT *phr) +{ + static const WCHAR sink_name[] = {'i','n','p','u','t',' ','p','i','n',0}; + struct gstdemux *object; + + if (!init_gstreamer()) + { + *phr = E_FAIL; + return NULL; + } + + mark_wine_thread(); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + { + *phr = E_OUTOFMEMORY; + return NULL; + } + + strmbase_filter_init(&object->filter, &GST_Vtbl, outer, &CLSID_WAVEParser, &filter_ops); + + object->sink.dir = PINDIR_INPUT; + object->sink.filter = &object->filter; + lstrcpynW(object->sink.name, sink_name, ARRAY_SIZE(object->sink.name)); + object->sink.IPin_iface.lpVtbl = &GST_InputPin_Vtbl; + object->sink.pFuncsTable = &wave_parser_sink_ops; + object->init_gst = wave_parser_init_gst; + *phr = S_OK; + + TRACE("Created WAVE parser %p.\n", object); + return &object->filter.IUnknown_inner; +} diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 4419a6fde54..8503f9c7aba 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -46,6 +46,8 @@ static const WCHAR wGstreamer_Mp3[] = {'G','S','t','r','e','a','m','e','r',' ','M','p','3',' ','f','i','l','t','e','r',0}; static const WCHAR wGstreamer_AudioConvert[] = {'G','S','t','r','e','a','m','e','r',' ','A','u','d','i','o','C','o','n','v','e','r','t',' ','f','i','l','t','e','r',0}; +static const WCHAR wave_parserW[] = +{'W','a','v','e',' ','P','a','r','s','e','r',0};
static WCHAR wNull[] = {'\0'};
@@ -180,6 +182,47 @@ AMOVIESETUP_FILTER const amfAudioConvert = amfAudioConvertPin };
+static const AMOVIESETUP_MEDIATYPE wave_parser_sink_type_data[] = +{ + {&MEDIATYPE_Stream, &MEDIASUBTYPE_WAVE}, + {&MEDIATYPE_Stream, &MEDIASUBTYPE_AU}, + {&MEDIATYPE_Stream, &MEDIASUBTYPE_AIFF}, +}; + +static const AMOVIESETUP_MEDIATYPE wave_parser_source_type_data[] = +{ + {&MEDIATYPE_Audio, &GUID_NULL}, +}; + +static const AMOVIESETUP_PIN wave_parser_pin_data[] = +{ + { + NULL, + FALSE, FALSE, FALSE, FALSE, + &GUID_NULL, + NULL, + ARRAY_SIZE(wave_parser_sink_type_data), + wave_parser_sink_type_data, + }, + { + NULL, + FALSE, FALSE, FALSE, FALSE, + &GUID_NULL, + NULL, + ARRAY_SIZE(wave_parser_source_type_data), + wave_parser_source_type_data, + }, +}; + +static const AMOVIESETUP_FILTER wave_parser_filter_data = +{ + &CLSID_WAVEParser, + wave_parserW, + MERIT_UNLIKELY, + ARRAY_SIZE(wave_parser_pin_data), + wave_parser_pin_data, +}; + FactoryTemplate const g_Templates[] = { { wGstreamer_Splitter, @@ -216,6 +259,13 @@ FactoryTemplate const g_Templates[] = { NULL, &amfAudioConvert, }, + { + wave_parserW, + &CLSID_WAVEParser, + wave_parser_create, + NULL, + &wave_parser_filter_data, + }, };
const int g_cTemplates = ARRAY_SIZE(g_Templates);