strmbase: Allow NULL for time format in ConvertTimeFormat.
[wine] / dlls / strmbase / seeking.c
1 /*
2  * Filter Seeking and Control Interfaces
3  *
4  * Copyright 2003 Robert Shearman
5  * Copyright 2010 Aric Stewart, CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 /* FIXME: critical sections */
22
23 #define COBJMACROS
24
25 #include "dshow.h"
26 #include "wine/strmbase.h"
27
28 #include "uuids.h"
29 #include "wine/debug.h"
30
31 #include <assert.h>
32
33 WINE_DEFAULT_DEBUG_CHANNEL(strmbase);
34
35 HRESULT SourceSeeking_Init(SourceSeeking *pSeeking, const IMediaSeekingVtbl *Vtbl, SourceSeeking_ChangeStop fnChangeStop, SourceSeeking_ChangeStart fnChangeStart, SourceSeeking_ChangeRate fnChangeRate, PCRITICAL_SECTION crit_sect)
36 {
37     assert(fnChangeStop && fnChangeStart && fnChangeRate);
38
39     pSeeking->lpVtbl = Vtbl;
40     pSeeking->refCount = 1;
41     pSeeking->fnChangeRate = fnChangeRate;
42     pSeeking->fnChangeStop = fnChangeStop;
43     pSeeking->fnChangeStart = fnChangeStart;
44     pSeeking->dwCapabilities = AM_SEEKING_CanSeekForwards |
45         AM_SEEKING_CanSeekBackwards |
46         AM_SEEKING_CanSeekAbsolute |
47         AM_SEEKING_CanGetStopPos |
48         AM_SEEKING_CanGetDuration;
49     pSeeking->llCurrent = 0;
50     pSeeking->llStop = ((ULONGLONG)0x80000000) << 32;
51     pSeeking->llDuration = pSeeking->llStop;
52     pSeeking->dRate = 1.0;
53     pSeeking->timeformat = TIME_FORMAT_MEDIA_TIME;
54     pSeeking->crst = crit_sect;
55     return S_OK;
56 }
57
58 HRESULT WINAPI SourceSeekingImpl_GetCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
59 {
60     SourceSeeking *This = (SourceSeeking *)iface;
61
62     TRACE("(%p)\n", pCapabilities);
63
64     *pCapabilities = This->dwCapabilities;
65
66     return S_OK;
67 }
68
69 HRESULT WINAPI SourceSeekingImpl_CheckCapabilities(IMediaSeeking * iface, DWORD * pCapabilities)
70 {
71     SourceSeeking *This = (SourceSeeking *)iface;
72     HRESULT hr;
73     DWORD dwCommonCaps;
74
75     TRACE("(%p)\n", pCapabilities);
76
77     if (!pCapabilities)
78         return E_POINTER;
79
80     dwCommonCaps = *pCapabilities & This->dwCapabilities;
81
82     if (!dwCommonCaps)
83         hr = E_FAIL;
84     else
85         hr = (*pCapabilities == dwCommonCaps) ?  S_OK : S_FALSE;
86     *pCapabilities = dwCommonCaps;
87     return hr;
88 }
89
90 HRESULT WINAPI SourceSeekingImpl_IsFormatSupported(IMediaSeeking * iface, const GUID * pFormat)
91 {
92     TRACE("(%s)\n", debugstr_guid(pFormat));
93
94     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : S_FALSE);
95 }
96
97 HRESULT WINAPI SourceSeekingImpl_QueryPreferredFormat(IMediaSeeking * iface, GUID * pFormat)
98 {
99     TRACE("(%s)\n", debugstr_guid(pFormat));
100
101     *pFormat = TIME_FORMAT_MEDIA_TIME;
102     return S_OK;
103 }
104
105 HRESULT WINAPI SourceSeekingImpl_GetTimeFormat(IMediaSeeking * iface, GUID * pFormat)
106 {
107     SourceSeeking *This = (SourceSeeking *)iface;
108     TRACE("(%s)\n", debugstr_guid(pFormat));
109
110     EnterCriticalSection(This->crst);
111     *pFormat = This->timeformat;
112     LeaveCriticalSection(This->crst);
113
114     return S_OK;
115 }
116
117 HRESULT WINAPI SourceSeekingImpl_IsUsingTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
118 {
119     SourceSeeking *This = (SourceSeeking *)iface;
120     HRESULT hr = S_OK;
121
122     TRACE("(%s)\n", debugstr_guid(pFormat));
123
124     EnterCriticalSection(This->crst);
125     if (!IsEqualIID(pFormat, &This->timeformat))
126         hr = S_FALSE;
127     LeaveCriticalSection(This->crst);
128
129     return hr;
130 }
131
132 HRESULT WINAPI SourceSeekingImpl_SetTimeFormat(IMediaSeeking * iface, const GUID * pFormat)
133 {
134     SourceSeeking *This = (SourceSeeking *)iface;
135     TRACE("%p %s\n", This, debugstr_guid(pFormat));
136     return (IsEqualIID(pFormat, &TIME_FORMAT_MEDIA_TIME) ? S_OK : E_INVALIDARG);
137 }
138
139
140 HRESULT WINAPI SourceSeekingImpl_GetDuration(IMediaSeeking * iface, LONGLONG * pDuration)
141 {
142     SourceSeeking *This = (SourceSeeking *)iface;
143
144     TRACE("(%p)\n", pDuration);
145
146     EnterCriticalSection(This->crst);
147     *pDuration = This->llDuration;
148     LeaveCriticalSection(This->crst);
149
150     return S_OK;
151 }
152
153 HRESULT WINAPI SourceSeekingImpl_GetStopPosition(IMediaSeeking * iface, LONGLONG * pStop)
154 {
155     SourceSeeking *This = (SourceSeeking *)iface;
156
157     TRACE("(%p)\n", pStop);
158
159     EnterCriticalSection(This->crst);
160     *pStop = This->llStop;
161     LeaveCriticalSection(This->crst);
162
163     return S_OK;
164 }
165
166 /* FIXME: Make use of the info the filter should expose */
167 HRESULT WINAPI SourceSeekingImpl_GetCurrentPosition(IMediaSeeking * iface, LONGLONG * pCurrent)
168 {
169     SourceSeeking *This = (SourceSeeking *)iface;
170
171     TRACE("(%p)\n", pCurrent);
172
173     EnterCriticalSection(This->crst);
174     *pCurrent = This->llCurrent;
175     LeaveCriticalSection(This->crst);
176
177     return S_OK;
178 }
179
180 HRESULT WINAPI SourceSeekingImpl_ConvertTimeFormat(IMediaSeeking * iface, LONGLONG * pTarget, const GUID * pTargetFormat, LONGLONG Source, const GUID * pSourceFormat)
181 {
182     SourceSeeking *This = (SourceSeeking *)iface;
183     if (!pTargetFormat)
184         pTargetFormat = &This->timeformat;
185     if (!pSourceFormat)
186         pSourceFormat = &This->timeformat;
187     if (IsEqualIID(pTargetFormat, &TIME_FORMAT_MEDIA_TIME) && IsEqualIID(pSourceFormat, &TIME_FORMAT_MEDIA_TIME))
188     {
189         *pTarget = Source;
190         return S_OK;
191     }
192     /* FIXME: clear pTarget? */
193     return E_INVALIDARG;
194 }
195
196 static inline LONGLONG Adjust(LONGLONG value, const LONGLONG * pModifier, DWORD dwFlags)
197 {
198     switch (dwFlags & AM_SEEKING_PositioningBitsMask)
199     {
200     case AM_SEEKING_NoPositioning:
201         return value;
202     case AM_SEEKING_AbsolutePositioning:
203         return *pModifier;
204     case AM_SEEKING_RelativePositioning:
205     case AM_SEEKING_IncrementalPositioning:
206         return value + *pModifier;
207     default:
208         assert(FALSE);
209         return 0;
210     }
211 }
212
213 HRESULT WINAPI SourceSeekingImpl_SetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, DWORD dwCurrentFlags, LONGLONG * pStop, DWORD dwStopFlags)
214 {
215     SourceSeeking *This = (SourceSeeking *)iface;
216     BOOL bChangeCurrent = FALSE, bChangeStop = FALSE;
217     LONGLONG llNewCurrent, llNewStop;
218
219     TRACE("(%p, %x, %p, %x)\n", pCurrent, dwCurrentFlags, pStop, dwStopFlags);
220     EnterCriticalSection(This->crst);
221
222     llNewCurrent = Adjust(This->llCurrent, pCurrent, dwCurrentFlags);
223     llNewStop = Adjust(This->llStop, pStop, dwStopFlags);
224
225     if (pCurrent)
226         bChangeCurrent = TRUE;
227     if (llNewStop != This->llStop)
228         bChangeStop = TRUE;
229
230     TRACE("Old: %u, New: %u\n", (DWORD)(This->llCurrent/10000000), (DWORD)(llNewCurrent/10000000));
231
232     This->llCurrent = llNewCurrent;
233     This->llStop = llNewStop;
234
235     if (pCurrent && (dwCurrentFlags & AM_SEEKING_ReturnTime))
236         *pCurrent = llNewCurrent;
237     if (pStop && (dwStopFlags & AM_SEEKING_ReturnTime))
238         *pStop = llNewStop;
239     LeaveCriticalSection(This->crst);
240
241     if (bChangeCurrent)
242         This->fnChangeStart(iface);
243     if (bChangeStop)
244         This->fnChangeStop(iface);
245
246     return S_OK;
247 }
248
249 HRESULT WINAPI SourceSeekingImpl_GetPositions(IMediaSeeking * iface, LONGLONG * pCurrent, LONGLONG * pStop)
250 {
251     SourceSeeking *This = (SourceSeeking *)iface;
252
253     TRACE("(%p, %p)\n", pCurrent, pStop);
254
255     EnterCriticalSection(This->crst);
256     IMediaSeeking_GetCurrentPosition(iface, pCurrent);
257     IMediaSeeking_GetStopPosition(iface, pStop);
258     LeaveCriticalSection(This->crst);
259
260     return S_OK;
261 }
262
263 HRESULT WINAPI SourceSeekingImpl_GetAvailable(IMediaSeeking * iface, LONGLONG * pEarliest, LONGLONG * pLatest)
264 {
265     SourceSeeking *This = (SourceSeeking *)iface;
266
267     TRACE("(%p, %p)\n", pEarliest, pLatest);
268
269     EnterCriticalSection(This->crst);
270     *pEarliest = 0;
271     *pLatest = This->llDuration;
272     LeaveCriticalSection(This->crst);
273
274     return S_OK;
275 }
276
277 HRESULT WINAPI SourceSeekingImpl_SetRate(IMediaSeeking * iface, double dRate)
278 {
279     SourceSeeking *This = (SourceSeeking *)iface;
280     BOOL bChangeRate = (dRate != This->dRate);
281     HRESULT hr = S_OK;
282
283     TRACE("(%e)\n", dRate);
284
285     if (dRate > 100 || dRate < .001)
286     {
287         FIXME("Excessive rate %e, ignoring\n", dRate);
288         return VFW_E_UNSUPPORTED_AUDIO;
289     }
290
291     EnterCriticalSection(This->crst);
292     This->dRate = dRate;
293     if (bChangeRate)
294         hr = This->fnChangeRate(iface);
295     LeaveCriticalSection(This->crst);
296
297     return hr;
298 }
299
300 HRESULT WINAPI SourceSeekingImpl_GetRate(IMediaSeeking * iface, double * dRate)
301 {
302     SourceSeeking *This = (SourceSeeking *)iface;
303
304     TRACE("(%p)\n", dRate);
305
306     EnterCriticalSection(This->crst);
307     /* Forward? */
308     *dRate = This->dRate;
309     LeaveCriticalSection(This->crst);
310
311     return S_OK;
312 }
313
314 HRESULT WINAPI SourceSeekingImpl_GetPreroll(IMediaSeeking * iface, LONGLONG * pPreroll)
315 {
316     TRACE("(%p)\n", pPreroll);
317
318     *pPreroll = 0;
319     return S_OK;
320 }