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