quartz: Add sanity check to mediaseeking setrate.
[wine] / dlls / quartz / control.c
1 /*
2  * Filter Seeking and Control Interfaces
3  *
4  * Copyright 2003 Robert Shearman
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 /* FIXME: critical sections */
21
22 #include "quartz_private.h"
23 #include "control_private.h"
24
25 #include "uuids.h"
26 #include "wine/debug.h"
27
28 #include <assert.h>
29
30 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
31
32
33 typedef HRESULT (*SeekFunc)( IMediaSeeking *to, LPVOID arg );
34
35 static HRESULT ForwardCmdSeek( IBaseFilter* from, SeekFunc fnSeek, LPVOID arg )
36 {
37     HRESULT hr = S_OK;
38     HRESULT hr_return = S_OK;
39     IEnumPins *enumpins = NULL;
40     BOOL foundend = FALSE, allnotimpl = TRUE;
41
42     hr = IBaseFilter_EnumPins( from, &enumpins );
43     if (FAILED(hr))
44         goto out;
45
46     hr = IEnumPins_Reset( enumpins );
47     while (hr == S_OK) {
48         IPin *pin = NULL;
49         hr = IEnumPins_Next( enumpins, 1, &pin, NULL );
50         if (hr == VFW_E_ENUM_OUT_OF_SYNC)
51         {
52             hr = IEnumPins_Reset( enumpins );
53             continue;
54         }
55         if (pin)
56         {
57             PIN_DIRECTION dir;
58
59             IPin_QueryDirection( pin, &dir );
60             if (dir == PINDIR_INPUT)
61             {
62                 IPin *connected = NULL;
63
64                 IPin_ConnectedTo( pin, &connected );
65                 if (connected)
66                 {
67                     HRESULT hr_local;
68                     IMediaSeeking *seek = NULL;
69
70                     hr_local = IPin_QueryInterface( connected, &IID_IMediaSeeking, (void**)&seek );
71                     if (!hr_local)
72                     {
73                         foundend = TRUE;
74                         hr_local = fnSeek( seek , arg );
75                         if (hr_local != E_NOTIMPL)
76                             allnotimpl = FALSE;
77
78                         hr_return = updatehres( hr_return, hr_local );
79                         IMediaSeeking_Release( seek );
80                     }
81                     IPin_Release(connected);
82                 }
83             }
84             IPin_Release( pin );
85         }
86     } while (hr == S_OK);
87     if (foundend && allnotimpl)
88         hr = E_NOTIMPL;
89     else
90         hr = hr_return;
91
92 out:
93     FIXME("Returning: %08x\n", hr);
94     return hr;
95 }
96
97
98 HRESULT MediaSeekingImpl_Init(IBaseFilter *pUserData, CHANGEPROC fnChangeStop, CHANGEPROC fnChangeStart, CHANGEPROC fnChangeRate, MediaSeekingImpl * pSeeking, PCRITICAL_SECTION crit_sect)
99 {
100     assert(fnChangeStop && fnChangeStart && fnChangeRate);
101
102     pSeeking->refCount = 1;
103     pSeeking->pUserData = pUserData;
104     pSeeking->fnChangeRate = fnChangeRate;
105     pSeeking->fnChangeStop = fnChangeStop;
106     pSeeking->fnChangeStart = fnChangeStart;
107     pSeeking->dwCapabilities = AM_SEEKING_CanSeekForwards |
108         AM_SEEKING_CanSeekBackwards |
109         AM_SEEKING_CanSeekAbsolute |
110         AM_SEEKING_CanGetStopPos |
111         AM_SEEKING_CanGetDuration;
112     pSeeking->llStart = 0;
113     pSeeking->llStop = ((ULONGLONG)0x80000000) << 32;
114     pSeeking->llDuration = pSeeking->llStop - pSeeking->llStart;
115     pSeeking->dRate = 1.0;
116     pSeeking->timeformat = TIME_FORMAT_MEDIA_TIME;
117     pSeeking->crst = crit_sect;
118
119     return S_OK;
120 }
121
122
123 HRESULT WINAPI MediaSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
124 {
125     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
126
127     TRACE("(%p)\n", pCapabilities);
128
129     *pCapabilities = This->dwCapabilities;
130
131     return S_OK;
132 }
133
134 static HRESULT fwd_checkcaps(IMediaSeeking *iface, LPVOID pcaps)
135 {
136     DWORD *caps = pcaps;
137     return IMediaSeeking_CheckCapabilities(iface, caps);
138 }
139
140 HRESULT WINAPI MediaSeekingImpl_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
141 {
142     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
143     HRESULT hr;
144     DWORD dwCommonCaps;
145
146     TRACE("(%p)\n", pCapabilities);
147
148     if (!pCapabilities)
149         return E_POINTER;
150
151     hr = ForwardCmdSeek(This->pUserData, fwd_checkcaps, pCapabilities);
152     if (FAILED(hr) && hr != E_NOTIMPL)
153         return hr;
154
155     dwCommonCaps = *pCapabilities & This->dwCapabilities;
156
157     if (!dwCommonCaps)
158         hr = E_FAIL;
159     else
160         hr = (*pCapabilities == dwCommonCaps) ?  S_OK : S_FALSE;
161     *pCapabilities = dwCommonCaps;
162
163     return hr;
164 }
165
166 HRESULT WINAPI MediaSeekingImpl_IsFormatSupported(IMediaSeeking * iface, const GUID * pFormat)
167 {
168     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
169
170     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
171 }
172
173 HRESULT WINAPI MediaSeekingImpl_QueryPreferredFormat(IMediaSeeking * iface, GUID * pFormat)
174 {
175     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
176
177     *pFormat = TIME_FORMAT_MEDIA_TIME;
178     return S_OK;
179 }
180
181 HRESULT WINAPI MediaSeekingImpl_GetTimeFormat(IMediaSeeking * iface, GUID * pFormat)
182 {
183     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
184     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
185
186     EnterCriticalSection(This->crst);
187     *pFormat = This->timeformat;
188     LeaveCriticalSection(This->crst);
189
190     return S_OK;
191 }
192
193 HRESULT WINAPI MediaSeekingImpl_IsUsingTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
194 {
195     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
196     HRESULT hr = S_OK;
197
198     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
199
200     EnterCriticalSection(This->crst);
201     if (!IsEqualIID(pFormat, &This->timeformat))
202         hr = S_FALSE;
203     LeaveCriticalSection(This->crst);
204
205     return hr;
206 }
207
208
209 static HRESULT fwd_settimeformat(IMediaSeeking *iface, LPVOID pformat)
210 {
211     const GUID *format = pformat;
212     return IMediaSeeking_SetTimeFormat(iface, format);
213 }
214
215 HRESULT WINAPI MediaSeekingImpl_SetTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
216 {
217     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
218     TRACE("(%s)\n", qzdebugstr_guid(pFormat));
219
220     ForwardCmdSeek(This->pUserData, fwd_settimeformat, (LPVOID)pFormat);
221
222     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
223 }
224
225
226 static HRESULT fwd_getduration(IMediaSeeking *iface, LPVOID pdur)
227 {
228     LONGLONG *duration = pdur;
229     LONGLONG mydur = *duration;
230     HRESULT hr;
231
232     hr = IMediaSeeking_GetDuration(iface, &mydur);
233     if (FAILED(hr))
234         return hr;
235
236     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
237         *duration = mydur;
238     return hr;
239 }
240
241 HRESULT WINAPI MediaSeekingImpl_GetDuration(IMediaSeeking * iface, LONGLONG * pDuration)
242 {
243     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
244
245     TRACE("(%p)\n", pDuration);
246
247     EnterCriticalSection(This->crst);
248     *pDuration = This->llDuration;
249     ForwardCmdSeek(This->pUserData, fwd_getduration, pDuration);
250     LeaveCriticalSection(This->crst);
251
252     return S_OK;
253 }
254
255
256 static HRESULT fwd_getstopposition(IMediaSeeking *iface, LPVOID pdur)
257 {
258     LONGLONG *duration = pdur;
259     LONGLONG mydur = *duration;
260     HRESULT hr;
261
262     hr = IMediaSeeking_GetStopPosition(iface, &mydur);
263     if (FAILED(hr))
264         return hr;
265
266     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
267         *duration = mydur;
268     return hr;
269 }
270
271 HRESULT WINAPI MediaSeekingImpl_GetStopPosition(IMediaSeeking * iface, LONGLONG * pStop)
272 {
273     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
274
275     TRACE("(%p)\n", pStop);
276
277     EnterCriticalSection(This->crst);
278     *pStop = This->llStop;
279     ForwardCmdSeek(This->pUserData, fwd_getstopposition, pStop);
280     LeaveCriticalSection(This->crst);
281
282     return S_OK;
283 }
284
285
286 static HRESULT fwd_getcurposition(IMediaSeeking *iface, LPVOID pdur)
287 {
288     LONGLONG *duration = pdur;
289     LONGLONG mydur = *duration;
290     HRESULT hr;
291
292     hr = IMediaSeeking_GetCurrentPosition(iface, &mydur);
293     if (FAILED(hr))
294         return hr;
295
296     if ((mydur < *duration) || (*duration < 0 && mydur > 0))
297         *duration = mydur;
298     return hr;
299 }
300
301 /* FIXME: Make use of the info the filter should expose */
302 HRESULT WINAPI MediaSeekingImpl_GetCurrentPosition(IMediaSeeking * iface, LONGLONG * pCurrent)
303 {
304     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
305
306     TRACE("(%p)\n", pCurrent);
307
308     EnterCriticalSection(This->crst);
309     *pCurrent = This->llStart;
310     ForwardCmdSeek(This->pUserData, fwd_getcurposition, pCurrent);
311     LeaveCriticalSection(This->crst);
312
313     return S_OK;
314 }
315
316 HRESULT WINAPI MediaSeekingImpl_ConvertTimeFormat(IMediaSeeking * iface, LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat)
317 {
318     if (IsEqualIID(pTargetFormat, &TIME_FORMAT_MEDIA_TIME) && IsEqualIID(pSourceFormat, &TIME_FORMAT_MEDIA_TIME))
319     {
320         *pTarget = Source;
321         return S_OK;
322     }
323     /* FIXME: clear pTarget? */
324     return E_INVALIDARG;
325 }
326
327 static inline LONGLONG Adjust(LONGLONG value, const LONGLONG * pModifier, DWORD dwFlags)
328 {
329     switch (dwFlags & AM_SEEKING_PositioningBitsMask)
330     {
331     case AM_SEEKING_NoPositioning:
332         return value;
333     case AM_SEEKING_AbsolutePositioning:
334         return *pModifier;
335     case AM_SEEKING_RelativePositioning:
336     case AM_SEEKING_IncrementalPositioning:
337         return value + *pModifier;
338     default:
339         assert(FALSE);
340         return 0;
341     }
342 }
343
344 HRESULT WINAPI MediaSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, DWORD dwCurrentFlags, LONGLONG * pStop, DWORD dwStopFlags)
345 {
346     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
347     BOOL bChangeStart = FALSE, bChangeStop = FALSE;
348     LONGLONG llNewStart, llNewStop;
349
350     TRACE("(%p, %x, %p, %x)\n", pCurrent, dwCurrentFlags, pStop, dwStopFlags);
351
352     EnterCriticalSection(This->crst);
353
354     llNewStart = Adjust(This->llStart, pCurrent, dwCurrentFlags);
355     llNewStop = Adjust(This->llStop, pStop, dwStopFlags);
356
357     if (llNewStart != This->llStart)
358         bChangeStart = TRUE;
359     if (llNewStop != This->llStop)
360         bChangeStop = TRUE;
361
362     This->llStart = llNewStart;
363     This->llStop = llNewStop;
364
365     if (dwCurrentFlags & AM_SEEKING_ReturnTime)
366         *pCurrent = llNewStart;
367     if (dwStopFlags & AM_SEEKING_ReturnTime)
368         *pStop = llNewStop;
369
370     if (bChangeStart)
371         This->fnChangeStart(This->pUserData);
372     if (bChangeStop)
373         This->fnChangeStop(This->pUserData);
374
375     LeaveCriticalSection(This->crst);
376
377     return S_OK;
378 }
379
380 HRESULT WINAPI MediaSeekingImpl_GetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, LONGLONG * pStop)
381 {
382     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
383
384     TRACE("(%p, %p)\n", pCurrent, pStop);
385
386     EnterCriticalSection(This->crst);
387     *pCurrent = This->llStart;
388     *pStop = This->llStop;
389     LeaveCriticalSection(This->crst);
390
391     return S_OK;
392 }
393
394 HRESULT WINAPI MediaSeekingImpl_GetAvailable(IMediaSeeking * iface, LONGLONG * pEarliest, LONGLONG * pLatest)
395 {
396     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
397
398     TRACE("(%p, %p)\n", pEarliest, pLatest);
399
400     EnterCriticalSection(This->crst);
401     *pEarliest = 0;
402     *pLatest = This->llDuration;
403     LeaveCriticalSection(This->crst);
404
405     return S_OK;
406 }
407
408 static HRESULT fwd_setrate(IMediaSeeking *iface, LPVOID prate)
409 {
410     double *rate = prate;
411
412     HRESULT hr;
413
414     hr = IMediaSeeking_SetRate(iface, *rate);
415     if (FAILED(hr))
416         return hr;
417
418     return hr;
419 }
420
421 HRESULT WINAPI MediaSeekingImpl_SetRate(IMediaSeeking * iface, double dRate)
422 {
423     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
424     BOOL bChangeRate = (dRate != This->dRate);
425     HRESULT hr = S_OK;
426
427     TRACE("(%e)\n", dRate);
428
429     if (dRate > 100 || dRate < .001)
430     {
431         FIXME("Excessive rate %e, ignoring\n", dRate);
432         return VFW_E_UNSUPPORTED_AUDIO;
433     }
434
435     EnterCriticalSection(This->crst);
436     This->dRate = dRate;
437     if (bChangeRate)
438         hr = This->fnChangeRate(This->pUserData);
439     ForwardCmdSeek(This->pUserData, fwd_setrate, &dRate);
440     LeaveCriticalSection(This->crst);
441
442     return hr;
443 }
444
445 HRESULT WINAPI MediaSeekingImpl_GetRate(IMediaSeeking * iface, double * dRate)
446 {
447     MediaSeekingImpl *This = (MediaSeekingImpl *)iface;
448
449     TRACE("(%p)\n", dRate);
450
451     EnterCriticalSection(This->crst);
452     /* Forward? */
453     *dRate = This->dRate;
454     LeaveCriticalSection(This->crst);
455
456     return S_OK;
457 }
458
459 HRESULT WINAPI MediaSeekingImpl_GetPreroll(IMediaSeeking * iface, LONGLONG * pPreroll)
460 {
461     TRACE("(%p)\n", pPreroll);
462
463     *pPreroll = 0;
464     return S_OK;
465 }