Commit b9e8a742 authored by Anton Baskanov's avatar Anton Baskanov Committed by Alexandre Julliard

winegstreamer: Handle quality control messages in CMpegVideoCodec.

Based on the code from quartz_parser and wg_parser.
parent 1fd2958f
......@@ -1170,14 +1170,14 @@ static void test_quality_control(IFilterGraph2 *graph, IBaseFilter *filter,
ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality);
todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
hr = IFilterGraph2_ConnectDirect(graph, &testsource->source.pin.IPin_iface, sink, &mpeg_mt);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
testsource_qc.notify_sender = (IBaseFilter *)0xdeadbeef;
hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality);
todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
ok(testsource_qc.notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n",
testsource_qc.notify_sender);
......@@ -1186,11 +1186,11 @@ static void test_quality_control(IFilterGraph2 *graph, IBaseFilter *filter,
qc.notify_sender = (IBaseFilter *)0xdeadbeef;
hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
todo_wine ok(qc.notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", qc.notify_sender);
ok(qc.notify_sender == (IBaseFilter *)0xdeadbeef, "Got sender %p.\n", qc.notify_sender);
qc.notify_hr = E_FAIL;
hr = IQualityControl_Notify(source_qc, &testsink->filter.IBaseFilter_iface, quality);
todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
qc.notify_hr = S_OK;
IFilterGraph2_Disconnect(graph, sink);
......
......@@ -109,6 +109,8 @@ bool wg_transform_set_output_format(wg_transform_t transform, struct wg_format *
bool wg_transform_get_status(wg_transform_t transform, bool *accepts_input);
HRESULT wg_transform_drain(wg_transform_t transform);
HRESULT wg_transform_flush(wg_transform_t transform);
void wg_transform_notify_qos(wg_transform_t transform,
bool underflow, double proportion, int64_t diff, uint64_t timestamp);
HRESULT wg_muxer_create(const char *format, wg_muxer_t *muxer);
void wg_muxer_destroy(wg_muxer_t muxer);
......
......@@ -457,6 +457,24 @@ HRESULT wg_transform_flush(wg_transform_t transform)
return S_OK;
}
void wg_transform_notify_qos(wg_transform_t transform,
bool underflow, double proportion, int64_t diff, uint64_t timestamp)
{
struct wg_transform_notify_qos_params params =
{
.transform = transform,
.underflow = underflow,
.proportion = proportion,
.diff = diff,
.timestamp = timestamp,
};
TRACE("transform %#I64x, underflow %d, proportion %.16e, diff %I64d, timestamp %I64u.\n",
transform, underflow, proportion, diff, timestamp);
WINE_UNIX_CALL(unix_wg_transform_notify_qos, &params);
}
HRESULT wg_muxer_create(const char *format, wg_muxer_t *muxer)
{
struct wg_muxer_create_params params =
......
......@@ -52,6 +52,7 @@ struct transform_ops
HRESULT (*source_query_accept)(struct transform *filter, const AM_MEDIA_TYPE *mt);
HRESULT (*source_get_media_type)(struct transform *filter, unsigned int index, AM_MEDIA_TYPE *mt);
HRESULT (*source_decide_buffer_size)(struct transform *filter, IMemAllocator *allocator, ALLOCATOR_PROPERTIES *props);
HRESULT (*source_qc_notify)(struct transform *filter, IBaseFilter *sender, Quality q);
};
static inline struct transform *impl_from_strmbase_filter(struct strmbase_filter *iface)
......@@ -505,23 +506,8 @@ static ULONG WINAPI source_quality_control_Release(IQualityControl *iface)
static HRESULT WINAPI source_quality_control_Notify(IQualityControl *iface, IBaseFilter *sender, Quality q)
{
struct transform *filter = impl_from_source_IQualityControl(iface);
IQualityControl *peer;
HRESULT hr = VFW_E_NOT_FOUND;
TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n",
filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
if (filter->qc_sink)
return IQualityControl_Notify(filter->qc_sink, &filter->filter.IBaseFilter_iface, q);
if (filter->sink.pin.peer
&& SUCCEEDED(IPin_QueryInterface(filter->sink.pin.peer, &IID_IQualityControl, (void **)&peer)))
{
hr = IQualityControl_Notify(peer, &filter->filter.IBaseFilter_iface, q);
IQualityControl_Release(peer);
}
return hr;
return filter->ops->source_qc_notify(filter, sender, q);
}
static HRESULT WINAPI source_quality_control_SetSink(IQualityControl *iface, IQualityControl *sink)
......@@ -567,6 +553,73 @@ static HRESULT transform_create(IUnknown *outer, const CLSID *clsid, const struc
return S_OK;
}
static HRESULT passthrough_source_qc_notify(struct transform *filter, IBaseFilter *sender, Quality q)
{
IQualityControl *peer;
HRESULT hr = VFW_E_NOT_FOUND;
TRACE("filter %p, sender %p, type %s, proportion %ld, late %s, timestamp %s.\n",
filter, sender, q.Type == Famine ? "Famine" : "Flood", q.Proportion,
debugstr_time(q.Late), debugstr_time(q.TimeStamp));
if (filter->qc_sink)
return IQualityControl_Notify(filter->qc_sink, &filter->filter.IBaseFilter_iface, q);
if (filter->sink.pin.peer
&& SUCCEEDED(IPin_QueryInterface(filter->sink.pin.peer, &IID_IQualityControl, (void **)&peer)))
{
hr = IQualityControl_Notify(peer, &filter->filter.IBaseFilter_iface, q);
IQualityControl_Release(peer);
}
return hr;
}
static HRESULT handle_source_qc_notify(struct transform *filter, IBaseFilter *sender, Quality q)
{
uint64_t timestamp;
int64_t diff;
TRACE("filter %p, sender %p, type %s, proportion %ld, late %s, timestamp %s.\n",
filter, sender, q.Type == Famine ? "Famine" : "Flood", q.Proportion,
debugstr_time(q.Late), debugstr_time(q.TimeStamp));
/* DirectShow filters sometimes pass negative timestamps (Audiosurf uses the
* current time instead of the time of the last buffer). GstClockTime is
* unsigned, so clamp it to 0. */
timestamp = max(q.TimeStamp, 0);
/* The documentation specifies that timestamp + diff must be nonnegative. */
diff = q.Late;
if (diff < 0 && timestamp < (uint64_t)-diff)
diff = -timestamp;
/* DirectShow "Proportion" describes what percentage of buffers the upstream
* filter should keep (i.e. dropping the rest). If frames are late, the
* proportion will be less than 1. For example, a proportion of 500 means
* that the element should drop half of its frames, essentially because
* frames are taking twice as long as they should to arrive.
*
* GStreamer "proportion" is the inverse of this; it describes how much
* faster the upstream element should produce frames. I.e. if frames are
* taking twice as long as they should to arrive, we want the frames to be
* decoded twice as fast, and so we pass 2.0 to GStreamer. */
if (!q.Proportion)
{
WARN("Ignoring quality message with zero proportion.\n");
return S_OK;
}
/* GST_QOS_TYPE_OVERFLOW is also used for buffers that arrive on time, but
* DirectShow filters might use Famine, so check that there actually is an
* underrun. */
wg_transform_notify_qos(filter->transform, q.Type == Famine && q.Proportion < 1000,
1000.0 / q.Proportion, diff, timestamp);
return S_OK;
}
static HRESULT mpeg_audio_codec_sink_query_accept(struct transform *filter, const AM_MEDIA_TYPE *mt)
{
const MPEG1WAVEFORMAT *format;
......@@ -686,6 +739,7 @@ static const struct transform_ops mpeg_audio_codec_transform_ops =
mpeg_audio_codec_source_query_accept,
mpeg_audio_codec_source_get_media_type,
mpeg_audio_codec_source_decide_buffer_size,
passthrough_source_qc_notify,
};
HRESULT mpeg_audio_codec_create(IUnknown *outer, IUnknown **out)
......@@ -837,6 +891,7 @@ static const struct transform_ops mpeg_video_codec_transform_ops =
mpeg_video_codec_source_query_accept,
mpeg_video_codec_source_get_media_type,
mpeg_video_codec_source_decide_buffer_size,
handle_source_qc_notify,
};
HRESULT mpeg_video_codec_create(IUnknown *outer, IUnknown **out)
......@@ -962,6 +1017,7 @@ static const struct transform_ops mpeg_layer3_decoder_transform_ops =
mpeg_layer3_decoder_source_query_accept,
mpeg_layer3_decoder_source_get_media_type,
mpeg_layer3_decoder_source_decide_buffer_size,
passthrough_source_qc_notify,
};
HRESULT mpeg_layer3_decoder_create(IUnknown *outer, IUnknown **out)
......
......@@ -62,6 +62,7 @@ extern NTSTATUS wg_transform_read_data(void *args);
extern NTSTATUS wg_transform_get_status(void *args);
extern NTSTATUS wg_transform_drain(void *args);
extern NTSTATUS wg_transform_flush(void *args);
extern NTSTATUS wg_transform_notify_qos(void *args);
/* wg_muxer.c */
......
......@@ -372,6 +372,15 @@ struct wg_transform_get_status_params
UINT32 accepts_input;
};
struct wg_transform_notify_qos_params
{
wg_transform_t transform;
UINT8 underflow;
DOUBLE proportion;
INT64 diff;
UINT64 timestamp;
};
struct wg_muxer_create_params
{
wg_muxer_t muxer;
......@@ -439,6 +448,7 @@ enum unix_funcs
unix_wg_transform_get_status,
unix_wg_transform_drain,
unix_wg_transform_flush,
unix_wg_transform_notify_qos,
unix_wg_muxer_create,
unix_wg_muxer_destroy,
......
......@@ -1921,6 +1921,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
X(wg_transform_get_status),
X(wg_transform_drain),
X(wg_transform_flush),
X(wg_transform_notify_qos),
X(wg_muxer_create),
X(wg_muxer_destroy),
......@@ -2253,6 +2254,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
X(wg_transform_get_status),
X(wg_transform_drain),
X(wg_transform_flush),
X(wg_transform_notify_qos),
X64(wg_muxer_create),
X(wg_muxer_destroy),
......
......@@ -930,3 +930,33 @@ NTSTATUS wg_transform_flush(void *args)
return STATUS_SUCCESS;
}
NTSTATUS wg_transform_notify_qos(void *args)
{
const struct wg_transform_notify_qos_params *params = args;
struct wg_transform *transform = get_transform(params->transform);
GstClockTimeDiff diff = params->diff * 100;
GstClockTime stream_time;
GstEvent *event;
/* We return timestamps in stream time, i.e. relative to the start of the
* file (or other medium), but gst_event_new_qos() expects the timestamp in
* running time. */
stream_time = gst_segment_to_running_time(&transform->segment, GST_FORMAT_TIME, params->timestamp * 100);
if (diff < (GstClockTimeDiff)-stream_time)
diff = -stream_time;
if (stream_time == -1)
{
/* This can happen legitimately if the sample falls outside of the
* segment bounds. GStreamer elements shouldn't present the sample in
* that case, but DirectShow doesn't care. */
GST_LOG("Ignoring QoS event.");
return S_OK;
}
if (!(event = gst_event_new_qos(params->underflow ? GST_QOS_TYPE_UNDERFLOW : GST_QOS_TYPE_OVERFLOW,
params->proportion, diff, stream_time)))
GST_ERROR("Failed to create QOS event.");
push_event(transform->my_sink, event);
return S_OK;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment