Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/strmbase/seeking.c | 53 +++++++++++++++++++-------------- dlls/winegstreamer/gstdemux.c | 5 ++-- dlls/wineqtdecoder/qtsplitter.c | 3 +- include/wine/strmbase.h | 7 +++-- 4 files changed, 40 insertions(+), 28 deletions(-)
diff --git a/dlls/strmbase/seeking.c b/dlls/strmbase/seeking.c index 8a773b51d5..d87e4f8516 100644 --- a/dlls/strmbase/seeking.c +++ b/dlls/strmbase/seeking.c @@ -18,7 +18,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -/* FIXME: critical sections */
#include "strmbase_private.h"
@@ -29,7 +28,9 @@ static inline SourceSeeking *impl_from_IMediaSeeking(IMediaSeeking *iface) return CONTAINING_RECORD(iface, SourceSeeking, IMediaSeeking_iface); }
-HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl, SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, SourceSeeking_ChangeRate fnChangeRate, PCRITICAL_SECTION crit_sect) +HRESULT strmbase_seeking_init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl, + SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, + SourceSeeking_ChangeRate fnChangeRate) { assert(fnChangeStop && fnChangeStart && fnChangeRate);
@@ -48,10 +49,16 @@ HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtb pSeeking->llDuration = pSeeking->llStop; pSeeking->dRate = 1.0; pSeeking->timeformat = TIME_FORMAT_MEDIA_TIME; - pSeeking->crst = crit_sect; + InitializeCriticalSection(&pSeeking->cs); + pSeeking->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SourceSeeking.cs"); return S_OK; }
+void strmbase_seeking_cleanup(SourceSeeking *seeking) +{ + DeleteCriticalSection(&seeking->cs); +} + HRESULT WINAPI SourceSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities) { SourceSeeking *This = impl_from_IMediaSeeking(iface); @@ -104,9 +111,9 @@ HRESULT WINAPI SourceSeekingImpl_GetTimeFormat(IMediaSeeking * iface, GUID * pFo SourceSeeking *This = impl_from_IMediaSeeking(iface); TRACE("(%s)\n", debugstr_guid(pFormat));
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); *pFormat = This->timeformat; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -118,10 +125,10 @@ HRESULT WINAPI SourceSeekingImpl_IsUsingTimeFormat(IMediaSeeking * iface, const
TRACE("(%s)\n", debugstr_guid(pFormat));
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); if (!IsEqualIID(pFormat, &This->timeformat)) hr = S_FALSE; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return hr; } @@ -140,9 +147,9 @@ HRESULT WINAPI SourceSeekingImpl_GetDuration(IMediaSeeking * iface, LONGLONG * p
TRACE("(%p)\n", pDuration);
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); *pDuration = This->llDuration; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -153,9 +160,9 @@ HRESULT WINAPI SourceSeekingImpl_GetStopPosition(IMediaSeeking * iface, LONGLONG
TRACE("(%p)\n", pStop);
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); *pStop = This->llStop; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -167,9 +174,9 @@ HRESULT WINAPI SourceSeekingImpl_GetCurrentPosition(IMediaSeeking * iface, LONGL
TRACE("(%p)\n", pCurrent);
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); *pCurrent = This->llCurrent; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -214,7 +221,7 @@ HRESULT WINAPI SourceSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG * LONGLONG llNewCurrent, llNewStop;
TRACE("(%p, %x, %p, %x)\n", pCurrent, dwCurrentFlags, pStop, dwStopFlags); - EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs);
llNewCurrent = Adjust(This->llCurrent, pCurrent, dwCurrentFlags); llNewStop = Adjust(This->llStop, pStop, dwStopFlags); @@ -233,7 +240,7 @@ HRESULT WINAPI SourceSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG * *pCurrent = llNewCurrent; if (pStop && (dwStopFlags & AM_SEEKING_ReturnTime)) *pStop = llNewStop; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
if (bChangeCurrent) This->fnChangeStart(iface); @@ -249,10 +256,10 @@ HRESULT WINAPI SourceSeekingImpl_GetPositions(IMediaSeeking * iface, LONGLONG *
TRACE("(%p, %p)\n", pCurrent, pStop);
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); IMediaSeeking_GetCurrentPosition(iface, pCurrent); IMediaSeeking_GetStopPosition(iface, pStop); - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -263,10 +270,10 @@ HRESULT WINAPI SourceSeekingImpl_GetAvailable(IMediaSeeking * iface, LONGLONG *
TRACE("(%p, %p)\n", pEarliest, pLatest);
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); *pEarliest = 0; *pLatest = This->llDuration; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } @@ -285,11 +292,11 @@ HRESULT WINAPI SourceSeekingImpl_SetRate(IMediaSeeking * iface, double dRate) return VFW_E_UNSUPPORTED_AUDIO; }
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); This->dRate = dRate; if (bChangeRate) hr = This->fnChangeRate(iface); - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return hr; } @@ -300,10 +307,10 @@ HRESULT WINAPI SourceSeekingImpl_GetRate(IMediaSeeking * iface, double * dRate)
TRACE("(%p)\n", dRate);
- EnterCriticalSection(This->crst); + EnterCriticalSection(&This->cs); /* Forward? */ *dRate = This->dRate; - LeaveCriticalSection(This->crst); + LeaveCriticalSection(&This->cs);
return S_OK; } diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 479077b48f..5fa9d66c93 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -1884,6 +1884,7 @@ static void free_source_pin(struct gstdemux_source *pin) FreeMediaType(&pin->mt); gst_segment_free(pin->segment);
+ strmbase_seeking_cleanup(&pin->seek); strmbase_source_cleanup(&pin->pin); heap_free(pin); } @@ -1936,8 +1937,8 @@ static struct gstdemux_source *create_pin(struct gstdemux *filter, const WCHAR * 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); + strmbase_seeking_init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop, + GST_ChangeCurrent, GST_ChangeRate); BaseFilterImpl_IncrementPinVersion(&filter->filter);
sprintf(pad_name, "qz_sink_%u", filter->cStreams); diff --git a/dlls/wineqtdecoder/qtsplitter.c b/dlls/wineqtdecoder/qtsplitter.c index fbec10a912..81e18d3420 100644 --- a/dlls/wineqtdecoder/qtsplitter.c +++ b/dlls/wineqtdecoder/qtsplitter.c @@ -340,7 +340,8 @@ IUnknown * CALLBACK QTSplitter_create(IUnknown *outer, HRESULT *phr) This->pInputPin.pin.peer = NULL; This->pInputPin.pin.pFuncsTable = &sink_ops;
- SourceSeeking_Init(&This->sourceSeeking, &QT_Seeking_Vtbl, QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate, &This->filter.csFilter); + strmbase_seeking_init(&This->sourceSeeking, &QT_Seeking_Vtbl, + QTSplitter_ChangeStop, QTSplitter_ChangeStart, QTSplitter_ChangeRate);
*phr = S_OK; return &This->filter.IUnknown_inner; diff --git a/include/wine/strmbase.h b/include/wine/strmbase.h index b08303f93d..7949e6670a 100644 --- a/include/wine/strmbase.h +++ b/include/wine/strmbase.h @@ -272,10 +272,13 @@ typedef struct SourceSeeking double dRate; LONGLONG llCurrent, llStop, llDuration; GUID timeformat; - PCRITICAL_SECTION crst; + CRITICAL_SECTION cs; } SourceSeeking;
-HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl, SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, SourceSeeking_ChangeRate fnChangeRate, PCRITICAL_SECTION crit_sect); +HRESULT strmbase_seeking_init(SourceSeeking *seeking, const IMediaSeekingVtbl *vtbl, + SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, + SourceSeeking_ChangeRate fnChangeRate); +void strmbase_seeking_cleanup(SourceSeeking *seeking);
HRESULT WINAPI SourceSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities); HRESULT WINAPI SourceSeekingImpl_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities);
These may be called from the streaming thread, so it's not safe to do so.
Nor does it seem necessary. We expect that no streaming thread should ever call methods on our pad or pin, and as long as we hold the filter lock and wait for the no-more-pads signal when connecting or starting the stream, we cannot race with application threads.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 5fa9d66c93..9eddf78ced 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -814,13 +814,17 @@ static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
TRACE("%p %p %p\n", This, bin, pad);
- EnterCriticalSection(&This->filter.csFilter); for (x = 0; x < This->cStreams; ++x) { if (This->ppPins[x]->their_src == pad) break; } if (x == This->cStreams) - goto out; + { + char *name = gst_pad_get_name(pad); + WARN("No pin matching pad %s found.\n", debugstr_a(name)); + g_free(name); + return; + }
pin = This->ppPins[x];
@@ -831,9 +835,6 @@ static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
gst_object_unref(pin->their_src); pin->their_src = NULL; -out: - TRACE("Removed %i/%i\n", x, This->cStreams); - LeaveCriticalSection(&This->filter.csFilter); }
static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux *This) @@ -971,7 +972,6 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user) return; }
- EnterCriticalSection(&This->filter.csFilter); for (x = 0; x < This->cStreams; ++x) { struct gstdemux_source *pin = This->ppPins[x]; if (!pin->their_src) { @@ -986,13 +986,11 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user) pin->their_src = pad; gst_object_ref(pin->their_src); TRACE("Relinked\n"); - LeaveCriticalSection(&This->filter.csFilter); return; } } } init_new_decoded_pad(bin, pad, This); - LeaveCriticalSection(&This->filter.csFilter); }
static gboolean query_function(GstPad *pad, GstObject *parent, GstQuery *query)
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index 9eddf78ced..c9a32e35a9 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -1431,8 +1431,17 @@ static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart) }
EnterCriticalSection(&This->filter.csFilter); + + if (This->no_more_pads_event) + ResetEvent(This->no_more_pads_event); + gst_element_set_state(This->container, GST_STATE_PLAYING);
+ /* Make sure that all of our pads are connected before returning, lest we + * e.g. try to seek and fail. */ + if (This->no_more_pads_event) + WaitForSingleObject(This->no_more_pads_event, INFINITE); + for (i = 0; i < This->cStreams; i++) { hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin); if (SUCCEEDED(hr)) {
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gstdemux.c | 255 ++++++++++++++++------------------ 1 file changed, 122 insertions(+), 133 deletions(-)
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index c9a32e35a9..da9bca6d1c 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -88,11 +88,6 @@ struct gstdemux_source SourceSeeking seek; };
-static inline struct gstdemux *impl_from_IBaseFilter(IBaseFilter *iface) -{ - return CONTAINING_RECORD(iface, struct gstdemux, filter.IBaseFilter_iface); -} - static inline struct gstdemux *impl_from_strmbase_filter(struct strmbase_filter *iface) { return CONTAINING_RECORD(iface, struct gstdemux, filter); @@ -1247,10 +1242,123 @@ static void gstdemux_destroy(struct strmbase_filter *iface) heap_free(filter); }
+static HRESULT gstdemux_init_stream(struct strmbase_filter *iface) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + HRESULT hr = VFW_E_NOT_CONNECTED, pin_hr; + GstStateChangeReturn ret; + unsigned int i; + + if (!filter->container) + return VFW_E_NOT_CONNECTED; + + if (filter->no_more_pads_event) + ResetEvent(filter->no_more_pads_event); + + if ((ret = gst_element_set_state(filter->container, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to pause stream.\n"); + return E_FAIL; + } + + /* Make sure that all of our pads are connected before returning, lest we + * e.g. try to seek and fail. */ + if (filter->no_more_pads_event) + WaitForSingleObject(filter->no_more_pads_event, INFINITE); + + for (i = 0; i < filter->cStreams; ++i) + { + if (SUCCEEDED(pin_hr = BaseOutputPinImpl_Active(&filter->ppPins[i]->pin))) + hr = pin_hr; + } + return hr; +} + +static HRESULT gstdemux_start_stream(struct strmbase_filter *iface, REFERENCE_TIME time) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return VFW_E_NOT_CONNECTED; + + if ((ret = gst_element_set_state(filter->container, GST_STATE_PLAYING)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play stream.\n"); + return E_FAIL; + } + else if (ret == GST_STATE_CHANGE_ASYNC) + return S_FALSE; + return S_OK; +} + +static HRESULT gstdemux_stop_stream(struct strmbase_filter *iface) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return VFW_E_NOT_CONNECTED; + + if ((ret = gst_element_set_state(filter->container, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to pause stream.\n"); + return E_FAIL; + } + else if (ret == GST_STATE_CHANGE_ASYNC) + return S_FALSE; + return S_OK; +} + +static HRESULT gstdemux_cleanup_stream(struct strmbase_filter *iface) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return S_OK; + + filter->ignore_flush = TRUE; + if ((ret = gst_element_set_state(filter->container, GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to pause stream.\n"); + return E_FAIL; + } + gst_element_get_state(filter->container, NULL, NULL, GST_CLOCK_TIME_NONE); + filter->ignore_flush = FALSE; + + return S_OK; +} + +static HRESULT gstdemux_wait_state(struct strmbase_filter *iface, DWORD timeout) +{ + struct gstdemux *filter = impl_from_strmbase_filter(iface); + GstStateChangeReturn ret; + + if (!filter->container) + return S_OK; + + ret = gst_element_get_state(filter->container, NULL, NULL, + timeout == INFINITE ? GST_CLOCK_TIME_NONE : timeout * 1000); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to get state.\n"); + return E_FAIL; + } + else if (ret == GST_STATE_CHANGE_ASYNC) + return VFW_S_STATE_INTERMEDIATE; + return S_OK; +} + static const struct strmbase_filter_ops filter_ops = { .filter_get_pin = gstdemux_get_pin, .filter_destroy = gstdemux_destroy, + .filter_init_stream = gstdemux_init_stream, + .filter_start_stream = gstdemux_start_stream, + .filter_stop_stream = gstdemux_stop_stream, + .filter_cleanup_stream = gstdemux_cleanup_stream, + .filter_wait_state = gstdemux_wait_state, };
static HRESULT sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) @@ -1360,139 +1468,15 @@ IUnknown * CALLBACK Gstreamer_Splitter_create(IUnknown *outer, HRESULT *phr) return &object->filter.IUnknown_inner; }
-static HRESULT WINAPI GST_Stop(IBaseFilter *iface) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - - TRACE("(%p)\n", This); - - mark_wine_thread(); - - if (This->container) { - 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; - } - return S_OK; -} - -static HRESULT WINAPI GST_Pause(IBaseFilter *iface) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - HRESULT hr = S_OK; - GstState now; - GstStateChangeReturn ret; - - TRACE("(%p)\n", This); - - if (!This->container) - return VFW_E_NOT_CONNECTED; - - mark_wine_thread(); - - gst_element_get_state(This->container, &now, NULL, -1); - if (now == GST_STATE_PAUSED) - return S_OK; - if (now != GST_STATE_PLAYING) - hr = IBaseFilter_Run(iface, -1); - if (FAILED(hr)) - return hr; - ret = gst_element_set_state(This->container, GST_STATE_PAUSED); - if (ret == GST_STATE_CHANGE_ASYNC) - hr = S_FALSE; - return hr; -} - -static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - HRESULT hr = S_OK; - ULONG i; - GstState now; - HRESULT hr_any = VFW_E_NOT_CONNECTED; - - TRACE("(%p)->(%s)\n", This, wine_dbgstr_longlong(tStart)); - - mark_wine_thread(); - - if (!This->container) - return VFW_E_NOT_CONNECTED; - - gst_element_get_state(This->container, &now, NULL, -1); - if (now == GST_STATE_PLAYING) - return S_OK; - if (now == GST_STATE_PAUSED) { - GstStateChangeReturn ret; - ret = gst_element_set_state(This->container, GST_STATE_PLAYING); - if (ret == GST_STATE_CHANGE_ASYNC) - return S_FALSE; - return S_OK; - } - - EnterCriticalSection(&This->filter.csFilter); - - if (This->no_more_pads_event) - ResetEvent(This->no_more_pads_event); - - gst_element_set_state(This->container, GST_STATE_PLAYING); - - /* Make sure that all of our pads are connected before returning, lest we - * e.g. try to seek and fail. */ - if (This->no_more_pads_event) - WaitForSingleObject(This->no_more_pads_event, INFINITE); - - for (i = 0; i < This->cStreams; i++) { - hr = BaseOutputPinImpl_Active(&This->ppPins[i]->pin); - if (SUCCEEDED(hr)) { - hr_any = hr; - } - } - hr = hr_any; - LeaveCriticalSection(&This->filter.csFilter); - - return hr; -} - -static HRESULT WINAPI GST_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState) -{ - struct gstdemux *This = impl_from_IBaseFilter(iface); - HRESULT hr = S_OK; - GstState now, pending; - GstStateChangeReturn ret; - - TRACE("(%p)->(%d, %p)\n", This, dwMilliSecsTimeout, pState); - - mark_wine_thread(); - - if (!This->container) { - *pState = State_Stopped; - return S_OK; - } - - ret = gst_element_get_state(This->container, &now, &pending, dwMilliSecsTimeout == INFINITE ? -1 : dwMilliSecsTimeout * 1000); - - if (ret == GST_STATE_CHANGE_ASYNC) - hr = VFW_S_STATE_INTERMEDIATE; - else - pending = now; - - switch (pending) { - case GST_STATE_PAUSED: *pState = State_Paused; return hr; - case GST_STATE_PLAYING: *pState = State_Running; return hr; - default: *pState = State_Stopped; return hr; - } -} - static const IBaseFilterVtbl GST_Vtbl = { BaseFilterImpl_QueryInterface, BaseFilterImpl_AddRef, BaseFilterImpl_Release, BaseFilterImpl_GetClassID, - GST_Stop, - GST_Pause, - GST_Run, - GST_GetState, + BaseFilterImpl_Stop, + BaseFilterImpl_Pause, + BaseFilterImpl_Run, + BaseFilterImpl_GetState, BaseFilterImpl_SetSyncSource, BaseFilterImpl_GetSyncSource, BaseFilterImpl_EnumPins, @@ -2632,6 +2616,11 @@ static const struct strmbase_filter_ops mpeg_splitter_ops = .filter_query_interface = mpeg_splitter_query_interface, .filter_get_pin = gstdemux_get_pin, .filter_destroy = gstdemux_destroy, + .filter_init_stream = gstdemux_init_stream, + .filter_start_stream = gstdemux_start_stream, + .filter_stop_stream = gstdemux_stop_stream, + .filter_cleanup_stream = gstdemux_cleanup_stream, + .filter_wait_state = gstdemux_wait_state, };
IUnknown * CALLBACK mpeg_splitter_create(IUnknown *outer, HRESULT *phr)