pdh/tests: Rename a counter to make the purpose of the test immediately clear.
[wine] / dlls / qedit / mediadet.c
1 /*              DirectShow Media Detector object (QEDIT.DLL)
2  *
3  * Copyright 2008 Google (Lei Zhang, Dan Hipschman)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include <assert.h>
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "ole2.h"
29
30 #include "qedit_private.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(qedit);
34
35 typedef struct MediaDetImpl {
36     IMediaDet IMediaDet_iface;
37     LONG refCount;
38     IGraphBuilder *graph;
39     IBaseFilter *source;
40     IBaseFilter *splitter;
41     LONG num_streams;
42     LONG cur_stream;
43     IPin *cur_pin;
44 } MediaDetImpl;
45
46 static inline MediaDetImpl *impl_from_IMediaDet(IMediaDet *iface)
47 {
48     return CONTAINING_RECORD(iface, MediaDetImpl, IMediaDet_iface);
49 }
50
51 static void MD_cleanup(MediaDetImpl *This)
52 {
53     if (This->cur_pin) IPin_Release(This->cur_pin);
54     This->cur_pin = NULL;
55     if (This->source) IBaseFilter_Release(This->source);
56     This->source = NULL;
57     if (This->splitter) IBaseFilter_Release(This->splitter);
58     This->splitter = NULL;
59     if (This->graph) IGraphBuilder_Release(This->graph);
60     This->graph = NULL;
61     This->num_streams = -1;
62     This->cur_stream = 0;
63 }
64
65 static ULONG WINAPI MediaDet_AddRef(IMediaDet* iface)
66 {
67     MediaDetImpl *This = impl_from_IMediaDet(iface);
68     ULONG refCount = InterlockedIncrement(&This->refCount);
69     TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
70     return refCount;
71 }
72
73 static ULONG WINAPI MediaDet_Release(IMediaDet* iface)
74 {
75     MediaDetImpl *This = impl_from_IMediaDet(iface);
76     ULONG refCount = InterlockedDecrement(&This->refCount);
77     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
78
79     if (refCount == 0)
80     {
81         MD_cleanup(This);
82         CoTaskMemFree(This);
83         return 0;
84     }
85
86     return refCount;
87 }
88
89 static HRESULT WINAPI MediaDet_QueryInterface(IMediaDet* iface, REFIID riid,
90                                               void **ppvObject)
91 {
92     MediaDetImpl *This = impl_from_IMediaDet(iface);
93     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
94
95     if (IsEqualIID(riid, &IID_IUnknown) ||
96         IsEqualIID(riid, &IID_IMediaDet)) {
97         MediaDet_AddRef(iface);
98         *ppvObject = This;
99         return S_OK;
100     }
101     *ppvObject = NULL;
102     WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppvObject);
103     return E_NOINTERFACE;
104 }
105
106 static HRESULT WINAPI MediaDet_get_Filter(IMediaDet* iface, IUnknown **pVal)
107 {
108     MediaDetImpl *This = impl_from_IMediaDet(iface);
109     FIXME("(%p)->(%p): not implemented!\n", This, pVal);
110     return E_NOTIMPL;
111 }
112
113 static HRESULT WINAPI MediaDet_put_Filter(IMediaDet* iface, IUnknown *newVal)
114 {
115     MediaDetImpl *This = impl_from_IMediaDet(iface);
116     FIXME("(%p)->(%p): not implemented!\n", This, newVal);
117     return E_NOTIMPL;
118 }
119
120 static HRESULT WINAPI MediaDet_get_OutputStreams(IMediaDet* iface, LONG *pVal)
121 {
122     MediaDetImpl *This = impl_from_IMediaDet(iface);
123     IEnumPins *pins;
124     IPin *pin;
125     HRESULT hr;
126
127     TRACE("(%p)\n", This);
128
129     if (!This->splitter)
130         return E_INVALIDARG;
131
132     if (This->num_streams != -1)
133     {
134         *pVal = This->num_streams;
135         return S_OK;
136     }
137
138     *pVal = 0;
139
140     hr = IBaseFilter_EnumPins(This->splitter, &pins);
141     if (FAILED(hr))
142         return hr;
143
144     while (IEnumPins_Next(pins, 1, &pin, NULL) == S_OK)
145     {
146         PIN_DIRECTION dir;
147         hr = IPin_QueryDirection(pin, &dir);
148         IPin_Release(pin);
149         if (FAILED(hr))
150         {
151             IEnumPins_Release(pins);
152             return hr;
153         }
154
155         if (dir == PINDIR_OUTPUT)
156             ++*pVal;
157     }
158     IEnumPins_Release(pins);
159
160     This->num_streams = *pVal;
161     return S_OK;
162 }
163
164 static HRESULT WINAPI MediaDet_get_CurrentStream(IMediaDet* iface, LONG *pVal)
165 {
166     MediaDetImpl *This = impl_from_IMediaDet(iface);
167     TRACE("(%p)\n", This);
168
169     if (!pVal)
170         return E_POINTER;
171
172     *pVal = This->cur_stream;
173     return S_OK;
174 }
175
176 static HRESULT SetCurPin(MediaDetImpl *This, LONG strm)
177 {
178     IEnumPins *pins;
179     IPin *pin;
180     HRESULT hr;
181
182     assert(This->splitter);
183     assert(0 <= strm && strm < This->num_streams);
184
185     if (This->cur_pin)
186     {
187         IPin_Release(This->cur_pin);
188         This->cur_pin = NULL;
189     }
190
191     hr = IBaseFilter_EnumPins(This->splitter, &pins);
192     if (FAILED(hr))
193         return hr;
194
195     while (IEnumPins_Next(pins, 1, &pin, NULL) == S_OK && !This->cur_pin)
196     {
197         PIN_DIRECTION dir;
198         hr = IPin_QueryDirection(pin, &dir);
199         if (FAILED(hr))
200         {
201             IPin_Release(pin);
202             IEnumPins_Release(pins);
203             return hr;
204         }
205
206         if (dir == PINDIR_OUTPUT && strm-- == 0)
207             This->cur_pin = pin;
208         else
209             IPin_Release(pin);
210     }
211     IEnumPins_Release(pins);
212
213     assert(This->cur_pin);
214     return S_OK;
215 }
216
217 static HRESULT WINAPI MediaDet_put_CurrentStream(IMediaDet* iface, LONG newVal)
218 {
219     MediaDetImpl *This = impl_from_IMediaDet(iface);
220     HRESULT hr;
221
222     TRACE("(%p)->(%d)\n", This, newVal);
223
224     if (This->num_streams == -1)
225     {
226         LONG n;
227         hr = MediaDet_get_OutputStreams(iface, &n);
228         if (FAILED(hr))
229             return hr;
230     }
231
232     if (newVal < 0 || This->num_streams <= newVal)
233         return E_INVALIDARG;
234
235     hr = SetCurPin(This, newVal);
236     if (FAILED(hr))
237         return hr;
238
239     This->cur_stream = newVal;
240     return S_OK;
241 }
242
243 static HRESULT WINAPI MediaDet_get_StreamType(IMediaDet* iface, GUID *pVal)
244 {
245     MediaDetImpl *This = impl_from_IMediaDet(iface);
246     FIXME("(%p)->(%p): not implemented!\n", This, debugstr_guid(pVal));
247     return E_NOTIMPL;
248 }
249
250 static HRESULT WINAPI MediaDet_get_StreamTypeB(IMediaDet* iface, BSTR *pVal)
251 {
252     MediaDetImpl *This = impl_from_IMediaDet(iface);
253     FIXME("(%p)->(%p): not implemented!\n", This, pVal);
254     return E_NOTIMPL;
255 }
256
257 static HRESULT WINAPI MediaDet_get_StreamLength(IMediaDet* iface, double *pVal)
258 {
259     MediaDetImpl *This = impl_from_IMediaDet(iface);
260     FIXME("(%p): stub!\n", This);
261     return VFW_E_INVALIDMEDIATYPE;
262 }
263
264 static HRESULT WINAPI MediaDet_get_Filename(IMediaDet* iface, BSTR *pVal)
265 {
266     MediaDetImpl *This = impl_from_IMediaDet(iface);
267     IFileSourceFilter *file;
268     LPOLESTR name;
269     HRESULT hr;
270
271     TRACE("(%p)\n", This);
272
273     if (!pVal)
274         return E_POINTER;
275
276     *pVal = NULL;
277     /* MSDN says it should return E_FAIL if no file is open, but tests
278        show otherwise.  */
279     if (!This->source)
280         return S_OK;
281
282     hr = IBaseFilter_QueryInterface(This->source, &IID_IFileSourceFilter,
283                                     (void **) &file);
284     if (FAILED(hr))
285         return hr;
286
287     hr = IFileSourceFilter_GetCurFile(file, &name, NULL);
288     IFileSourceFilter_Release(file);
289     if (FAILED(hr))
290         return hr;
291
292     *pVal = SysAllocString(name);
293     CoTaskMemFree(name);
294     if (!*pVal)
295         return E_OUTOFMEMORY;
296
297     return S_OK;
298 }
299
300 /* From quartz, 2008/04/07 */
301 static HRESULT GetFilterInfo(IMoniker *pMoniker, GUID *pclsid, VARIANT *pvar)
302 {
303     static const WCHAR wszClsidName[] = {'C','L','S','I','D',0};
304     static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0};
305     IPropertyBag *pPropBagCat = NULL;
306     HRESULT hr;
307
308     VariantInit(pvar);
309     V_VT(pvar) = VT_BSTR;
310
311     hr = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag,
312                                 (LPVOID *) &pPropBagCat);
313
314     if (SUCCEEDED(hr))
315         hr = IPropertyBag_Read(pPropBagCat, wszClsidName, pvar, NULL);
316
317     if (SUCCEEDED(hr))
318     {
319         hr = CLSIDFromString(V_UNION(pvar, bstrVal), pclsid);
320         VariantClear(pvar);
321         V_VT(pvar) = VT_BSTR;
322     }
323
324     if (SUCCEEDED(hr))
325         hr = IPropertyBag_Read(pPropBagCat, wszFriendlyName, pvar, NULL);
326
327     if (SUCCEEDED(hr))
328         TRACE("Moniker = %s - %s\n", debugstr_guid(pclsid),
329               debugstr_w(V_UNION(pvar, bstrVal)));
330
331     if (pPropBagCat)
332         IPropertyBag_Release(pPropBagCat);
333
334     return hr;
335 }
336
337 static HRESULT GetSplitter(MediaDetImpl *This)
338 {
339     IFileSourceFilter *file;
340     LPOLESTR name;
341     AM_MEDIA_TYPE mt;
342     GUID type[2];
343     IFilterMapper2 *map;
344     IEnumMoniker *filters;
345     IMoniker *mon;
346     VARIANT var;
347     GUID clsid;
348     IBaseFilter *splitter;
349     IEnumPins *pins;
350     IPin *source_pin, *splitter_pin;
351     HRESULT hr;
352
353     hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
354                           &IID_IFilterMapper2, (void **) &map);
355     if (FAILED(hr))
356         return hr;
357
358     hr = IBaseFilter_QueryInterface(This->source, &IID_IFileSourceFilter,
359                                     (void **) &file);
360     if (FAILED(hr))
361     {
362         IFilterMapper2_Release(map);
363         return hr;
364     }
365
366     hr = IFileSourceFilter_GetCurFile(file, &name, &mt);
367     IFileSourceFilter_Release(file);
368     CoTaskMemFree(name);
369     if (FAILED(hr))
370     {
371         IFilterMapper2_Release(map);
372         return hr;
373     }
374     type[0] = mt.majortype;
375     type[1] = mt.subtype;
376     CoTaskMemFree(mt.pbFormat);
377
378     hr = IFilterMapper2_EnumMatchingFilters(map, &filters, 0, TRUE,
379                                             MERIT_UNLIKELY, FALSE, 1, type,
380                                             NULL, NULL, FALSE, TRUE,
381                                             0, NULL, NULL, NULL);
382     IFilterMapper2_Release(map);
383     if (FAILED(hr))
384         return hr;
385
386     hr = E_NOINTERFACE;
387     while (IEnumMoniker_Next(filters, 1, &mon, NULL) == S_OK)
388     {
389         hr = GetFilterInfo(mon, &clsid, &var);
390         IMoniker_Release(mon);
391         if (FAILED(hr))
392             continue;
393
394         hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER,
395                               &IID_IBaseFilter, (void **) &splitter);
396         if (FAILED(hr))
397         {
398             VariantClear(&var);
399             goto retry;
400         }
401
402         hr = IGraphBuilder_AddFilter(This->graph, splitter,
403                                      V_UNION(&var, bstrVal));
404         VariantClear(&var);
405         This->splitter = splitter;
406         if (FAILED(hr))
407             goto retry;
408
409         hr = IBaseFilter_EnumPins(This->source, &pins);
410         if (FAILED(hr))
411             goto retry;
412         IEnumPins_Next(pins, 1, &source_pin, NULL);
413         IEnumPins_Release(pins);
414
415         hr = IBaseFilter_EnumPins(splitter, &pins);
416         if (FAILED(hr))
417         {
418             IPin_Release(source_pin);
419             goto retry;
420         }
421         IEnumPins_Next(pins, 1, &splitter_pin, NULL);
422         IEnumPins_Release(pins);
423
424         hr = IPin_Connect(source_pin, splitter_pin, NULL);
425         IPin_Release(source_pin);
426         IPin_Release(splitter_pin);
427         if (SUCCEEDED(hr))
428             break;
429
430 retry:
431         IBaseFilter_Release(splitter);
432         This->splitter = NULL;
433     }
434
435     IEnumMoniker_Release(filters);
436     if (FAILED(hr))
437         return hr;
438
439     return S_OK;
440 }
441
442 static HRESULT WINAPI MediaDet_put_Filename(IMediaDet* iface, BSTR newVal)
443 {
444     static const WCHAR reader[] = {'R','e','a','d','e','r',0};
445     MediaDetImpl *This = impl_from_IMediaDet(iface);
446     IGraphBuilder *gb;
447     IBaseFilter *bf;
448     HRESULT hr;
449
450     TRACE("(%p)->(%s)\n", This, debugstr_w(newVal));
451
452     if (This->graph)
453     {
454         WARN("MSDN says not to call this method twice\n");
455         MD_cleanup(This);
456     }
457
458     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
459                           &IID_IGraphBuilder, (void **) &gb);
460     if (FAILED(hr))
461         return hr;
462
463     hr = IGraphBuilder_AddSourceFilter(gb, newVal, reader, &bf);
464     if (FAILED(hr))
465     {
466         IGraphBuilder_Release(gb);
467         return hr;
468     }
469
470     This->graph = gb;
471     This->source = bf;
472     hr = GetSplitter(This);
473     if (FAILED(hr))
474         return hr;
475
476     return MediaDet_put_CurrentStream(iface, 0);
477 }
478
479 static HRESULT WINAPI MediaDet_GetBitmapBits(IMediaDet* iface,
480                                              double StreamTime,
481                                              LONG *pBufferSize, char *pBuffer,
482                                              LONG Width, LONG Height)
483 {
484     MediaDetImpl *This = impl_from_IMediaDet(iface);
485     FIXME("(%p)->(%f %p %p %d %d): not implemented!\n", This, StreamTime, pBufferSize, pBuffer,
486           Width, Height);
487     return E_NOTIMPL;
488 }
489
490 static HRESULT WINAPI MediaDet_WriteBitmapBits(IMediaDet* iface,
491                                                double StreamTime, LONG Width,
492                                                LONG Height, BSTR Filename)
493 {
494     MediaDetImpl *This = impl_from_IMediaDet(iface);
495     FIXME("(%p)->(%f %d %d %p): not implemented!\n", This, StreamTime, Width, Height, Filename);
496     return E_NOTIMPL;
497 }
498
499 static HRESULT WINAPI MediaDet_get_StreamMediaType(IMediaDet* iface,
500                                                    AM_MEDIA_TYPE *pVal)
501 {
502     MediaDetImpl *This = impl_from_IMediaDet(iface);
503     IEnumMediaTypes *types;
504     AM_MEDIA_TYPE *pmt;
505     HRESULT hr;
506
507     TRACE("(%p)\n", This);
508
509     if (!pVal)
510         return E_POINTER;
511
512     if (!This->cur_pin)
513         return E_INVALIDARG;
514
515     hr = IPin_EnumMediaTypes(This->cur_pin, &types);
516     if (SUCCEEDED(hr))
517     {
518         hr = (IEnumMediaTypes_Next(types, 1, &pmt, NULL) == S_OK
519               ? S_OK
520               : E_NOINTERFACE);
521         IEnumMediaTypes_Release(types);
522     }
523
524     if (SUCCEEDED(hr))
525     {
526         *pVal = *pmt;
527         CoTaskMemFree(pmt);
528     }
529
530     return hr;
531 }
532
533 static HRESULT WINAPI MediaDet_GetSampleGrabber(IMediaDet* iface,
534                                                 ISampleGrabber **ppVal)
535 {
536     MediaDetImpl *This = impl_from_IMediaDet(iface);
537     FIXME("(%p)->(%p): not implemented!\n", This, ppVal);
538     return E_NOTIMPL;
539 }
540
541 static HRESULT WINAPI MediaDet_get_FrameRate(IMediaDet* iface, double *pVal)
542 {
543     MediaDetImpl *This = impl_from_IMediaDet(iface);
544     AM_MEDIA_TYPE mt;
545     VIDEOINFOHEADER *vh;
546     HRESULT hr;
547
548     TRACE("(%p)\n", This);
549
550     if (!pVal)
551         return E_POINTER;
552
553     hr = MediaDet_get_StreamMediaType(iface, &mt);
554     if (FAILED(hr))
555         return hr;
556
557     if (!IsEqualGUID(&mt.majortype, &MEDIATYPE_Video))
558     {
559         CoTaskMemFree(mt.pbFormat);
560         return VFW_E_INVALIDMEDIATYPE;
561     }
562
563     vh = (VIDEOINFOHEADER *) mt.pbFormat;
564     *pVal = 1.0e7 / (double) vh->AvgTimePerFrame;
565
566     CoTaskMemFree(mt.pbFormat);
567     return S_OK;
568 }
569
570 static HRESULT WINAPI MediaDet_EnterBitmapGrabMode(IMediaDet* iface,
571                                                    double SeekTime)
572 {
573     MediaDetImpl *This = impl_from_IMediaDet(iface);
574     FIXME("(%p)->(%f): not implemented!\n", This, SeekTime);
575     return E_NOTIMPL;
576 }
577
578 static const IMediaDetVtbl IMediaDet_VTable =
579 {
580     MediaDet_QueryInterface,
581     MediaDet_AddRef,
582     MediaDet_Release,
583     MediaDet_get_Filter,
584     MediaDet_put_Filter,
585     MediaDet_get_OutputStreams,
586     MediaDet_get_CurrentStream,
587     MediaDet_put_CurrentStream,
588     MediaDet_get_StreamType,
589     MediaDet_get_StreamTypeB,
590     MediaDet_get_StreamLength,
591     MediaDet_get_Filename,
592     MediaDet_put_Filename,
593     MediaDet_GetBitmapBits,
594     MediaDet_WriteBitmapBits,
595     MediaDet_get_StreamMediaType,
596     MediaDet_GetSampleGrabber,
597     MediaDet_get_FrameRate,
598     MediaDet_EnterBitmapGrabMode,
599 };
600
601 HRESULT MediaDet_create(IUnknown * pUnkOuter, LPVOID * ppv) {
602     MediaDetImpl* obj = NULL;
603
604     TRACE("(%p,%p)\n", ppv, pUnkOuter);
605
606     if (pUnkOuter)
607         return CLASS_E_NOAGGREGATION;
608
609     obj = CoTaskMemAlloc(sizeof(MediaDetImpl));
610     if (NULL == obj) {
611         *ppv = NULL;
612         return E_OUTOFMEMORY;
613     }
614     ZeroMemory(obj, sizeof(MediaDetImpl));
615
616     obj->refCount = 1;
617     obj->IMediaDet_iface.lpVtbl = &IMediaDet_VTable;
618     obj->graph = NULL;
619     obj->source = NULL;
620     obj->splitter = NULL;
621     obj->cur_pin = NULL;
622     obj->num_streams = -1;
623     obj->cur_stream = 0;
624     *ppv = obj;
625
626     return S_OK;
627 }