[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_BSTR(pvar), 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), debugstr_w(V_BSTR(pvar)));
360
361 if (pPropBagCat)
362 IPropertyBag_Release(pPropBagCat);
363
364 return hr;
365 }
366
367 static HRESULT GetSplitter(MediaDetImpl *This)
368 {
369 IFileSourceFilter *file;
370 LPOLESTR name;
371 AM_MEDIA_TYPE mt;
372 GUID type[2];
373 IFilterMapper2 *map;
374 IEnumMoniker *filters;
375 IMoniker *mon;
376 VARIANT var;
377 GUID clsid;
378 IBaseFilter *splitter;
379 IEnumPins *pins;
380 IPin *source_pin, *splitter_pin;
381 HRESULT hr;
382
383 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
384 &IID_IFilterMapper2, (void **) &map);
385 if (FAILED(hr))
386 return hr;
387
388 hr = IBaseFilter_QueryInterface(This->source, &IID_IFileSourceFilter,
389 (void **) &file);
390 if (FAILED(hr))
391 {
392 IFilterMapper2_Release(map);
393 return hr;
394 }
395
396 hr = IFileSourceFilter_GetCurFile(file, &name, &mt);
397 IFileSourceFilter_Release(file);
398 CoTaskMemFree(name);
399 if (FAILED(hr))
400 {
401 IFilterMapper2_Release(map);
402 return hr;
403 }
404 type[0] = mt.majortype;
405 type[1] = mt.subtype;
406 CoTaskMemFree(mt.pbFormat);
407
408 hr = IFilterMapper2_EnumMatchingFilters(map, &filters, 0, TRUE,
409 MERIT_UNLIKELY, FALSE, 1, type,
410 NULL, NULL, FALSE, TRUE,
411 0, NULL, NULL, NULL);
412 IFilterMapper2_Release(map);
413 if (FAILED(hr))
414 return hr;
415
416 hr = E_NOINTERFACE;
417 while (IEnumMoniker_Next(filters, 1, &mon, NULL) == S_OK)
418 {
419 hr = GetFilterInfo(mon, &clsid, &var);
420 IMoniker_Release(mon);
421 if (FAILED(hr))
422 continue;
423
424 hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER,
425 &IID_IBaseFilter, (void **) &splitter);
426 if (FAILED(hr))
427 {
428 VariantClear(&var);
429 continue;
430 }
431
432 hr = IGraphBuilder_AddFilter(This->graph, splitter, V_BSTR(&var));
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 }