Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/strmbase/renderer.c
8669 views
1
/*
2
* Generic Implementation of strmbase Base Renderer classes
3
*
4
* Copyright 2012 Aric Stewart, CodeWeavers
5
*
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
*/
20
21
#define COBJMACROS
22
#include "wine/strmbase.h"
23
#include "wine/debug.h"
24
25
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
26
27
/* The following quality-of-service code is based on GstBaseSink QoS code, which
28
* is covered by the following copyright information:
29
*
30
* GStreamer
31
* Copyright (C) 2005-2007 Wim Taymans <[email protected]>
32
*
33
* gstbasesink.c: Base class for sink elements
34
*
35
* This library is free software; you can redistribute it and/or
36
* modify it under the terms of the GNU Library General Public
37
* License as published by the Free Software Foundation; either
38
* version 2 of the License, or (at your option) any later version.
39
*
40
* This library is distributed in the hope that it will be useful,
41
* but WITHOUT ANY WARRANTY; without even the implied warranty of
42
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43
* Library General Public License for more details.
44
*
45
* You should have received a copy of the GNU Library General Public
46
* License along with this library; if not, write to the
47
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
48
* Boston, MA 02110-1301, USA.
49
*/
50
51
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
52
53
/* Generic running average; this has a neutral window size. */
54
#define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)
55
56
/* The windows for these running averages are experimentally obtained.
57
* Positive values get averaged more, while negative values use a small
58
* window so we can react faster to badness. */
59
#define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
60
#define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
61
62
static void reset_qos(struct strmbase_renderer *filter)
63
{
64
filter->last_left = filter->avg_duration = filter->avg_pt = -1;
65
filter->avg_rate = -1.0;
66
}
67
68
static void perform_qos(struct strmbase_renderer *filter,
69
const REFERENCE_TIME start, const REFERENCE_TIME stop, REFERENCE_TIME jitter)
70
{
71
REFERENCE_TIME pt, entered, left, duration;
72
double rate;
73
74
if (jitter < 0)
75
{
76
/* This is the time the buffer entered the sink. */
77
if (start < -jitter)
78
entered = 0;
79
else
80
entered = start + jitter;
81
left = start;
82
}
83
else
84
{
85
/* This is the time the buffer entered the sink. */
86
entered = start + jitter;
87
/* This is the time the buffer left the sink. */
88
left = start + jitter;
89
}
90
91
/* Calculate the duration of the buffer. */
92
if (stop >= start)
93
duration = stop - start;
94
else
95
duration = 0;
96
97
/* If we have the time when the last buffer left us, calculate processing
98
* time. */
99
if (filter->last_left >= 0)
100
{
101
if (entered > filter->last_left)
102
pt = entered - filter->last_left;
103
else
104
pt = 0;
105
}
106
else
107
{
108
pt = filter->avg_pt;
109
}
110
111
TRACE("start %s, entered %s, left %s, pt %s, duration %s, jitter %s.\n",
112
debugstr_time(start), debugstr_time(entered), debugstr_time(left),
113
debugstr_time(pt), debugstr_time(duration), debugstr_time(jitter));
114
115
TRACE("average duration %s, average pt %s, average rate %.16e.\n",
116
debugstr_time(filter->avg_duration), debugstr_time(filter->avg_pt), filter->avg_rate);
117
118
/* Collect running averages. For first observations, we copy the values. */
119
if (filter->avg_duration < 0)
120
filter->avg_duration = duration;
121
else
122
filter->avg_duration = UPDATE_RUNNING_AVG(filter->avg_duration, duration);
123
124
if (filter->avg_pt < 0)
125
filter->avg_pt = pt;
126
else
127
filter->avg_pt = UPDATE_RUNNING_AVG(filter->avg_pt, pt);
128
129
if (filter->avg_duration != 0)
130
rate = (double)filter->avg_pt / (double)filter->avg_duration;
131
else
132
rate = 0.0;
133
134
if (filter->last_left >= 0)
135
{
136
if (filter->avg_rate < 0.0)
137
{
138
filter->avg_rate = rate;
139
}
140
else
141
{
142
if (rate > 1.0)
143
filter->avg_rate = UPDATE_RUNNING_AVG_N(filter->avg_rate, rate);
144
else
145
filter->avg_rate = UPDATE_RUNNING_AVG_P(filter->avg_rate, rate);
146
}
147
}
148
149
if (filter->avg_rate >= 0.0)
150
{
151
Quality q;
152
153
/* If we have a valid rate, start sending QoS messages. */
154
if (jitter < 0)
155
{
156
/* Make sure we never go below 0 when adding the jitter to the
157
* timestamp. */
158
if (start < -jitter)
159
jitter = -start;
160
}
161
else
162
{
163
jitter += stop - start;
164
}
165
166
q.Type = (jitter > 0 ? Famine : Flood);
167
q.Proportion = 1000.0 / filter->avg_rate;
168
if (q.Proportion < 200)
169
q.Proportion = 200;
170
else if (q.Proportion > 5000)
171
q.Proportion = 5000;
172
q.Late = jitter;
173
q.TimeStamp = start;
174
IQualityControl_Notify(&filter->IQualityControl_iface, &filter->filter.IBaseFilter_iface, q);
175
}
176
177
/* Record when this buffer will leave us. */
178
filter->last_left = left;
179
}
180
181
static inline struct strmbase_renderer *impl_from_strmbase_filter(struct strmbase_filter *iface)
182
{
183
return CONTAINING_RECORD(iface, struct strmbase_renderer, filter);
184
}
185
186
static inline struct strmbase_renderer *impl_from_IPin(IPin *iface)
187
{
188
return CONTAINING_RECORD(iface, struct strmbase_renderer, sink.pin.IPin_iface);
189
}
190
191
static struct strmbase_pin *renderer_get_pin(struct strmbase_filter *iface, unsigned int index)
192
{
193
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
194
195
if (index == 0)
196
return &filter->sink.pin;
197
return NULL;
198
}
199
200
static void renderer_destroy(struct strmbase_filter *iface)
201
{
202
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
203
filter->ops->renderer_destroy(filter);
204
}
205
206
static HRESULT renderer_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
207
{
208
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
209
HRESULT hr;
210
211
if (filter->ops->renderer_query_interface
212
&& SUCCEEDED(hr = filter->ops->renderer_query_interface(filter, iid, out)))
213
{
214
return hr;
215
}
216
217
if (IsEqualGUID(iid, &IID_IMediaPosition))
218
*out = &filter->passthrough.IMediaPosition_iface;
219
else if (IsEqualGUID(iid, &IID_IMediaSeeking))
220
*out = &filter->passthrough.IMediaSeeking_iface;
221
else if (IsEqualGUID(iid, &IID_IQualityControl))
222
*out = &filter->IQualityControl_iface;
223
else
224
return E_NOINTERFACE;
225
226
IUnknown_AddRef((IUnknown *)*out);
227
return S_OK;
228
}
229
230
static HRESULT renderer_init_stream(struct strmbase_filter *iface)
231
{
232
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
233
234
if (filter->sink.pin.peer)
235
ResetEvent(filter->state_event);
236
filter->eos = FALSE;
237
ResetEvent(filter->flush_event);
238
if (filter->ops->renderer_init_stream)
239
filter->ops->renderer_init_stream(filter);
240
241
return filter->sink.pin.peer ? S_FALSE : S_OK;
242
}
243
244
static HRESULT renderer_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start)
245
{
246
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
247
IFilterGraph *graph = filter->filter.graph;
248
IMediaEventSink *event_sink;
249
250
filter->stream_start = start;
251
SetEvent(filter->state_event);
252
SetEvent(filter->run_event);
253
reset_qos(filter);
254
if (filter->sink.pin.peer && filter->ops->renderer_start_stream)
255
filter->ops->renderer_start_stream(filter);
256
257
if ((filter->eos || !filter->sink.pin.peer) && graph
258
&& SUCCEEDED(IFilterGraph_QueryInterface(graph,
259
&IID_IMediaEventSink, (void **)&event_sink)))
260
{
261
IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
262
(LONG_PTR)&filter->filter.IBaseFilter_iface);
263
IMediaEventSink_Release(event_sink);
264
}
265
266
return S_OK;
267
}
268
269
static HRESULT renderer_stop_stream(struct strmbase_filter *iface)
270
{
271
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
272
273
ResetEvent(filter->run_event);
274
275
if (filter->sink.pin.peer && filter->ops->renderer_stop_stream)
276
filter->ops->renderer_stop_stream(filter);
277
278
return S_OK;
279
}
280
281
static HRESULT renderer_cleanup_stream(struct strmbase_filter *iface)
282
{
283
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
284
285
strmbase_passthrough_invalidate_time(&filter->passthrough);
286
SetEvent(filter->state_event);
287
SetEvent(filter->flush_event);
288
289
return S_OK;
290
}
291
292
static HRESULT renderer_wait_state(struct strmbase_filter *iface, DWORD timeout)
293
{
294
struct strmbase_renderer *filter = impl_from_strmbase_filter(iface);
295
296
if (WaitForSingleObject(filter->state_event, timeout) == WAIT_TIMEOUT)
297
return VFW_S_STATE_INTERMEDIATE;
298
return S_OK;
299
}
300
301
static const struct strmbase_filter_ops filter_ops =
302
{
303
.filter_get_pin = renderer_get_pin,
304
.filter_destroy = renderer_destroy,
305
.filter_query_interface = renderer_query_interface,
306
.filter_init_stream = renderer_init_stream,
307
.filter_start_stream = renderer_start_stream,
308
.filter_stop_stream = renderer_stop_stream,
309
.filter_cleanup_stream = renderer_cleanup_stream,
310
.filter_wait_state = renderer_wait_state,
311
};
312
313
static HRESULT sink_query_accept(struct strmbase_pin *pin, const AM_MEDIA_TYPE *mt)
314
{
315
struct strmbase_renderer *filter = impl_from_IPin(&pin->IPin_iface);
316
return filter->ops->renderer_query_accept(filter, mt);
317
}
318
319
static HRESULT sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
320
{
321
struct strmbase_renderer *filter = impl_from_IPin(&iface->IPin_iface);
322
HRESULT hr;
323
324
if (filter->ops->renderer_pin_query_interface
325
&& SUCCEEDED(hr = filter->ops->renderer_pin_query_interface(filter, iid, out)))
326
return hr;
327
328
if (IsEqualGUID(iid, &IID_IMemInputPin))
329
*out = &filter->sink.IMemInputPin_iface;
330
else
331
return E_NOINTERFACE;
332
333
IUnknown_AddRef((IUnknown *)*out);
334
return S_OK;
335
}
336
337
static HRESULT WINAPI BaseRenderer_Receive(struct strmbase_sink *pin, IMediaSample *sample)
338
{
339
struct strmbase_renderer *filter = impl_from_IPin(&pin->pin.IPin_iface);
340
REFERENCE_TIME start, stop;
341
BOOL need_wait = FALSE;
342
FILTER_STATE state;
343
HRESULT hr = S_OK;
344
AM_MEDIA_TYPE *mt;
345
346
if (filter->eos || filter->sink.flushing)
347
return S_FALSE;
348
349
state = filter->filter.state;
350
if (state == State_Stopped)
351
return VFW_E_WRONG_STATE;
352
353
if (IMediaSample_GetMediaType(sample, &mt) == S_OK)
354
{
355
TRACE("Format change.\n");
356
strmbase_dump_media_type(mt);
357
358
if (FAILED(filter->ops->renderer_query_accept(filter, mt)))
359
return VFW_E_TYPE_NOT_ACCEPTED;
360
DeleteMediaType(mt);
361
}
362
363
if (filter->filter.clock && SUCCEEDED(IMediaSample_GetTime(sample, &start, &stop)))
364
{
365
strmbase_passthrough_update_time(&filter->passthrough, start);
366
need_wait = TRUE;
367
}
368
369
if (state == State_Paused)
370
{
371
HANDLE events[2] = {filter->run_event, filter->flush_event};
372
373
filter->current_sample = sample;
374
375
hr = filter->ops->renderer_render(filter, sample);
376
377
SetEvent(filter->state_event);
378
LeaveCriticalSection(&filter->filter.stream_cs);
379
WaitForMultipleObjects(2, events, FALSE, INFINITE);
380
EnterCriticalSection(&filter->filter.stream_cs);
381
382
filter->current_sample = NULL;
383
}
384
385
if (need_wait)
386
{
387
REFERENCE_TIME now, jitter;
388
DWORD_PTR cookie;
389
390
IReferenceClock_GetTime(filter->filter.clock, &now);
391
392
/* "jitter" describes how early (negative) or late (positive) this
393
* buffer arrived, relative to its PTS. */
394
jitter = (now - filter->stream_start) - start;
395
396
if (jitter <= -10000) /* if we are early by at least 1 ms */
397
{
398
HANDLE handles[2] = {filter->advise_event, filter->flush_event};
399
DWORD ret;
400
401
IReferenceClock_AdviseTime(filter->filter.clock, filter->stream_start,
402
start, (HEVENT)filter->advise_event, &cookie);
403
404
ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
405
IReferenceClock_Unadvise(filter->filter.clock, cookie);
406
407
if (ret == 1)
408
{
409
TRACE("Flush signaled; discarding current sample.\n");
410
return S_OK;
411
}
412
}
413
414
if (state == State_Running)
415
hr = filter->ops->renderer_render(filter, sample);
416
417
perform_qos(filter, start, stop, jitter);
418
}
419
else
420
{
421
if (state == State_Running)
422
hr = filter->ops->renderer_render(filter, sample);
423
}
424
425
return hr;
426
}
427
428
static HRESULT sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
429
{
430
struct strmbase_renderer *filter = impl_from_IPin(&iface->pin.IPin_iface);
431
432
if (filter->ops->renderer_connect)
433
return filter->ops->renderer_connect(filter, mt);
434
return S_OK;
435
}
436
437
static void sink_disconnect(struct strmbase_sink *iface)
438
{
439
struct strmbase_renderer *filter = impl_from_IPin(&iface->pin.IPin_iface);
440
441
if (filter->ops->renderer_disconnect)
442
filter->ops->renderer_disconnect(filter);
443
}
444
445
static HRESULT sink_eos(struct strmbase_sink *iface)
446
{
447
struct strmbase_renderer *filter = impl_from_IPin(&iface->pin.IPin_iface);
448
IFilterGraph *graph = filter->filter.graph;
449
IMediaEventSink *event_sink;
450
451
filter->eos = TRUE;
452
453
if (filter->filter.state == State_Running && graph
454
&& SUCCEEDED(IFilterGraph_QueryInterface(graph,
455
&IID_IMediaEventSink, (void **)&event_sink)))
456
{
457
IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
458
(LONG_PTR)&filter->filter.IBaseFilter_iface);
459
IMediaEventSink_Release(event_sink);
460
}
461
strmbase_passthrough_eos(&filter->passthrough);
462
SetEvent(filter->state_event);
463
464
return S_OK;
465
}
466
467
static HRESULT sink_begin_flush(struct strmbase_sink *iface)
468
{
469
struct strmbase_renderer *filter = impl_from_IPin(&iface->pin.IPin_iface);
470
471
SetEvent(filter->flush_event);
472
473
return S_OK;
474
}
475
476
static HRESULT sink_end_flush(struct strmbase_sink *iface)
477
{
478
struct strmbase_renderer *filter = impl_from_IPin(&iface->pin.IPin_iface);
479
480
EnterCriticalSection(&filter->filter.stream_cs);
481
482
filter->eos = FALSE;
483
reset_qos(filter);
484
strmbase_passthrough_invalidate_time(&filter->passthrough);
485
ResetEvent(filter->flush_event);
486
487
LeaveCriticalSection(&filter->filter.stream_cs);
488
return S_OK;
489
}
490
491
static const struct strmbase_sink_ops sink_ops =
492
{
493
.base.pin_query_accept = sink_query_accept,
494
.base.pin_query_interface = sink_query_interface,
495
.pfnReceive = BaseRenderer_Receive,
496
.sink_connect = sink_connect,
497
.sink_disconnect = sink_disconnect,
498
.sink_eos = sink_eos,
499
.sink_begin_flush = sink_begin_flush,
500
.sink_end_flush = sink_end_flush,
501
};
502
503
static struct strmbase_renderer *impl_from_IQualityControl(IQualityControl *iface)
504
{
505
return CONTAINING_RECORD(iface, struct strmbase_renderer, IQualityControl_iface);
506
}
507
508
static HRESULT WINAPI quality_control_QueryInterface(IQualityControl *iface, REFIID iid, void **out)
509
{
510
struct strmbase_renderer *filter = impl_from_IQualityControl(iface);
511
return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
512
}
513
514
static ULONG WINAPI quality_control_AddRef(IQualityControl *iface)
515
{
516
struct strmbase_renderer *filter = impl_from_IQualityControl(iface);
517
return IUnknown_AddRef(filter->filter.outer_unk);
518
}
519
520
static ULONG WINAPI quality_control_Release(IQualityControl *iface)
521
{
522
struct strmbase_renderer *filter = impl_from_IQualityControl(iface);
523
return IUnknown_Release(filter->filter.outer_unk);
524
}
525
526
static HRESULT WINAPI quality_control_Notify(IQualityControl *iface, IBaseFilter *sender, Quality q)
527
{
528
struct strmbase_renderer *filter = impl_from_IQualityControl(iface);
529
HRESULT hr = S_FALSE;
530
531
TRACE("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s.\n",
532
filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
533
534
if (filter->qc_sink)
535
return IQualityControl_Notify(filter->qc_sink, &filter->filter.IBaseFilter_iface, q);
536
537
if (filter->sink.pin.peer)
538
{
539
IQualityControl *peer_qc = NULL;
540
IPin_QueryInterface(filter->sink.pin.peer, &IID_IQualityControl, (void **)&peer_qc);
541
if (peer_qc)
542
{
543
hr = IQualityControl_Notify(peer_qc, &filter->filter.IBaseFilter_iface, q);
544
IQualityControl_Release(peer_qc);
545
}
546
}
547
548
return hr;
549
}
550
551
static HRESULT WINAPI quality_control_SetSink(IQualityControl *iface, IQualityControl *sink)
552
{
553
struct strmbase_renderer *filter = impl_from_IQualityControl(iface);
554
555
TRACE("filter %p, sink %p.\n", filter, sink);
556
557
filter->qc_sink = sink;
558
return S_OK;
559
}
560
561
static const IQualityControlVtbl quality_control_vtbl =
562
{
563
quality_control_QueryInterface,
564
quality_control_AddRef,
565
quality_control_Release,
566
quality_control_Notify,
567
quality_control_SetSink,
568
};
569
570
void strmbase_renderer_cleanup(struct strmbase_renderer *filter)
571
{
572
if (filter->sink.pin.peer)
573
IPin_Disconnect(filter->sink.pin.peer);
574
IPin_Disconnect(&filter->sink.pin.IPin_iface);
575
strmbase_sink_cleanup(&filter->sink);
576
577
strmbase_passthrough_cleanup(&filter->passthrough);
578
579
CloseHandle(filter->state_event);
580
CloseHandle(filter->advise_event);
581
CloseHandle(filter->run_event);
582
CloseHandle(filter->flush_event);
583
strmbase_filter_cleanup(&filter->filter);
584
}
585
586
void strmbase_renderer_init(struct strmbase_renderer *filter, IUnknown *outer,
587
const CLSID *clsid, const WCHAR *sink_name, const struct strmbase_renderer_ops *ops)
588
{
589
memset(filter, 0, sizeof(*filter));
590
strmbase_filter_init(&filter->filter, outer, clsid, &filter_ops);
591
strmbase_passthrough_init(&filter->passthrough, (IUnknown *)&filter->filter.IBaseFilter_iface);
592
ISeekingPassThru_Init(&filter->passthrough.ISeekingPassThru_iface, TRUE, &filter->sink.pin.IPin_iface);
593
filter->IQualityControl_iface.lpVtbl = &quality_control_vtbl;
594
595
filter->ops = ops;
596
597
strmbase_sink_init(&filter->sink, &filter->filter, sink_name, &sink_ops, NULL);
598
599
filter->state_event = CreateEventW(NULL, TRUE, TRUE, NULL);
600
filter->advise_event = CreateEventW(NULL, FALSE, FALSE, NULL);
601
filter->run_event = CreateEventW(NULL, TRUE, FALSE, NULL);
602
filter->flush_event = CreateEventW(NULL, TRUE, TRUE, NULL);
603
}
604
605