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
impl_from_IUnknown(IUnknown * iface)48 static inline MediaDetImpl *impl_from_IUnknown(IUnknown *iface)
49 {
50 return CONTAINING_RECORD(iface, MediaDetImpl, IUnknown_inner);
51 }
52
impl_from_IMediaDet(IMediaDet * iface)53 static inline MediaDetImpl *impl_from_IMediaDet(IMediaDet *iface)
54 {
55 return CONTAINING_RECORD(iface, MediaDetImpl, IMediaDet_iface);
56 }
57
MD_cleanup(MediaDetImpl * This)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 */
MediaDet_inner_QueryInterface(IUnknown * iface,REFIID riid,void ** ppv)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
MediaDet_inner_AddRef(IUnknown * iface)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
MediaDet_inner_Release(IUnknown * iface)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 */
MediaDet_QueryInterface(IMediaDet * iface,REFIID riid,void ** ppv)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
MediaDet_AddRef(IMediaDet * iface)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
MediaDet_Release(IMediaDet * iface)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
MediaDet_get_Filter(IMediaDet * iface,IUnknown ** pVal)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
MediaDet_put_Filter(IMediaDet * iface,IUnknown * newVal)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
MediaDet_get_OutputStreams(IMediaDet * iface,LONG * pVal)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
MediaDet_get_CurrentStream(IMediaDet * iface,LONG * pVal)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
SetCurPin(MediaDetImpl * This,LONG strm)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
MediaDet_put_CurrentStream(IMediaDet * iface,LONG newVal)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
MediaDet_get_StreamType(IMediaDet * iface,GUID * pVal)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
MediaDet_get_StreamTypeB(IMediaDet * iface,BSTR * pVal)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
MediaDet_get_StreamLength(IMediaDet * iface,double * pVal)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
MediaDet_get_Filename(IMediaDet * iface,BSTR * pVal)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 */
GetFilterInfo(IMoniker * pMoniker,GUID * pclsid,VARIANT * pvar)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
GetSplitter(MediaDetImpl * This)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
MediaDet_put_Filename(IMediaDet * iface,BSTR newVal)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
MediaDet_GetBitmapBits(IMediaDet * iface,double StreamTime,LONG * pBufferSize,char * pBuffer,LONG Width,LONG Height)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
MediaDet_WriteBitmapBits(IMediaDet * iface,double StreamTime,LONG Width,LONG Height,BSTR Filename)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
MediaDet_get_StreamMediaType(IMediaDet * iface,AM_MEDIA_TYPE * pVal)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
MediaDet_GetSampleGrabber(IMediaDet * iface,ISampleGrabber ** ppVal)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
MediaDet_get_FrameRate(IMediaDet * iface,double * pVal)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
MediaDet_EnterBitmapGrabMode(IMediaDet * iface,double SeekTime)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
MediaDet_create(IUnknown * pUnkOuter,LPVOID * ppv)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