xref: /reactos/dll/directx/wine/qedit/mediadet.c (revision 02e84521)
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     IUnknown IUnknown_inner;
37     IMediaDet IMediaDet_iface;
38     IUnknown *outer_unk;
39     LONG ref;
40     IGraphBuilder *graph;
41     IBaseFilter *source;
42     IBaseFilter *splitter;
43     LONG num_streams;
44     LONG cur_stream;
45     IPin *cur_pin;
46 } MediaDetImpl;
47 
48 static inline MediaDetImpl *impl_from_IUnknown(IUnknown *iface)
49 {
50     return CONTAINING_RECORD(iface, MediaDetImpl, IUnknown_inner);
51 }
52 
53 static inline MediaDetImpl *impl_from_IMediaDet(IMediaDet *iface)
54 {
55     return CONTAINING_RECORD(iface, MediaDetImpl, IMediaDet_iface);
56 }
57 
58 static void MD_cleanup(MediaDetImpl *This)
59 {
60     if (This->cur_pin) IPin_Release(This->cur_pin);
61     This->cur_pin = NULL;
62     if (This->source) IBaseFilter_Release(This->source);
63     This->source = NULL;
64     if (This->splitter) IBaseFilter_Release(This->splitter);
65     This->splitter = NULL;
66     if (This->graph) IGraphBuilder_Release(This->graph);
67     This->graph = NULL;
68     This->num_streams = -1;
69     This->cur_stream = 0;
70 }
71 
72 /* MediaDet inner IUnknown */
73 static HRESULT WINAPI MediaDet_inner_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
74 {
75     MediaDetImpl *This = impl_from_IUnknown(iface);
76 
77     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
78 
79     *ppv = NULL;
80     if (IsEqualIID(riid, &IID_IUnknown))
81         *ppv = &This->IUnknown_inner;
82     else if (IsEqualIID(riid, &IID_IMediaDet))
83         *ppv = &This->IMediaDet_iface;
84     else
85         WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppv);
86 
87     if (!*ppv)
88         return E_NOINTERFACE;
89 
90     IUnknown_AddRef((IUnknown*)*ppv);
91     return S_OK;
92 }
93 
94 static ULONG WINAPI MediaDet_inner_AddRef(IUnknown *iface)
95 {
96     MediaDetImpl *This = impl_from_IUnknown(iface);
97     ULONG ref = InterlockedIncrement(&This->ref);
98 
99     TRACE("(%p) new ref = %u\n", This, ref);
100 
101     return ref;
102 }
103 
104 static ULONG WINAPI MediaDet_inner_Release(IUnknown *iface)
105 {
106     MediaDetImpl *This = impl_from_IUnknown(iface);
107     ULONG ref = InterlockedDecrement(&This->ref);
108 
109     TRACE("(%p) new ref = %u\n", This, ref);
110 
111     if (ref == 0)
112     {
113         MD_cleanup(This);
114         CoTaskMemFree(This);
115     }
116 
117     return ref;
118 }
119 
120 static const IUnknownVtbl mediadet_vtbl =
121 {
122     MediaDet_inner_QueryInterface,
123     MediaDet_inner_AddRef,
124     MediaDet_inner_Release,
125 };
126 
127 /* IMediaDet implementation */
128 static HRESULT WINAPI MediaDet_QueryInterface(IMediaDet *iface, REFIID riid, void **ppv)
129 {
130     MediaDetImpl *This = impl_from_IMediaDet(iface);
131     return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
132 }
133 
134 static ULONG WINAPI MediaDet_AddRef(IMediaDet *iface)
135 {
136     MediaDetImpl *This = impl_from_IMediaDet(iface);
137     return IUnknown_AddRef(This->outer_unk);
138 }
139 
140 static ULONG WINAPI MediaDet_Release(IMediaDet *iface)
141 {
142     MediaDetImpl *This = impl_from_IMediaDet(iface);
143     return IUnknown_Release(This->outer_unk);
144 }
145 
146 static HRESULT WINAPI MediaDet_get_Filter(IMediaDet* iface, IUnknown **pVal)
147 {
148     MediaDetImpl *This = impl_from_IMediaDet(iface);
149     FIXME("(%p)->(%p): not implemented!\n", This, pVal);
150     return E_NOTIMPL;
151 }
152 
153 static HRESULT WINAPI MediaDet_put_Filter(IMediaDet* iface, IUnknown *newVal)
154 {
155     MediaDetImpl *This = impl_from_IMediaDet(iface);
156     FIXME("(%p)->(%p): not implemented!\n", This, newVal);
157     return E_NOTIMPL;
158 }
159 
160 static HRESULT WINAPI MediaDet_get_OutputStreams(IMediaDet* iface, LONG *pVal)
161 {
162     MediaDetImpl *This = impl_from_IMediaDet(iface);
163     IEnumPins *pins;
164     IPin *pin;
165     HRESULT hr;
166 
167     TRACE("(%p)\n", This);
168 
169     if (!This->splitter)
170         return E_INVALIDARG;
171 
172     if (This->num_streams != -1)
173     {
174         *pVal = This->num_streams;
175         return S_OK;
176     }
177 
178     *pVal = 0;
179 
180     hr = IBaseFilter_EnumPins(This->splitter, &pins);
181     if (FAILED(hr))
182         return hr;
183 
184     while (IEnumPins_Next(pins, 1, &pin, NULL) == S_OK)
185     {
186         PIN_DIRECTION dir;
187         hr = IPin_QueryDirection(pin, &dir);
188         IPin_Release(pin);
189         if (FAILED(hr))
190         {
191             IEnumPins_Release(pins);
192             return hr;
193         }
194 
195         if (dir == PINDIR_OUTPUT)
196             ++*pVal;
197     }
198     IEnumPins_Release(pins);
199 
200     This->num_streams = *pVal;
201     return S_OK;
202 }
203 
204 static HRESULT WINAPI MediaDet_get_CurrentStream(IMediaDet* iface, LONG *pVal)
205 {
206     MediaDetImpl *This = impl_from_IMediaDet(iface);
207     TRACE("(%p)\n", This);
208 
209     if (!pVal)
210         return E_POINTER;
211 
212     *pVal = This->cur_stream;
213     return S_OK;
214 }
215 
216 static HRESULT SetCurPin(MediaDetImpl *This, LONG strm)
217 {
218     IEnumPins *pins;
219     IPin *pin;
220     HRESULT hr;
221 
222     assert(This->splitter);
223     assert(0 <= strm && strm < This->num_streams);
224 
225     if (This->cur_pin)
226     {
227         IPin_Release(This->cur_pin);
228         This->cur_pin = NULL;
229     }
230 
231     hr = IBaseFilter_EnumPins(This->splitter, &pins);
232     if (FAILED(hr))
233         return hr;
234 
235     while (IEnumPins_Next(pins, 1, &pin, NULL) == S_OK && !This->cur_pin)
236     {
237         PIN_DIRECTION dir;
238         hr = IPin_QueryDirection(pin, &dir);
239         if (FAILED(hr))
240         {
241             IPin_Release(pin);
242             IEnumPins_Release(pins);
243             return hr;
244         }
245 
246         if (dir == PINDIR_OUTPUT && strm-- == 0)
247             This->cur_pin = pin;
248         else
249             IPin_Release(pin);
250     }
251     IEnumPins_Release(pins);
252 
253     assert(This->cur_pin);
254     return S_OK;
255 }
256 
257 static HRESULT WINAPI MediaDet_put_CurrentStream(IMediaDet* iface, LONG newVal)
258 {
259     MediaDetImpl *This = impl_from_IMediaDet(iface);
260     HRESULT hr;
261 
262     TRACE("(%p)->(%d)\n", This, newVal);
263 
264     if (This->num_streams == -1)
265     {
266         LONG n;
267         hr = MediaDet_get_OutputStreams(iface, &n);
268         if (FAILED(hr))
269             return hr;
270     }
271 
272     if (newVal < 0 || This->num_streams <= newVal)
273         return E_INVALIDARG;
274 
275     hr = SetCurPin(This, newVal);
276     if (FAILED(hr))
277         return hr;
278 
279     This->cur_stream = newVal;
280     return S_OK;
281 }
282 
283 static HRESULT WINAPI MediaDet_get_StreamType(IMediaDet* iface, GUID *pVal)
284 {
285     MediaDetImpl *This = impl_from_IMediaDet(iface);
286     FIXME("(%p)->(%s): not implemented!\n", This, debugstr_guid(pVal));
287     return E_NOTIMPL;
288 }
289 
290 static HRESULT WINAPI MediaDet_get_StreamTypeB(IMediaDet* iface, BSTR *pVal)
291 {
292     MediaDetImpl *This = impl_from_IMediaDet(iface);
293     FIXME("(%p)->(%p): not implemented!\n", This, pVal);
294     return E_NOTIMPL;
295 }
296 
297 static HRESULT WINAPI MediaDet_get_StreamLength(IMediaDet* iface, double *pVal)
298 {
299     MediaDetImpl *This = impl_from_IMediaDet(iface);
300     FIXME("(%p): stub!\n", This);
301     return VFW_E_INVALIDMEDIATYPE;
302 }
303 
304 static HRESULT WINAPI MediaDet_get_Filename(IMediaDet* iface, BSTR *pVal)
305 {
306     MediaDetImpl *This = impl_from_IMediaDet(iface);
307     IFileSourceFilter *file;
308     LPOLESTR name;
309     HRESULT hr;
310 
311     TRACE("(%p)\n", This);
312 
313     if (!pVal)
314         return E_POINTER;
315 
316     *pVal = NULL;
317     /* MSDN says it should return E_FAIL if no file is open, but tests
318        show otherwise.  */
319     if (!This->source)
320         return S_OK;
321 
322     hr = IBaseFilter_QueryInterface(This->source, &IID_IFileSourceFilter,
323                                     (void **) &file);
324     if (FAILED(hr))
325         return hr;
326 
327     hr = IFileSourceFilter_GetCurFile(file, &name, NULL);
328     IFileSourceFilter_Release(file);
329     if (FAILED(hr))
330         return hr;
331 
332     *pVal = SysAllocString(name);
333     CoTaskMemFree(name);
334     if (!*pVal)
335         return E_OUTOFMEMORY;
336 
337     return S_OK;
338 }
339 
340 /* From quartz, 2008/04/07 */
341 static HRESULT GetFilterInfo(IMoniker *pMoniker, GUID *pclsid, VARIANT *pvar)
342 {
343     static const WCHAR wszClsidName[] = {'C','L','S','I','D',0};
344     static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0};
345     IPropertyBag *pPropBagCat = NULL;
346     HRESULT hr;
347 
348     VariantInit(pvar);
349     V_VT(pvar) = VT_BSTR;
350 
351     hr = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag,
352                                 (LPVOID *) &pPropBagCat);
353 
354     if (SUCCEEDED(hr))
355         hr = IPropertyBag_Read(pPropBagCat, wszClsidName, pvar, NULL);
356 
357     if (SUCCEEDED(hr))
358     {
359         hr = CLSIDFromString(V_BSTR(pvar), pclsid);
360         VariantClear(pvar);
361         V_VT(pvar) = VT_BSTR;
362     }
363 
364     if (SUCCEEDED(hr))
365         hr = IPropertyBag_Read(pPropBagCat, wszFriendlyName, pvar, NULL);
366 
367     if (SUCCEEDED(hr))
368         TRACE("Moniker = %s - %s\n", debugstr_guid(pclsid), debugstr_w(V_BSTR(pvar)));
369 
370     if (pPropBagCat)
371         IPropertyBag_Release(pPropBagCat);
372 
373     return hr;
374 }
375 
376 static HRESULT GetSplitter(MediaDetImpl *This)
377 {
378     IFileSourceFilter *file;
379     LPOLESTR name;
380     AM_MEDIA_TYPE mt;
381     GUID type[2];
382     IFilterMapper2 *map;
383     IEnumMoniker *filters;
384     IMoniker *mon;
385     VARIANT var;
386     GUID clsid;
387     IBaseFilter *splitter;
388     IEnumPins *pins;
389     IPin *source_pin, *splitter_pin;
390     HRESULT hr;
391 
392     hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
393                           &IID_IFilterMapper2, (void **) &map);
394     if (FAILED(hr))
395         return hr;
396 
397     hr = IBaseFilter_QueryInterface(This->source, &IID_IFileSourceFilter,
398                                     (void **) &file);
399     if (FAILED(hr))
400     {
401         IFilterMapper2_Release(map);
402         return hr;
403     }
404 
405     hr = IFileSourceFilter_GetCurFile(file, &name, &mt);
406     IFileSourceFilter_Release(file);
407     CoTaskMemFree(name);
408     if (FAILED(hr))
409     {
410         IFilterMapper2_Release(map);
411         return hr;
412     }
413     type[0] = mt.majortype;
414     type[1] = mt.subtype;
415     CoTaskMemFree(mt.pbFormat);
416 
417     hr = IFilterMapper2_EnumMatchingFilters(map, &filters, 0, TRUE,
418                                             MERIT_UNLIKELY, FALSE, 1, type,
419                                             NULL, NULL, FALSE, TRUE,
420                                             0, NULL, NULL, NULL);
421     IFilterMapper2_Release(map);
422     if (FAILED(hr))
423         return hr;
424 
425     hr = E_NOINTERFACE;
426     while (IEnumMoniker_Next(filters, 1, &mon, NULL) == S_OK)
427     {
428         hr = GetFilterInfo(mon, &clsid, &var);
429         IMoniker_Release(mon);
430         if (FAILED(hr))
431             continue;
432 
433         hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER,
434                               &IID_IBaseFilter, (void **) &splitter);
435         if (FAILED(hr))
436         {
437             VariantClear(&var);
438             continue;
439         }
440 
441         hr = IGraphBuilder_AddFilter(This->graph, splitter, V_BSTR(&var));
442         VariantClear(&var);
443         This->splitter = splitter;
444         if (FAILED(hr))
445             goto retry;
446 
447         hr = IBaseFilter_EnumPins(This->source, &pins);
448         if (FAILED(hr))
449             goto retry;
450         IEnumPins_Next(pins, 1, &source_pin, NULL);
451         IEnumPins_Release(pins);
452 
453         hr = IBaseFilter_EnumPins(splitter, &pins);
454         if (FAILED(hr))
455         {
456             IPin_Release(source_pin);
457             goto retry;
458         }
459         IEnumPins_Next(pins, 1, &splitter_pin, NULL);
460         IEnumPins_Release(pins);
461 
462         hr = IPin_Connect(source_pin, splitter_pin, NULL);
463         IPin_Release(source_pin);
464         IPin_Release(splitter_pin);
465         if (SUCCEEDED(hr))
466             break;
467 
468 retry:
469         IBaseFilter_Release(splitter);
470         This->splitter = NULL;
471     }
472 
473     IEnumMoniker_Release(filters);
474     if (FAILED(hr))
475         return hr;
476 
477     return S_OK;
478 }
479 
480 static HRESULT WINAPI MediaDet_put_Filename(IMediaDet* iface, BSTR newVal)
481 {
482     static const WCHAR reader[] = {'R','e','a','d','e','r',0};
483     MediaDetImpl *This = impl_from_IMediaDet(iface);
484     IGraphBuilder *gb;
485     IBaseFilter *bf;
486     HRESULT hr;
487 
488     TRACE("(%p)->(%s)\n", This, debugstr_w(newVal));
489 
490     if (This->graph)
491     {
492         WARN("MSDN says not to call this method twice\n");
493         MD_cleanup(This);
494     }
495 
496     hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
497                           &IID_IGraphBuilder, (void **) &gb);
498     if (FAILED(hr))
499         return hr;
500 
501     hr = IGraphBuilder_AddSourceFilter(gb, newVal, reader, &bf);
502     if (FAILED(hr))
503     {
504         IGraphBuilder_Release(gb);
505         return hr;
506     }
507 
508     This->graph = gb;
509     This->source = bf;
510     hr = GetSplitter(This);
511     if (FAILED(hr))
512         return hr;
513 
514     return MediaDet_put_CurrentStream(iface, 0);
515 }
516 
517 static HRESULT WINAPI MediaDet_GetBitmapBits(IMediaDet* iface,
518                                              double StreamTime,
519                                              LONG *pBufferSize, char *pBuffer,
520                                              LONG Width, LONG Height)
521 {
522     MediaDetImpl *This = impl_from_IMediaDet(iface);
523     FIXME("(%p)->(%f %p %p %d %d): not implemented!\n", This, StreamTime, pBufferSize, pBuffer,
524           Width, Height);
525     return E_NOTIMPL;
526 }
527 
528 static HRESULT WINAPI MediaDet_WriteBitmapBits(IMediaDet* iface,
529                                                double StreamTime, LONG Width,
530                                                LONG Height, BSTR Filename)
531 {
532     MediaDetImpl *This = impl_from_IMediaDet(iface);
533     FIXME("(%p)->(%f %d %d %p): not implemented!\n", This, StreamTime, Width, Height, Filename);
534     return E_NOTIMPL;
535 }
536 
537 static HRESULT WINAPI MediaDet_get_StreamMediaType(IMediaDet* iface,
538                                                    AM_MEDIA_TYPE *pVal)
539 {
540     MediaDetImpl *This = impl_from_IMediaDet(iface);
541     IEnumMediaTypes *types;
542     AM_MEDIA_TYPE *pmt;
543     HRESULT hr;
544 
545     TRACE("(%p)\n", This);
546 
547     if (!pVal)
548         return E_POINTER;
549 
550     if (!This->cur_pin)
551         return E_INVALIDARG;
552 
553     hr = IPin_EnumMediaTypes(This->cur_pin, &types);
554     if (SUCCEEDED(hr))
555     {
556         hr = (IEnumMediaTypes_Next(types, 1, &pmt, NULL) == S_OK
557               ? S_OK
558               : E_NOINTERFACE);
559         IEnumMediaTypes_Release(types);
560     }
561 
562     if (SUCCEEDED(hr))
563     {
564         *pVal = *pmt;
565         CoTaskMemFree(pmt);
566     }
567 
568     return hr;
569 }
570 
571 static HRESULT WINAPI MediaDet_GetSampleGrabber(IMediaDet* iface,
572                                                 ISampleGrabber **ppVal)
573 {
574     MediaDetImpl *This = impl_from_IMediaDet(iface);
575     FIXME("(%p)->(%p): not implemented!\n", This, ppVal);
576     return E_NOTIMPL;
577 }
578 
579 static HRESULT WINAPI MediaDet_get_FrameRate(IMediaDet* iface, double *pVal)
580 {
581     MediaDetImpl *This = impl_from_IMediaDet(iface);
582     AM_MEDIA_TYPE mt;
583     VIDEOINFOHEADER *vh;
584     HRESULT hr;
585 
586     TRACE("(%p)\n", This);
587 
588     if (!pVal)
589         return E_POINTER;
590 
591     hr = MediaDet_get_StreamMediaType(iface, &mt);
592     if (FAILED(hr))
593         return hr;
594 
595     if (!IsEqualGUID(&mt.majortype, &MEDIATYPE_Video))
596     {
597         CoTaskMemFree(mt.pbFormat);
598         return VFW_E_INVALIDMEDIATYPE;
599     }
600 
601     vh = (VIDEOINFOHEADER *) mt.pbFormat;
602     *pVal = 1.0e7 / (double) vh->AvgTimePerFrame;
603 
604     CoTaskMemFree(mt.pbFormat);
605     return S_OK;
606 }
607 
608 static HRESULT WINAPI MediaDet_EnterBitmapGrabMode(IMediaDet* iface,
609                                                    double SeekTime)
610 {
611     MediaDetImpl *This = impl_from_IMediaDet(iface);
612     FIXME("(%p)->(%f): not implemented!\n", This, SeekTime);
613     return E_NOTIMPL;
614 }
615 
616 static const IMediaDetVtbl IMediaDet_VTable =
617 {
618     MediaDet_QueryInterface,
619     MediaDet_AddRef,
620     MediaDet_Release,
621     MediaDet_get_Filter,
622     MediaDet_put_Filter,
623     MediaDet_get_OutputStreams,
624     MediaDet_get_CurrentStream,
625     MediaDet_put_CurrentStream,
626     MediaDet_get_StreamType,
627     MediaDet_get_StreamTypeB,
628     MediaDet_get_StreamLength,
629     MediaDet_get_Filename,
630     MediaDet_put_Filename,
631     MediaDet_GetBitmapBits,
632     MediaDet_WriteBitmapBits,
633     MediaDet_get_StreamMediaType,
634     MediaDet_GetSampleGrabber,
635     MediaDet_get_FrameRate,
636     MediaDet_EnterBitmapGrabMode,
637 };
638 
639 HRESULT MediaDet_create(IUnknown * pUnkOuter, LPVOID * ppv) {
640     MediaDetImpl* obj = NULL;
641 
642     TRACE("(%p,%p)\n", pUnkOuter, ppv);
643 
644     obj = CoTaskMemAlloc(sizeof(MediaDetImpl));
645     if (NULL == obj) {
646         *ppv = NULL;
647         return E_OUTOFMEMORY;
648     }
649     ZeroMemory(obj, sizeof(MediaDetImpl));
650 
651     obj->ref = 1;
652     obj->IUnknown_inner.lpVtbl = &mediadet_vtbl;
653     obj->IMediaDet_iface.lpVtbl = &IMediaDet_VTable;
654     obj->graph = NULL;
655     obj->source = NULL;
656     obj->splitter = NULL;
657     obj->cur_pin = NULL;
658     obj->num_streams = -1;
659     obj->cur_stream = 0;
660 
661     if (pUnkOuter)
662         obj->outer_unk = pUnkOuter;
663     else
664         obj->outer_unk = &obj->IUnknown_inner;
665 
666     *ppv = &obj->IUnknown_inner;
667     return S_OK;
668 }
669