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