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