1 /* DirectShow Sample Grabber object (QEDIT.DLL)
3 * Copyright 2009 Paul Chitescu
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.
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.
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
30 #include "qedit_private.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
37 struct strmbase_filter filter
;
38 ISampleGrabber ISampleGrabber_iface
;
40 struct strmbase_source source
;
41 struct strmbase_passthrough passthrough
;
43 struct strmbase_sink sink
;
44 AM_MEDIA_TYPE filter_mt
;
45 IMemInputPin IMemInputPin_iface
;
46 IMemAllocator
*allocator
;
48 ISampleGrabberCB
*grabberIface
;
61 static struct sample_grabber
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
63 return CONTAINING_RECORD(iface
, struct sample_grabber
, filter
);
66 static struct sample_grabber
*impl_from_ISampleGrabber(ISampleGrabber
*iface
)
68 return CONTAINING_RECORD(iface
, struct sample_grabber
, ISampleGrabber_iface
);
71 static struct sample_grabber
*impl_from_IMemInputPin(IMemInputPin
*iface
)
73 return CONTAINING_RECORD(iface
, struct sample_grabber
, IMemInputPin_iface
);
77 /* Cleanup at end of life */
78 static void SampleGrabber_cleanup(struct sample_grabber
*This
)
80 TRACE("(%p)\n", This
);
82 IMemAllocator_Release(This
->allocator
);
83 if (This
->grabberIface
)
84 ISampleGrabberCB_Release(This
->grabberIface
);
85 FreeMediaType(&This
->filter_mt
);
86 CoTaskMemFree(This
->bufferData
);
89 static struct strmbase_pin
*sample_grabber_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
91 struct sample_grabber
*filter
= impl_from_strmbase_filter(iface
);
94 return &filter
->sink
.pin
;
96 return &filter
->source
.pin
;
100 static void sample_grabber_destroy(struct strmbase_filter
*iface
)
102 struct sample_grabber
*filter
= impl_from_strmbase_filter(iface
);
104 SampleGrabber_cleanup(filter
);
105 strmbase_sink_cleanup(&filter
->sink
);
106 strmbase_source_cleanup(&filter
->source
);
107 strmbase_passthrough_cleanup(&filter
->passthrough
);
108 strmbase_filter_cleanup(&filter
->filter
);
112 static HRESULT
sample_grabber_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
114 struct sample_grabber
*filter
= impl_from_strmbase_filter(iface
);
116 if (IsEqualGUID(iid
, &IID_ISampleGrabber
))
117 *out
= &filter
->ISampleGrabber_iface
;
119 return E_NOINTERFACE
;
121 IUnknown_AddRef((IUnknown
*)*out
);
125 static const struct strmbase_filter_ops filter_ops
=
127 .filter_get_pin
= sample_grabber_get_pin
,
128 .filter_destroy
= sample_grabber_destroy
,
129 .filter_query_interface
= sample_grabber_query_interface
,
132 /* Helper that buffers data and/or calls installed sample callbacks */
133 static void SampleGrabber_callback(struct sample_grabber
*This
, IMediaSample
*sample
)
136 REFERENCE_TIME tStart
, tEnd
;
137 if (This
->bufferLen
>= 0) {
139 LONG size
= IMediaSample_GetActualDataLength(sample
);
140 if (size
>= 0 && SUCCEEDED(IMediaSample_GetPointer(sample
, &data
))) {
143 EnterCriticalSection(&This
->filter
.filter_cs
);
144 if (This
->bufferLen
!= size
) {
145 CoTaskMemFree(This
->bufferData
);
146 This
->bufferData
= size
? CoTaskMemAlloc(size
) : NULL
;
147 This
->bufferLen
= size
;
150 CopyMemory(This
->bufferData
, data
, size
);
151 LeaveCriticalSection(&This
->filter
.filter_cs
);
154 if (!This
->grabberIface
)
156 if (SUCCEEDED(IMediaSample_GetTime(sample
, &tStart
, &tEnd
)))
157 time
= 1e-7 * tStart
;
158 switch (This
->grabberMethod
) {
161 ULONG ref
= IMediaSample_AddRef(sample
);
162 ISampleGrabberCB_SampleCB(This
->grabberIface
, time
, sample
);
163 ref
= IMediaSample_Release(sample
) + 1 - ref
;
166 ERR("(%p) Callback referenced sample %p by %u\n", This
, sample
, ref
);
167 /* ugly as hell but some apps are sooo buggy */
169 IMediaSample_Release(sample
);
176 LONG size
= IMediaSample_GetActualDataLength(sample
);
177 if (size
&& SUCCEEDED(IMediaSample_GetPointer(sample
, &data
)) && data
)
178 ISampleGrabberCB_BufferCB(This
->grabberIface
, time
, data
, size
);
184 FIXME("unsupported method %d\n", This
->grabberMethod
);
185 /* do not bother us again */
186 This
->grabberMethod
= -1;
191 static HRESULT WINAPI
192 SampleGrabber_ISampleGrabber_QueryInterface(ISampleGrabber
*iface
, REFIID riid
, void **ppv
)
194 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
195 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, riid
, ppv
);
200 SampleGrabber_ISampleGrabber_AddRef(ISampleGrabber
*iface
)
202 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
203 return IUnknown_AddRef(filter
->filter
.outer_unk
);
208 SampleGrabber_ISampleGrabber_Release(ISampleGrabber
*iface
)
210 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
211 return IUnknown_Release(filter
->filter
.outer_unk
);
215 static HRESULT WINAPI
216 SampleGrabber_ISampleGrabber_SetOneShot(ISampleGrabber
*iface
, BOOL oneShot
)
218 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
219 TRACE("(%p)->(%u)\n", This
, oneShot
);
220 This
->oneShot
= oneShot
? OneShot_Wait
: OneShot_None
;
225 static HRESULT WINAPI
SampleGrabber_ISampleGrabber_SetMediaType(ISampleGrabber
*iface
, const AM_MEDIA_TYPE
*mt
)
227 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
229 TRACE("filter %p, mt %p.\n", filter
, mt
);
230 strmbase_dump_media_type(mt
);
235 FreeMediaType(&filter
->filter_mt
);
236 CopyMediaType(&filter
->filter_mt
, mt
);
241 static HRESULT WINAPI
242 SampleGrabber_ISampleGrabber_GetConnectedMediaType(ISampleGrabber
*iface
, AM_MEDIA_TYPE
*mt
)
244 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
246 TRACE("filter %p, mt %p.\n", filter
, mt
);
251 if (!filter
->sink
.pin
.peer
)
252 return VFW_E_NOT_CONNECTED
;
254 CopyMediaType(mt
, &filter
->sink
.pin
.mt
);
259 static HRESULT WINAPI
260 SampleGrabber_ISampleGrabber_SetBufferSamples(ISampleGrabber
*iface
, BOOL bufferEm
)
262 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
263 TRACE("(%p)->(%u)\n", This
, bufferEm
);
264 EnterCriticalSection(&This
->filter
.filter_cs
);
266 if (This
->bufferLen
< 0)
270 This
->bufferLen
= -1;
271 LeaveCriticalSection(&This
->filter
.filter_cs
);
276 static HRESULT WINAPI
277 SampleGrabber_ISampleGrabber_GetCurrentBuffer(ISampleGrabber
*iface
, LONG
*bufSize
, LONG
*buffer
)
279 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
281 TRACE("(%p)->(%p, %p)\n", This
, bufSize
, buffer
);
284 EnterCriticalSection(&This
->filter
.filter_cs
);
285 if (!This
->sink
.pin
.peer
)
286 ret
= VFW_E_NOT_CONNECTED
;
287 else if (This
->bufferLen
< 0)
289 else if (This
->bufferLen
== 0)
290 ret
= VFW_E_WRONG_STATE
;
293 if (*bufSize
>= This
->bufferLen
)
294 CopyMemory(buffer
, This
->bufferData
, This
->bufferLen
);
298 *bufSize
= This
->bufferLen
;
300 LeaveCriticalSection(&This
->filter
.filter_cs
);
305 static HRESULT WINAPI
306 SampleGrabber_ISampleGrabber_GetCurrentSample(ISampleGrabber
*iface
, IMediaSample
**sample
)
308 /* MS doesn't implement it either, no one should call it */
309 WARN("(%p): not implemented\n", sample
);
314 static HRESULT WINAPI
315 SampleGrabber_ISampleGrabber_SetCallback(ISampleGrabber
*iface
, ISampleGrabberCB
*cb
, LONG whichMethod
)
317 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
318 TRACE("(%p)->(%p, %u)\n", This
, cb
, whichMethod
);
319 if (This
->grabberIface
)
320 ISampleGrabberCB_Release(This
->grabberIface
);
321 This
->grabberIface
= cb
;
322 This
->grabberMethod
= whichMethod
;
324 ISampleGrabberCB_AddRef(cb
);
328 static HRESULT WINAPI
SampleGrabber_IMemInputPin_QueryInterface(IMemInputPin
*iface
, REFIID iid
, void **out
)
330 struct sample_grabber
*filter
= impl_from_IMemInputPin(iface
);
331 return IPin_QueryInterface(&filter
->sink
.pin
.IPin_iface
, iid
, out
);
334 static ULONG WINAPI
SampleGrabber_IMemInputPin_AddRef(IMemInputPin
*iface
)
336 struct sample_grabber
*filter
= impl_from_IMemInputPin(iface
);
337 return IPin_AddRef(&filter
->sink
.pin
.IPin_iface
);
340 static ULONG WINAPI
SampleGrabber_IMemInputPin_Release(IMemInputPin
*iface
)
342 struct sample_grabber
*filter
= impl_from_IMemInputPin(iface
);
343 return IPin_Release(&filter
->sink
.pin
.IPin_iface
);
347 static HRESULT WINAPI
348 SampleGrabber_IMemInputPin_GetAllocator(IMemInputPin
*iface
, IMemAllocator
**allocator
)
350 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
351 TRACE("(%p)->(%p) allocator = %p\n", This
, allocator
, This
->allocator
);
354 *allocator
= This
->allocator
;
356 return VFW_E_NO_ALLOCATOR
;
357 IMemAllocator_AddRef(*allocator
);
362 static HRESULT WINAPI
363 SampleGrabber_IMemInputPin_NotifyAllocator(IMemInputPin
*iface
, IMemAllocator
*allocator
, BOOL readOnly
)
365 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
366 TRACE("(%p)->(%p, %u) allocator = %p\n", This
, allocator
, readOnly
, This
->allocator
);
367 if (This
->allocator
== allocator
)
370 IMemAllocator_Release(This
->allocator
);
371 This
->allocator
= allocator
;
373 IMemAllocator_AddRef(allocator
);
378 static HRESULT WINAPI
379 SampleGrabber_IMemInputPin_GetAllocatorRequirements(IMemInputPin
*iface
, ALLOCATOR_PROPERTIES
*props
)
381 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
382 FIXME("(%p)->(%p): semi-stub\n", This
, props
);
385 return This
->source
.pMemInputPin
? IMemInputPin_GetAllocatorRequirements(This
->source
.pMemInputPin
, props
) : E_NOTIMPL
;
389 static HRESULT WINAPI
390 SampleGrabber_IMemInputPin_Receive(IMemInputPin
*iface
, IMediaSample
*sample
)
392 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
394 TRACE("(%p)->(%p) output = %p, grabber = %p\n", This
, sample
, This
->source
.pMemInputPin
, This
->grabberIface
);
397 if (This
->oneShot
== OneShot_Past
)
399 SampleGrabber_callback(This
, sample
);
400 hr
= This
->source
.pMemInputPin
? IMemInputPin_Receive(This
->source
.pMemInputPin
, sample
) : S_OK
;
401 if (This
->oneShot
== OneShot_Wait
) {
402 This
->oneShot
= OneShot_Past
;
404 if (This
->source
.pin
.peer
)
405 IPin_EndOfStream(This
->source
.pin
.peer
);
411 static HRESULT WINAPI
412 SampleGrabber_IMemInputPin_ReceiveMultiple(IMemInputPin
*iface
, IMediaSample
**samples
, LONG nSamples
, LONG
*nProcessed
)
414 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
416 TRACE("(%p)->(%p, %u, %p) output = %p, grabber = %p\n", This
, samples
, nSamples
, nProcessed
, This
->source
.pMemInputPin
, This
->grabberIface
);
417 if (!samples
|| !nProcessed
)
419 if ((This
->filter
.state
!= State_Running
) || (This
->oneShot
== OneShot_Past
))
421 for (idx
= 0; idx
< nSamples
; idx
++)
422 SampleGrabber_callback(This
, samples
[idx
]);
423 return This
->source
.pMemInputPin
? IMemInputPin_ReceiveMultiple(This
->source
.pMemInputPin
, samples
, nSamples
, nProcessed
) : S_OK
;
427 static HRESULT WINAPI
428 SampleGrabber_IMemInputPin_ReceiveCanBlock(IMemInputPin
*iface
)
430 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
431 TRACE("(%p)\n", This
);
432 return This
->source
.pMemInputPin
? IMemInputPin_ReceiveCanBlock(This
->source
.pMemInputPin
) : S_OK
;
435 static const ISampleGrabberVtbl ISampleGrabber_VTable
=
437 SampleGrabber_ISampleGrabber_QueryInterface
,
438 SampleGrabber_ISampleGrabber_AddRef
,
439 SampleGrabber_ISampleGrabber_Release
,
440 SampleGrabber_ISampleGrabber_SetOneShot
,
441 SampleGrabber_ISampleGrabber_SetMediaType
,
442 SampleGrabber_ISampleGrabber_GetConnectedMediaType
,
443 SampleGrabber_ISampleGrabber_SetBufferSamples
,
444 SampleGrabber_ISampleGrabber_GetCurrentBuffer
,
445 SampleGrabber_ISampleGrabber_GetCurrentSample
,
446 SampleGrabber_ISampleGrabber_SetCallback
,
449 static const IMemInputPinVtbl IMemInputPin_VTable
=
451 SampleGrabber_IMemInputPin_QueryInterface
,
452 SampleGrabber_IMemInputPin_AddRef
,
453 SampleGrabber_IMemInputPin_Release
,
454 SampleGrabber_IMemInputPin_GetAllocator
,
455 SampleGrabber_IMemInputPin_NotifyAllocator
,
456 SampleGrabber_IMemInputPin_GetAllocatorRequirements
,
457 SampleGrabber_IMemInputPin_Receive
,
458 SampleGrabber_IMemInputPin_ReceiveMultiple
,
459 SampleGrabber_IMemInputPin_ReceiveCanBlock
,
462 static struct sample_grabber
*impl_from_sink_pin(struct strmbase_pin
*iface
)
464 return CONTAINING_RECORD(iface
, struct sample_grabber
, sink
.pin
);
467 static HRESULT
sample_grabber_sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
469 struct sample_grabber
*filter
= impl_from_sink_pin(iface
);
471 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
472 *out
= &filter
->IMemInputPin_iface
;
474 return E_NOINTERFACE
;
476 IUnknown_AddRef((IUnknown
*)*out
);
480 static BOOL
check_filter_mt(struct sample_grabber
*filter
, const AM_MEDIA_TYPE
*mt
)
482 if (IsEqualGUID(&filter
->filter_mt
.majortype
, &GUID_NULL
))
484 if (!IsEqualGUID(&filter
->filter_mt
.majortype
, &mt
->majortype
))
487 if (IsEqualGUID(&filter
->filter_mt
.subtype
, &GUID_NULL
))
489 if (!IsEqualGUID(&filter
->filter_mt
.subtype
, &mt
->subtype
))
492 if (IsEqualGUID(&filter
->filter_mt
.formattype
, &GUID_NULL
))
494 if (!IsEqualGUID(&filter
->filter_mt
.formattype
, &mt
->formattype
))
500 static HRESULT
sample_grabber_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
502 struct sample_grabber
*filter
= impl_from_sink_pin(iface
);
504 return check_filter_mt(filter
, mt
) ? S_OK
: S_FALSE
;
507 static HRESULT
sample_grabber_sink_get_media_type(struct strmbase_pin
*iface
,
508 unsigned int index
, AM_MEDIA_TYPE
*mt
)
510 struct sample_grabber
*filter
= impl_from_sink_pin(iface
);
511 IEnumMediaTypes
*enummt
;
515 if (!filter
->source
.pin
.peer
)
516 return VFW_E_NOT_CONNECTED
;
518 if (FAILED(hr
= IPin_EnumMediaTypes(filter
->source
.pin
.peer
, &enummt
)))
521 if ((!index
|| IEnumMediaTypes_Skip(enummt
, index
) == S_OK
)
522 && IEnumMediaTypes_Next(enummt
, 1, &pmt
, NULL
) == S_OK
)
524 CopyMediaType(mt
, pmt
);
525 DeleteMediaType(pmt
);
526 IEnumMediaTypes_Release(enummt
);
530 IEnumMediaTypes_Release(enummt
);
531 return VFW_S_NO_MORE_ITEMS
;
534 static const struct strmbase_sink_ops sink_ops
=
536 .base
.pin_query_interface
= sample_grabber_sink_query_interface
,
537 .base
.pin_query_accept
= sample_grabber_sink_query_accept
,
538 .base
.pin_get_media_type
= sample_grabber_sink_get_media_type
,
541 static struct sample_grabber
*impl_from_source_pin(struct strmbase_pin
*iface
)
543 return CONTAINING_RECORD(iface
, struct sample_grabber
, source
.pin
);
546 static HRESULT
sample_grabber_source_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
548 struct sample_grabber
*filter
= impl_from_source_pin(iface
);
550 if (IsEqualGUID(iid
, &IID_IMediaPosition
))
551 *out
= &filter
->passthrough
.IMediaPosition_iface
;
552 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
553 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
555 return E_NOINTERFACE
;
557 IUnknown_AddRef((IUnknown
*)*out
);
561 static HRESULT
sample_grabber_source_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
563 struct sample_grabber
*filter
= impl_from_source_pin(iface
);
565 if (filter
->sink
.pin
.peer
&& IPin_QueryAccept(filter
->sink
.pin
.peer
, mt
) != S_OK
)
568 return check_filter_mt(filter
, mt
) ? S_OK
: S_FALSE
;
571 static HRESULT
sample_grabber_source_get_media_type(struct strmbase_pin
*iface
,
572 unsigned int index
, AM_MEDIA_TYPE
*mt
)
574 struct sample_grabber
*filter
= impl_from_source_pin(iface
);
575 IEnumMediaTypes
*enummt
;
579 if (!filter
->sink
.pin
.peer
)
580 return VFW_E_NOT_CONNECTED
;
582 if (FAILED(hr
= IPin_EnumMediaTypes(filter
->sink
.pin
.peer
, &enummt
)))
585 if ((!index
|| IEnumMediaTypes_Skip(enummt
, index
) == S_OK
)
586 && IEnumMediaTypes_Next(enummt
, 1, &pmt
, NULL
) == S_OK
)
588 CopyMediaType(mt
, pmt
);
589 DeleteMediaType(pmt
);
590 IEnumMediaTypes_Release(enummt
);
594 IEnumMediaTypes_Release(enummt
);
595 return VFW_S_NO_MORE_ITEMS
;
598 static inline BOOL
compare_media_types(const AM_MEDIA_TYPE
*a
, const AM_MEDIA_TYPE
*b
)
600 return !memcmp(a
, b
, offsetof(AM_MEDIA_TYPE
, pbFormat
))
601 && !memcmp(a
->pbFormat
, b
->pbFormat
, a
->cbFormat
);
604 static HRESULT WINAPI
sample_grabber_source_DecideAllocator(struct strmbase_source
*iface
,
605 IMemInputPin
*peer
, IMemAllocator
**allocator
)
607 struct sample_grabber
*filter
= impl_from_source_pin(&iface
->pin
);
608 const AM_MEDIA_TYPE
*mt
= &iface
->pin
.mt
;
610 if (!compare_media_types(mt
, &filter
->sink
.pin
.mt
))
612 IFilterGraph2
*graph
;
615 if (FAILED(hr
= IFilterGraph_QueryInterface(filter
->filter
.graph
,
616 &IID_IFilterGraph2
, (void **)&graph
)))
618 ERR("Failed to get IFilterGraph2 interface, hr %#x.\n", hr
);
622 hr
= IFilterGraph2_ReconnectEx(graph
, &filter
->sink
.pin
.IPin_iface
, mt
);
623 IFilterGraph2_Release(graph
);
630 static const struct strmbase_source_ops source_ops
=
632 .base
.pin_query_interface
= sample_grabber_source_query_interface
,
633 .base
.pin_query_accept
= sample_grabber_source_query_accept
,
634 .base
.pin_get_media_type
= sample_grabber_source_get_media_type
,
635 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
636 .pfnDecideAllocator
= sample_grabber_source_DecideAllocator
,
639 HRESULT
sample_grabber_create(IUnknown
*outer
, IUnknown
**out
)
641 struct sample_grabber
*object
;
643 if (!(object
= calloc(1, sizeof(*object
))))
644 return E_OUTOFMEMORY
;
646 strmbase_filter_init(&object
->filter
, outer
, &CLSID_SampleGrabber
, &filter_ops
);
647 object
->ISampleGrabber_iface
.lpVtbl
= &ISampleGrabber_VTable
;
648 object
->IMemInputPin_iface
.lpVtbl
= &IMemInputPin_VTable
;
650 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"In", &sink_ops
, NULL
);
652 strmbase_source_init(&object
->source
, &object
->filter
, L
"Out", &source_ops
);
653 strmbase_passthrough_init(&object
->passthrough
, (IUnknown
*)&object
->source
.pin
.IPin_iface
);
654 ISeekingPassThru_Init(&object
->passthrough
.ISeekingPassThru_iface
, FALSE
,
655 &object
->sink
.pin
.IPin_iface
);
657 object
->grabberMethod
= -1;
658 object
->oneShot
= OneShot_None
;
659 object
->bufferLen
= -1;
661 TRACE("Created sample grabber %p.\n", object
);
662 *out
= &object
->filter
.IUnknown_inner
;