Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 40 +++++++++++++++-------------------- 1 file changed, 17 insertions(+), 23 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 2ad4b08a13b..a0c8bf6102b 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -104,7 +104,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 +775,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 +785,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 +792,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 +874,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); @@ -1784,28 +1769,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)
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 119 +++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 52 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index a0c8bf6102b..87a1deb6f90 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 @@ -1068,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); @@ -1088,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; } @@ -1223,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; @@ -1249,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);
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.
It is very rare for applications to try to use these filters directly; most applications use autoplugging methods (Connect(), RenderFile(), etc.) Two applications that do (in fact, the only two I am aware of) are Neocron 2 and LEGO Drome Racers; both use the MPEG-1 stream splitter, and attempt to decode audio and program streams respectively.
The benefits of these patches are the removal of a lot of old, very messy, and not particularly well functioning parsing code from quartz. A further benefit of moving the MPEG-1 splitter in specific is that it provides an easy way to add in program stream support, on top of the mpegpsdemux plugin (as well as video stream support on top of mpegvideoparse, though I sincerely doubt we'll ever find an application that requires this).
All of the required plugins are in the "good" set, which as far as I'm aware has full multiarch handling in all major Linux distributions. The mpegpsdemux and mpegvideoparse elements are in the "bad" set, and some distributions still don't fully support 32-bit or multiarch. That said, since we don't currently support PS or video streams, it's not a regression from the current situation.
On MacOS, wineqtdecoder will still be used for most applications. Only those very few which try to create the filters directly will suffer. Since it is also possible to install GStreamer on MacOS, I don't see any blocking problem, but I welcome those more familiar with Mac to point out problems that I overlooked.
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 | 109 ++++++++ dlls/winegstreamer/main.c | 50 ++++ 10 files changed, 173 insertions(+), 471 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 87a1deb6f90..1b4857a2a18 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -2172,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);