winegstreamer: Manage our own streaming thread.
This is a rather large and complex change. It comprises several parts: (1) Instead of directly sending EOS, segment, and sample events to the downstream DirectShow sink, first queue them in a local buffer (i.e. "pin->event"). (2) Spawn a separate thread per source pin (i.e. "stream_thread") which consumes said events and sends them downstream. (3) When flushing or stopping, explicitly wait for this thread to pause or stop (respectively). There are a few reasons for this: (1) It reduces the number of Unix -> PE callbacks we need to make, easing PE conversion. This is not a great advantage *a priori*, and may not be worth a similar dedicated "handler" thread for most modules, but winegstreamer is different—we were already marshalling these calls onto another thread, and now that marshalling can go away (almost). (2) Because GStreamer can only do pad negotiation (and hence autoplugging) while running (in contrast to DirectShow, which can do it while stopped), we currently have to renegotiate every time the pipeline is started. Most applications don't start the graph more than once, but even that requires two negotiations, and startup time is demonstrably too high. It would be nice to keep the graph in PAUSED state all of the time, but this is difficult to do without more fine-grained control over the streaming thread. [In particular, we cannot reliably wait for all samples to be delivered except by stopping the GStreamer pipeline.] Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
92ee89c9d3
commit
9c138562fe
|
@ -22,6 +22,7 @@
|
||||||
#define __GST_PRIVATE_INCLUDED__
|
#define __GST_PRIVATE_INCLUDED__
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
|
|
@ -60,6 +60,13 @@ struct parser
|
||||||
|
|
||||||
LONGLONG filesize;
|
LONGLONG filesize;
|
||||||
|
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
|
||||||
|
/* FIXME: It would be nice to avoid duplicating these with strmbase.
|
||||||
|
* However, synchronization is tricky; we need access to be protected by a
|
||||||
|
* separate lock. */
|
||||||
|
bool streaming;
|
||||||
|
|
||||||
BOOL initial, ignore_flush;
|
BOOL initial, ignore_flush;
|
||||||
GstElement *container;
|
GstElement *container;
|
||||||
GstPad *my_src, *their_sink;
|
GstPad *my_src, *their_sink;
|
||||||
|
@ -74,6 +81,24 @@ struct parser
|
||||||
HRESULT (*source_get_media_type)(struct parser_source *pin, unsigned int index, AM_MEDIA_TYPE *mt);
|
HRESULT (*source_get_media_type)(struct parser_source *pin, unsigned int index, AM_MEDIA_TYPE *mt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum parser_event_type
|
||||||
|
{
|
||||||
|
PARSER_EVENT_NONE = 0,
|
||||||
|
PARSER_EVENT_BUFFER,
|
||||||
|
PARSER_EVENT_EOS,
|
||||||
|
PARSER_EVENT_SEGMENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct parser_event
|
||||||
|
{
|
||||||
|
enum parser_event_type type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
GstBuffer *buffer;
|
||||||
|
GstEvent *segment;
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
struct parser_source
|
struct parser_source
|
||||||
{
|
{
|
||||||
struct strmbase_source pin;
|
struct strmbase_source pin;
|
||||||
|
@ -85,6 +110,11 @@ struct parser_source
|
||||||
GstSegment *segment;
|
GstSegment *segment;
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
SourceSeeking seek;
|
SourceSeeking seek;
|
||||||
|
|
||||||
|
CONDITION_VARIABLE event_cv, event_empty_cv, flushing_cv, flush_stop_cv;
|
||||||
|
bool flushing, thread_blocked;
|
||||||
|
struct parser_event event;
|
||||||
|
HANDLE thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct parser *impl_from_strmbase_filter(struct strmbase_filter *iface)
|
static inline struct parser *impl_from_strmbase_filter(struct strmbase_filter *iface)
|
||||||
|
@ -657,49 +687,62 @@ static gboolean event_src(GstPad *pad, GstObject *parent, GstEvent *event)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn queue_stream_event(struct parser_source *pin, const struct parser_event *event)
|
||||||
|
{
|
||||||
|
struct parser *filter = impl_from_strmbase_filter(pin->pin.pin.filter);
|
||||||
|
|
||||||
|
EnterCriticalSection(&filter->cs);
|
||||||
|
while (filter->streaming && !pin->flushing && pin->event.type != PARSER_EVENT_NONE)
|
||||||
|
SleepConditionVariableCS(&pin->event_empty_cv, &filter->cs, INFINITE);
|
||||||
|
if (!filter->streaming || pin->flushing)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
TRACE("Filter is flushing; discarding event.\n");
|
||||||
|
return GST_FLOW_FLUSHING;
|
||||||
|
}
|
||||||
|
pin->event = *event;
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
WakeConditionVariable(&pin->event_cv);
|
||||||
|
TRACE("Event queued.\n");
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean event_sink(GstPad *pad, GstObject *parent, GstEvent *event)
|
static gboolean event_sink(GstPad *pad, GstObject *parent, GstEvent *event)
|
||||||
{
|
{
|
||||||
struct parser_source *pin = gst_pad_get_element_private(pad);
|
struct parser_source *pin = gst_pad_get_element_private(pad);
|
||||||
|
struct parser *filter = impl_from_strmbase_filter(pin->pin.pin.filter);
|
||||||
|
|
||||||
TRACE("pin %p, type \"%s\".\n", pin, GST_EVENT_TYPE_NAME(event));
|
TRACE("pin %p, type \"%s\".\n", pin, GST_EVENT_TYPE_NAME(event));
|
||||||
|
|
||||||
switch (event->type) {
|
switch (event->type)
|
||||||
case GST_EVENT_SEGMENT: {
|
{
|
||||||
gdouble rate, applied_rate;
|
case GST_EVENT_SEGMENT:
|
||||||
gint64 stop, pos;
|
|
||||||
const GstSegment *segment;
|
|
||||||
|
|
||||||
gst_event_parse_segment(event, &segment);
|
|
||||||
|
|
||||||
pos = segment->position;
|
|
||||||
stop = segment->stop;
|
|
||||||
rate = segment->rate;
|
|
||||||
applied_rate = segment->applied_rate;
|
|
||||||
|
|
||||||
if (segment->format != GST_FORMAT_TIME)
|
|
||||||
{
|
|
||||||
FIXME("Unhandled format \"%s\".\n", gst_format_get_name(segment->format));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_segment_copy_into(segment, pin->segment);
|
|
||||||
|
|
||||||
pos /= 100;
|
|
||||||
|
|
||||||
if (stop > 0)
|
|
||||||
stop /= 100;
|
|
||||||
|
|
||||||
if (pin->pin.pin.peer)
|
if (pin->pin.pin.peer)
|
||||||
IPin_NewSegment(pin->pin.pin.peer, pos, stop, rate*applied_rate);
|
{
|
||||||
|
struct parser_event stream_event;
|
||||||
|
|
||||||
|
stream_event.type = PARSER_EVENT_SEGMENT;
|
||||||
|
stream_event.u.segment = event;
|
||||||
|
if (queue_stream_event(pin, &stream_event) == GST_FLOW_OK)
|
||||||
|
{
|
||||||
|
/* Transfer our reference to the event to the thread. */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case GST_EVENT_EOS:
|
case GST_EVENT_EOS:
|
||||||
if (pin->pin.pin.peer)
|
if (pin->pin.pin.peer)
|
||||||
IPin_EndOfStream(pin->pin.pin.peer);
|
{
|
||||||
|
struct parser_event stream_event;
|
||||||
|
|
||||||
|
stream_event.type = PARSER_EVENT_EOS;
|
||||||
|
queue_stream_event(pin, &stream_event);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
SetEvent(pin->eos_event);
|
SetEvent(pin->eos_event);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_EVENT_FLUSH_START:
|
case GST_EVENT_FLUSH_START:
|
||||||
if (impl_from_strmbase_filter(pin->pin.pin.filter)->ignore_flush) {
|
if (impl_from_strmbase_filter(pin->pin.pin.filter)->ignore_flush) {
|
||||||
/* gst-plugins-base prior to 1.7 contains a bug which causes
|
/* gst-plugins-base prior to 1.7 contains a bug which causes
|
||||||
|
@ -713,13 +756,53 @@ static gboolean event_sink(GstPad *pad, GstObject *parent, GstEvent *event)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pin->pin.pin.peer)
|
if (pin->pin.pin.peer)
|
||||||
|
{
|
||||||
IPin_BeginFlush(pin->pin.pin.peer);
|
IPin_BeginFlush(pin->pin.pin.peer);
|
||||||
|
|
||||||
|
EnterCriticalSection(&filter->cs);
|
||||||
|
|
||||||
|
pin->flushing = true;
|
||||||
|
WakeConditionVariable(&pin->event_cv);
|
||||||
|
WakeConditionVariable(&pin->event_empty_cv);
|
||||||
|
/* Wait for the thread to pause itself, to ensure that no stale
|
||||||
|
* samples are sent. */
|
||||||
|
while (!pin->thread_blocked)
|
||||||
|
SleepConditionVariableCS(&pin->flushing_cv, &filter->cs, INFINITE);
|
||||||
|
|
||||||
|
/* And flush out any buffered event. */
|
||||||
|
switch (pin->event.type)
|
||||||
|
{
|
||||||
|
case PARSER_EVENT_NONE:
|
||||||
|
case PARSER_EVENT_EOS:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSER_EVENT_BUFFER:
|
||||||
|
gst_buffer_unref(pin->event.u.buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSER_EVENT_SEGMENT:
|
||||||
|
gst_event_unref(pin->event.u.segment);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pin->event.type = PARSER_EVENT_NONE;
|
||||||
|
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_EVENT_FLUSH_STOP:
|
case GST_EVENT_FLUSH_STOP:
|
||||||
gst_segment_init(pin->segment, GST_FORMAT_TIME);
|
gst_segment_init(pin->segment, GST_FORMAT_TIME);
|
||||||
if (pin->pin.pin.peer)
|
if (pin->pin.pin.peer)
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&filter->cs);
|
||||||
|
pin->flushing = false;
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
WakeConditionVariable(&pin->flush_stop_cv);
|
||||||
|
|
||||||
IPin_EndFlush(pin->pin.pin.peer);
|
IPin_EndFlush(pin->pin.pin.peer);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_EVENT_CAPS:
|
case GST_EVENT_CAPS:
|
||||||
{
|
{
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
|
@ -729,6 +812,7 @@ static gboolean event_sink(GstPad *pad, GstObject *parent, GstEvent *event)
|
||||||
SetEvent(pin->caps_event);
|
SetEvent(pin->caps_event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
WARN("Ignoring \"%s\" event.\n", GST_EVENT_TYPE_NAME(event));
|
WARN("Ignoring \"%s\" event.\n", GST_EVENT_TYPE_NAME(event));
|
||||||
}
|
}
|
||||||
|
@ -806,6 +890,36 @@ static DWORD CALLBACK push_data(LPVOID iface)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn got_data_sink(GstPad *pad, GstObject *parent, GstBuffer *buffer)
|
||||||
|
{
|
||||||
|
struct parser_source *pin = gst_pad_get_element_private(pad);
|
||||||
|
struct parser *filter = impl_from_strmbase_filter(pin->pin.pin.filter);
|
||||||
|
struct parser_event stream_event;
|
||||||
|
GstFlowReturn ret;
|
||||||
|
|
||||||
|
TRACE("pad %p, pin %p, buffer %p.\n", pad, pin, buffer);
|
||||||
|
|
||||||
|
if (filter->initial)
|
||||||
|
{
|
||||||
|
gst_buffer_unref(buffer);
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pin->pin.pin.peer)
|
||||||
|
{
|
||||||
|
gst_buffer_unref(buffer);
|
||||||
|
return GST_FLOW_NOT_LINKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_event.type = PARSER_EVENT_BUFFER;
|
||||||
|
stream_event.u.buffer = buffer;
|
||||||
|
/* Transfer our reference to the buffer to the thread. */
|
||||||
|
if ((ret = queue_stream_event(pin, &stream_event)) != GST_FLOW_OK)
|
||||||
|
gst_buffer_unref(buffer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill and send a single IMediaSample. */
|
||||||
static HRESULT send_sample(struct parser_source *pin, IMediaSample *sample,
|
static HRESULT send_sample(struct parser_source *pin, IMediaSample *sample,
|
||||||
GstBuffer *buf, GstMapInfo *info, gsize offset, gsize size, DWORD bytes_per_second)
|
GstBuffer *buf, GstMapInfo *info, gsize offset, gsize size, DWORD bytes_per_second)
|
||||||
{
|
{
|
||||||
|
@ -869,21 +983,14 @@ static HRESULT send_sample(struct parser_source *pin, IMediaSample *sample,
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn got_data_sink(GstPad *pad, GstObject *parent, GstBuffer *buf)
|
/* Send a single GStreamer buffer (splitting it into multiple IMediaSamples if
|
||||||
|
* necessary). */
|
||||||
|
static void send_buffer(struct parser_source *pin, GstBuffer *buf)
|
||||||
{
|
{
|
||||||
struct parser_source *pin = gst_pad_get_element_private(pad);
|
HRESULT hr;
|
||||||
struct parser *This = impl_from_strmbase_filter(pin->pin.pin.filter);
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
IMediaSample *sample;
|
IMediaSample *sample;
|
||||||
GstMapInfo info;
|
GstMapInfo info;
|
||||||
|
|
||||||
TRACE("%p %p\n", pad, buf);
|
|
||||||
|
|
||||||
if (This->initial) {
|
|
||||||
gst_buffer_unref(buf);
|
|
||||||
return GST_FLOW_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_buffer_map(buf, &info, GST_MAP_READ);
|
gst_buffer_map(buf, &info, GST_MAP_READ);
|
||||||
|
|
||||||
if (IsEqualGUID(&pin->pin.pin.mt.formattype, &FORMAT_WaveFormatEx)
|
if (IsEqualGUID(&pin->pin.pin.mt.formattype, &FORMAT_WaveFormatEx)
|
||||||
|
@ -937,14 +1044,93 @@ static GstFlowReturn got_data_sink(GstPad *pad, GstObject *parent, GstBuffer *bu
|
||||||
gst_buffer_unmap(buf, &info);
|
gst_buffer_unmap(buf, &info);
|
||||||
|
|
||||||
gst_buffer_unref(buf);
|
gst_buffer_unref(buf);
|
||||||
|
}
|
||||||
|
|
||||||
if (hr == VFW_E_NOT_CONNECTED)
|
static DWORD CALLBACK stream_thread(void *arg)
|
||||||
return GST_FLOW_NOT_LINKED;
|
{
|
||||||
|
struct parser_source *pin = arg;
|
||||||
|
struct parser *filter = impl_from_strmbase_filter(pin->pin.pin.filter);
|
||||||
|
|
||||||
if (FAILED(hr))
|
TRACE("Starting streaming thread for pin %p.\n", pin);
|
||||||
return GST_FLOW_FLUSHING;
|
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
for (;;)
|
||||||
|
{
|
||||||
|
struct parser_event event;
|
||||||
|
|
||||||
|
EnterCriticalSection(&filter->cs);
|
||||||
|
|
||||||
|
while (filter->streaming && !pin->flushing && pin->event.type == PARSER_EVENT_NONE)
|
||||||
|
SleepConditionVariableCS(&pin->event_cv, &filter->cs, INFINITE);
|
||||||
|
|
||||||
|
if (pin->flushing)
|
||||||
|
{
|
||||||
|
TRACE("Filter is flushing; pausing thread.\n");
|
||||||
|
pin->thread_blocked = true;
|
||||||
|
WakeConditionVariable(&pin->flushing_cv);
|
||||||
|
do
|
||||||
|
SleepConditionVariableCS(&pin->flush_stop_cv, &filter->cs, INFINITE);
|
||||||
|
while (pin->flushing);
|
||||||
|
pin->thread_blocked = false;
|
||||||
|
TRACE("Filter is no longer flushing; resuming thread.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter->streaming)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pin->event.type)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
event = pin->event;
|
||||||
|
pin->event.type = PARSER_EVENT_NONE;
|
||||||
|
WakeConditionVariable(&pin->event_empty_cv);
|
||||||
|
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
|
||||||
|
TRACE("Got event of type %#x.\n", event.type);
|
||||||
|
|
||||||
|
switch (event.type)
|
||||||
|
{
|
||||||
|
case PARSER_EVENT_BUFFER:
|
||||||
|
send_buffer(pin, event.u.buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSER_EVENT_EOS:
|
||||||
|
IPin_EndOfStream(pin->pin.pin.peer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARSER_EVENT_SEGMENT:
|
||||||
|
{
|
||||||
|
const GstSegment *segment;
|
||||||
|
|
||||||
|
gst_event_parse_segment(event.u.segment, &segment);
|
||||||
|
|
||||||
|
if (segment->format != GST_FORMAT_TIME)
|
||||||
|
{
|
||||||
|
FIXME("Unhandled format \"%s\".\n", gst_format_get_name(segment->format));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_segment_copy_into(segment, pin->segment);
|
||||||
|
|
||||||
|
IPin_NewSegment(pin->pin.pin.peer, segment->position / 100,
|
||||||
|
segment->stop / 100, segment->rate * segment->applied_rate);
|
||||||
|
gst_event_unref(event.u.segment);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PARSER_EVENT_NONE:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("Streaming stopped; exiting.\n");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn request_buffer_src(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buffer)
|
static GstFlowReturn request_buffer_src(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buffer)
|
||||||
|
@ -1478,6 +1664,9 @@ static void parser_destroy(struct strmbase_filter *iface)
|
||||||
gst_bus_set_sync_handler(filter->bus, NULL, NULL, NULL);
|
gst_bus_set_sync_handler(filter->bus, NULL, NULL, NULL);
|
||||||
gst_object_unref(filter->bus);
|
gst_object_unref(filter->bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter->cs.DebugInfo->Spare[0] = 0;
|
||||||
|
DeleteCriticalSection(&filter->cs);
|
||||||
strmbase_sink_cleanup(&filter->sink);
|
strmbase_sink_cleanup(&filter->sink);
|
||||||
strmbase_filter_cleanup(&filter->filter);
|
strmbase_filter_cleanup(&filter->filter);
|
||||||
heap_free(filter);
|
heap_free(filter);
|
||||||
|
@ -1493,12 +1682,21 @@ static HRESULT parser_init_stream(struct strmbase_filter *iface)
|
||||||
if (!filter->container)
|
if (!filter->container)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
EnterCriticalSection(&filter->cs);
|
||||||
|
filter->streaming = true;
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
|
||||||
for (i = 0; i < filter->source_count; ++i)
|
for (i = 0; i < filter->source_count; ++i)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
if (filter->sources[i]->pin.pin.peer && FAILED(hr = IMemAllocator_Commit(filter->sources[i]->pin.pAllocator)))
|
if (!filter->sources[i]->pin.pin.peer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMemAllocator_Commit(filter->sources[i]->pin.pAllocator)))
|
||||||
ERR("Failed to commit allocator, hr %#x.\n", hr);
|
ERR("Failed to commit allocator, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
filter->sources[i]->thread = CreateThread(NULL, 0, stream_thread, filter->sources[i], 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter->no_more_pads_event)
|
if (filter->no_more_pads_event)
|
||||||
|
@ -1544,6 +1742,21 @@ static HRESULT parser_cleanup_stream(struct strmbase_filter *iface)
|
||||||
if (!filter->container)
|
if (!filter->container)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
EnterCriticalSection(&filter->cs);
|
||||||
|
filter->streaming = false;
|
||||||
|
LeaveCriticalSection(&filter->cs);
|
||||||
|
|
||||||
|
for (i = 0; i < filter->source_count; ++i)
|
||||||
|
{
|
||||||
|
struct parser_source *pin = filter->sources[i];
|
||||||
|
|
||||||
|
if (!pin->pin.pin.peer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WakeConditionVariable(&pin->event_cv);
|
||||||
|
WakeConditionVariable(&pin->event_empty_cv);
|
||||||
|
}
|
||||||
|
|
||||||
filter->ignore_flush = TRUE;
|
filter->ignore_flush = TRUE;
|
||||||
if ((ret = gst_element_set_state(filter->container, GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE)
|
if ((ret = gst_element_set_state(filter->container, GST_STATE_READY)) == GST_STATE_CHANGE_FAILURE)
|
||||||
{
|
{
|
||||||
|
@ -1555,8 +1768,16 @@ static HRESULT parser_cleanup_stream(struct strmbase_filter *iface)
|
||||||
|
|
||||||
for (i = 0; i < filter->source_count; ++i)
|
for (i = 0; i < filter->source_count; ++i)
|
||||||
{
|
{
|
||||||
if (filter->sources[i]->pin.pin.peer)
|
struct parser_source *pin = filter->sources[i];
|
||||||
IMemAllocator_Decommit(filter->sources[i]->pin.pAllocator);
|
|
||||||
|
if (!pin->pin.pin.peer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
IMemAllocator_Decommit(pin->pin.pAllocator);
|
||||||
|
|
||||||
|
WaitForSingleObject(pin->thread, INFINITE);
|
||||||
|
CloseHandle(pin->thread);
|
||||||
|
pin->thread = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -1797,6 +2018,13 @@ static BOOL parser_init_gstreamer(void)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parser_init_common(struct parser *object)
|
||||||
|
{
|
||||||
|
object->error_event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||||
|
InitializeCriticalSection(&object->cs);
|
||||||
|
object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": parser.cs");
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
|
HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
|
||||||
{
|
{
|
||||||
struct parser *object;
|
struct parser *object;
|
||||||
|
@ -1809,11 +2037,12 @@ HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
|
||||||
if (!(object = heap_alloc_zero(sizeof(*object))))
|
if (!(object = heap_alloc_zero(sizeof(*object))))
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
parser_init_common(object);
|
||||||
|
|
||||||
strmbase_filter_init(&object->filter, outer, &CLSID_decodebin_parser, &filter_ops);
|
strmbase_filter_init(&object->filter, outer, &CLSID_decodebin_parser, &filter_ops);
|
||||||
strmbase_sink_init(&object->sink, &object->filter, wcsInputPinName, &sink_ops, NULL);
|
strmbase_sink_init(&object->sink, &object->filter, wcsInputPinName, &sink_ops, NULL);
|
||||||
|
|
||||||
object->no_more_pads_event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
object->no_more_pads_event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
object->error_event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
||||||
object->init_gst = decodebin_parser_init_gst;
|
object->init_gst = decodebin_parser_init_gst;
|
||||||
object->source_query_accept = decodebin_parser_source_query_accept;
|
object->source_query_accept = decodebin_parser_source_query_accept;
|
||||||
object->source_get_media_type = decodebin_parser_source_get_media_type;
|
object->source_get_media_type = decodebin_parser_source_get_media_type;
|
||||||
|
@ -2203,6 +2432,10 @@ static struct parser_source *create_pin(struct parser *filter, const WCHAR *name
|
||||||
pin->IQualityControl_iface.lpVtbl = &GSTOutPin_QualityControl_Vtbl;
|
pin->IQualityControl_iface.lpVtbl = &GSTOutPin_QualityControl_Vtbl;
|
||||||
strmbase_seeking_init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop,
|
strmbase_seeking_init(&pin->seek, &GST_Seeking_Vtbl, GST_ChangeStop,
|
||||||
GST_ChangeCurrent, GST_ChangeRate);
|
GST_ChangeCurrent, GST_ChangeRate);
|
||||||
|
InitializeConditionVariable(&pin->event_cv);
|
||||||
|
InitializeConditionVariable(&pin->event_empty_cv);
|
||||||
|
InitializeConditionVariable(&pin->flushing_cv);
|
||||||
|
InitializeConditionVariable(&pin->flush_stop_cv);
|
||||||
BaseFilterImpl_IncrementPinVersion(&filter->filter);
|
BaseFilterImpl_IncrementPinVersion(&filter->filter);
|
||||||
|
|
||||||
sprintf(pad_name, "qz_sink_%u", filter->source_count);
|
sprintf(pad_name, "qz_sink_%u", filter->source_count);
|
||||||
|
@ -2444,10 +2677,11 @@ HRESULT wave_parser_create(IUnknown *outer, IUnknown **out)
|
||||||
if (!(object = heap_alloc_zero(sizeof(*object))))
|
if (!(object = heap_alloc_zero(sizeof(*object))))
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
parser_init_common(object);
|
||||||
|
|
||||||
strmbase_filter_init(&object->filter, outer, &CLSID_WAVEParser, &filter_ops);
|
strmbase_filter_init(&object->filter, outer, &CLSID_WAVEParser, &filter_ops);
|
||||||
strmbase_sink_init(&object->sink, &object->filter, sink_name, &wave_parser_sink_ops, NULL);
|
strmbase_sink_init(&object->sink, &object->filter, sink_name, &wave_parser_sink_ops, NULL);
|
||||||
object->init_gst = wave_parser_init_gst;
|
object->init_gst = wave_parser_init_gst;
|
||||||
object->error_event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
||||||
object->source_query_accept = wave_parser_source_query_accept;
|
object->source_query_accept = wave_parser_source_query_accept;
|
||||||
object->source_get_media_type = wave_parser_source_get_media_type;
|
object->source_get_media_type = wave_parser_source_get_media_type;
|
||||||
|
|
||||||
|
@ -2568,10 +2802,11 @@ HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out)
|
||||||
if (!(object = heap_alloc_zero(sizeof(*object))))
|
if (!(object = heap_alloc_zero(sizeof(*object))))
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
parser_init_common(object);
|
||||||
|
|
||||||
strmbase_filter_init(&object->filter, outer, &CLSID_AviSplitter, &filter_ops);
|
strmbase_filter_init(&object->filter, outer, &CLSID_AviSplitter, &filter_ops);
|
||||||
strmbase_sink_init(&object->sink, &object->filter, sink_name, &avi_splitter_sink_ops, NULL);
|
strmbase_sink_init(&object->sink, &object->filter, sink_name, &avi_splitter_sink_ops, NULL);
|
||||||
object->no_more_pads_event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
object->no_more_pads_event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
object->error_event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
||||||
object->init_gst = avi_splitter_init_gst;
|
object->init_gst = avi_splitter_init_gst;
|
||||||
object->source_query_accept = avi_splitter_source_query_accept;
|
object->source_query_accept = avi_splitter_source_query_accept;
|
||||||
object->source_get_media_type = avi_splitter_source_get_media_type;
|
object->source_get_media_type = avi_splitter_source_get_media_type;
|
||||||
|
@ -2725,12 +2960,13 @@ HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out)
|
||||||
if (!(object = heap_alloc_zero(sizeof(*object))))
|
if (!(object = heap_alloc_zero(sizeof(*object))))
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
parser_init_common(object);
|
||||||
|
|
||||||
strmbase_filter_init(&object->filter, outer, &CLSID_MPEG1Splitter, &mpeg_splitter_ops);
|
strmbase_filter_init(&object->filter, outer, &CLSID_MPEG1Splitter, &mpeg_splitter_ops);
|
||||||
strmbase_sink_init(&object->sink, &object->filter, sink_name, &mpeg_splitter_sink_ops, NULL);
|
strmbase_sink_init(&object->sink, &object->filter, sink_name, &mpeg_splitter_sink_ops, NULL);
|
||||||
object->IAMStreamSelect_iface.lpVtbl = &stream_select_vtbl;
|
object->IAMStreamSelect_iface.lpVtbl = &stream_select_vtbl;
|
||||||
|
|
||||||
object->duration_event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
object->duration_event = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
object->error_event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
||||||
object->init_gst = mpeg_splitter_init_gst;
|
object->init_gst = mpeg_splitter_init_gst;
|
||||||
object->source_query_accept = mpeg_splitter_source_query_accept;
|
object->source_query_accept = mpeg_splitter_source_query_accept;
|
||||||
object->source_get_media_type = mpeg_splitter_source_get_media_type;
|
object->source_get_media_type = mpeg_splitter_source_get_media_type;
|
||||||
|
|
Loading…
Reference in New Issue