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 %lu\n", This
, sample
, ref
);
173 LONG size
= IMediaSample_GetActualDataLength(sample
);
174 if (size
&& SUCCEEDED(IMediaSample_GetPointer(sample
, &data
)) && data
)
175 ISampleGrabberCB_BufferCB(This
->grabberIface
, time
, data
, size
);
181 FIXME("Unknown method %ld.\n", This
->grabberMethod
);
182 /* do not bother us again */
183 This
->grabberMethod
= -1;
188 static HRESULT WINAPI
189 SampleGrabber_ISampleGrabber_QueryInterface(ISampleGrabber
*iface
, REFIID riid
, void **ppv
)
191 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
192 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, riid
, ppv
);
197 SampleGrabber_ISampleGrabber_AddRef(ISampleGrabber
*iface
)
199 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
200 return IUnknown_AddRef(filter
->filter
.outer_unk
);
205 SampleGrabber_ISampleGrabber_Release(ISampleGrabber
*iface
)
207 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
208 return IUnknown_Release(filter
->filter
.outer_unk
);
212 static HRESULT WINAPI
213 SampleGrabber_ISampleGrabber_SetOneShot(ISampleGrabber
*iface
, BOOL oneShot
)
215 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
216 TRACE("(%p)->(%u)\n", This
, oneShot
);
217 This
->oneShot
= oneShot
? OneShot_Wait
: OneShot_None
;
222 static HRESULT WINAPI
SampleGrabber_ISampleGrabber_SetMediaType(ISampleGrabber
*iface
, const AM_MEDIA_TYPE
*mt
)
224 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
226 TRACE("filter %p, mt %p.\n", filter
, mt
);
227 strmbase_dump_media_type(mt
);
232 FreeMediaType(&filter
->filter_mt
);
233 CopyMediaType(&filter
->filter_mt
, mt
);
238 static HRESULT WINAPI
239 SampleGrabber_ISampleGrabber_GetConnectedMediaType(ISampleGrabber
*iface
, AM_MEDIA_TYPE
*mt
)
241 struct sample_grabber
*filter
= impl_from_ISampleGrabber(iface
);
243 TRACE("filter %p, mt %p.\n", filter
, mt
);
248 if (!filter
->sink
.pin
.peer
)
249 return VFW_E_NOT_CONNECTED
;
251 CopyMediaType(mt
, &filter
->sink
.pin
.mt
);
256 static HRESULT WINAPI
257 SampleGrabber_ISampleGrabber_SetBufferSamples(ISampleGrabber
*iface
, BOOL bufferEm
)
259 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
260 TRACE("(%p)->(%u)\n", This
, bufferEm
);
261 EnterCriticalSection(&This
->filter
.filter_cs
);
263 if (This
->bufferLen
< 0)
267 This
->bufferLen
= -1;
268 LeaveCriticalSection(&This
->filter
.filter_cs
);
273 static HRESULT WINAPI
274 SampleGrabber_ISampleGrabber_GetCurrentBuffer(ISampleGrabber
*iface
, LONG
*bufSize
, LONG
*buffer
)
276 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
278 TRACE("(%p)->(%p, %p)\n", This
, bufSize
, buffer
);
281 EnterCriticalSection(&This
->filter
.filter_cs
);
282 if (!This
->sink
.pin
.peer
)
283 ret
= VFW_E_NOT_CONNECTED
;
284 else if (This
->bufferLen
< 0)
286 else if (This
->bufferLen
== 0)
287 ret
= VFW_E_WRONG_STATE
;
290 if (*bufSize
>= This
->bufferLen
)
291 CopyMemory(buffer
, This
->bufferData
, This
->bufferLen
);
295 *bufSize
= This
->bufferLen
;
297 LeaveCriticalSection(&This
->filter
.filter_cs
);
302 static HRESULT WINAPI
303 SampleGrabber_ISampleGrabber_GetCurrentSample(ISampleGrabber
*iface
, IMediaSample
**sample
)
305 /* MS doesn't implement it either, no one should call it */
306 WARN("(%p): not implemented\n", sample
);
311 static HRESULT WINAPI
312 SampleGrabber_ISampleGrabber_SetCallback(ISampleGrabber
*iface
, ISampleGrabberCB
*cb
, LONG whichMethod
)
314 struct sample_grabber
*This
= impl_from_ISampleGrabber(iface
);
316 TRACE("filter %p, callback %p, method %ld.\n", This
, cb
, whichMethod
);
318 if (This
->grabberIface
)
319 ISampleGrabberCB_Release(This
->grabberIface
);
320 This
->grabberIface
= cb
;
321 This
->grabberMethod
= whichMethod
;
323 ISampleGrabberCB_AddRef(cb
);
327 static HRESULT WINAPI
SampleGrabber_IMemInputPin_QueryInterface(IMemInputPin
*iface
, REFIID iid
, void **out
)
329 struct sample_grabber
*filter
= impl_from_IMemInputPin(iface
);
330 return IPin_QueryInterface(&filter
->sink
.pin
.IPin_iface
, iid
, out
);
333 static ULONG WINAPI
SampleGrabber_IMemInputPin_AddRef(IMemInputPin
*iface
)
335 struct sample_grabber
*filter
= impl_from_IMemInputPin(iface
);
336 return IPin_AddRef(&filter
->sink
.pin
.IPin_iface
);
339 static ULONG WINAPI
SampleGrabber_IMemInputPin_Release(IMemInputPin
*iface
)
341 struct sample_grabber
*filter
= impl_from_IMemInputPin(iface
);
342 return IPin_Release(&filter
->sink
.pin
.IPin_iface
);
346 static HRESULT WINAPI
347 SampleGrabber_IMemInputPin_GetAllocator(IMemInputPin
*iface
, IMemAllocator
**allocator
)
349 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
350 TRACE("(%p)->(%p) allocator = %p\n", This
, allocator
, This
->allocator
);
353 *allocator
= This
->allocator
;
355 return VFW_E_NO_ALLOCATOR
;
356 IMemAllocator_AddRef(*allocator
);
361 static HRESULT WINAPI
362 SampleGrabber_IMemInputPin_NotifyAllocator(IMemInputPin
*iface
, IMemAllocator
*allocator
, BOOL readOnly
)
364 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
365 TRACE("(%p)->(%p, %u) allocator = %p\n", This
, allocator
, readOnly
, This
->allocator
);
366 if (This
->allocator
== allocator
)
369 IMemAllocator_Release(This
->allocator
);
370 This
->allocator
= allocator
;
372 IMemAllocator_AddRef(allocator
);
377 static HRESULT WINAPI
378 SampleGrabber_IMemInputPin_GetAllocatorRequirements(IMemInputPin
*iface
, ALLOCATOR_PROPERTIES
*props
)
380 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
381 FIXME("(%p)->(%p): semi-stub\n", This
, props
);
384 return This
->source
.pMemInputPin
? IMemInputPin_GetAllocatorRequirements(This
->source
.pMemInputPin
, props
) : E_NOTIMPL
;
388 static HRESULT WINAPI
389 SampleGrabber_IMemInputPin_Receive(IMemInputPin
*iface
, IMediaSample
*sample
)
391 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
393 TRACE("(%p)->(%p) output = %p, grabber = %p\n", This
, sample
, This
->source
.pMemInputPin
, This
->grabberIface
);
396 if (This
->oneShot
== OneShot_Past
)
398 SampleGrabber_callback(This
, sample
);
399 hr
= This
->source
.pMemInputPin
? IMemInputPin_Receive(This
->source
.pMemInputPin
, sample
) : S_OK
;
400 if (This
->oneShot
== OneShot_Wait
) {
401 This
->oneShot
= OneShot_Past
;
403 if (This
->source
.pin
.peer
)
404 IPin_EndOfStream(This
->source
.pin
.peer
);
410 static HRESULT WINAPI
411 SampleGrabber_IMemInputPin_ReceiveMultiple(IMemInputPin
*iface
, IMediaSample
**samples
, LONG nSamples
, LONG
*nProcessed
)
413 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
416 TRACE("filter %p, samples %p, count %lu, ret_count %p.\n", This
, samples
, nSamples
, nProcessed
);
418 if (!samples
|| !nProcessed
)
420 if ((This
->filter
.state
!= State_Running
) || (This
->oneShot
== OneShot_Past
))
422 for (idx
= 0; idx
< nSamples
; idx
++)
423 SampleGrabber_callback(This
, samples
[idx
]);
424 return This
->source
.pMemInputPin
? IMemInputPin_ReceiveMultiple(This
->source
.pMemInputPin
, samples
, nSamples
, nProcessed
) : S_OK
;
428 static HRESULT WINAPI
429 SampleGrabber_IMemInputPin_ReceiveCanBlock(IMemInputPin
*iface
)
431 struct sample_grabber
*This
= impl_from_IMemInputPin(iface
);
432 TRACE("(%p)\n", This
);
433 return This
->source
.pMemInputPin
? IMemInputPin_ReceiveCanBlock(This
->source
.pMemInputPin
) : S_OK
;
436 static const ISampleGrabberVtbl ISampleGrabber_VTable
=
438 SampleGrabber_ISampleGrabber_QueryInterface
,
439 SampleGrabber_ISampleGrabber_AddRef
,
440 SampleGrabber_ISampleGrabber_Release
,
441 SampleGrabber_ISampleGrabber_SetOneShot
,
442 SampleGrabber_ISampleGrabber_SetMediaType
,
443 SampleGrabber_ISampleGrabber_GetConnectedMediaType
,
444 SampleGrabber_ISampleGrabber_SetBufferSamples
,
445 SampleGrabber_ISampleGrabber_GetCurrentBuffer
,
446 SampleGrabber_ISampleGrabber_GetCurrentSample
,
447 SampleGrabber_ISampleGrabber_SetCallback
,
450 static const IMemInputPinVtbl IMemInputPin_VTable
=
452 SampleGrabber_IMemInputPin_QueryInterface
,
453 SampleGrabber_IMemInputPin_AddRef
,
454 SampleGrabber_IMemInputPin_Release
,
455 SampleGrabber_IMemInputPin_GetAllocator
,
456 SampleGrabber_IMemInputPin_NotifyAllocator
,
457 SampleGrabber_IMemInputPin_GetAllocatorRequirements
,
458 SampleGrabber_IMemInputPin_Receive
,
459 SampleGrabber_IMemInputPin_ReceiveMultiple
,
460 SampleGrabber_IMemInputPin_ReceiveCanBlock
,
463 static struct sample_grabber
*impl_from_sink_pin(struct strmbase_pin
*iface
)
465 return CONTAINING_RECORD(iface
, struct sample_grabber
, sink
.pin
);
468 static HRESULT
sample_grabber_sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
470 struct sample_grabber
*filter
= impl_from_sink_pin(iface
);
472 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
473 *out
= &filter
->IMemInputPin_iface
;
475 return E_NOINTERFACE
;
477 IUnknown_AddRef((IUnknown
*)*out
);
481 static BOOL
check_filter_mt(struct sample_grabber
*filter
, const AM_MEDIA_TYPE
*mt
)
483 if (IsEqualGUID(&filter
->filter_mt
.majortype
, &GUID_NULL
))
485 if (!IsEqualGUID(&filter
->filter_mt
.majortype
, &mt
->majortype
))
488 if (IsEqualGUID(&filter
->filter_mt
.subtype
, &GUID_NULL
))
490 if (!IsEqualGUID(&filter
->filter_mt
.subtype
, &mt
->subtype
))
493 if (IsEqualGUID(&filter
->filter_mt
.formattype
, &GUID_NULL
))
495 if (!IsEqualGUID(&filter
->filter_mt
.formattype
, &mt
->formattype
))
501 static HRESULT
sample_grabber_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
503 struct sample_grabber
*filter
= impl_from_sink_pin(iface
);
505 return check_filter_mt(filter
, mt
) ? S_OK
: S_FALSE
;
508 static HRESULT
sample_grabber_sink_get_media_type(struct strmbase_pin
*iface
,
509 unsigned int index
, AM_MEDIA_TYPE
*mt
)
511 struct sample_grabber
*filter
= impl_from_sink_pin(iface
);
512 IEnumMediaTypes
*enummt
;
516 if (!filter
->source
.pin
.peer
)
517 return VFW_E_NOT_CONNECTED
;
519 if (FAILED(hr
= IPin_EnumMediaTypes(filter
->source
.pin
.peer
, &enummt
)))
522 if ((!index
|| IEnumMediaTypes_Skip(enummt
, index
) == S_OK
)
523 && IEnumMediaTypes_Next(enummt
, 1, &pmt
, NULL
) == S_OK
)
525 CopyMediaType(mt
, pmt
);
526 DeleteMediaType(pmt
);
527 IEnumMediaTypes_Release(enummt
);
531 IEnumMediaTypes_Release(enummt
);
532 return VFW_S_NO_MORE_ITEMS
;
535 static const struct strmbase_sink_ops sink_ops
=
537 .base
.pin_query_interface
= sample_grabber_sink_query_interface
,
538 .base
.pin_query_accept
= sample_grabber_sink_query_accept
,
539 .base
.pin_get_media_type
= sample_grabber_sink_get_media_type
,
542 static struct sample_grabber
*impl_from_source_pin(struct strmbase_pin
*iface
)
544 return CONTAINING_RECORD(iface
, struct sample_grabber
, source
.pin
);
547 static HRESULT
sample_grabber_source_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
549 struct sample_grabber
*filter
= impl_from_source_pin(iface
);
551 if (IsEqualGUID(iid
, &IID_IMediaPosition
))
552 *out
= &filter
->passthrough
.IMediaPosition_iface
;
553 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
554 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
556 return E_NOINTERFACE
;
558 IUnknown_AddRef((IUnknown
*)*out
);
562 static HRESULT
sample_grabber_source_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
*mt
)
564 struct sample_grabber
*filter
= impl_from_source_pin(iface
);
566 if (filter
->sink
.pin
.peer
&& IPin_QueryAccept(filter
->sink
.pin
.peer
, mt
) != S_OK
)
569 return check_filter_mt(filter
, mt
) ? S_OK
: S_FALSE
;
572 static HRESULT
sample_grabber_source_get_media_type(struct strmbase_pin
*iface
,
573 unsigned int index
, AM_MEDIA_TYPE
*mt
)
575 struct sample_grabber
*filter
= impl_from_source_pin(iface
);
576 IEnumMediaTypes
*enummt
;
580 if (!filter
->sink
.pin
.peer
)
581 return VFW_E_NOT_CONNECTED
;
583 if (FAILED(hr
= IPin_EnumMediaTypes(filter
->sink
.pin
.peer
, &enummt
)))
586 if ((!index
|| IEnumMediaTypes_Skip(enummt
, index
) == S_OK
)
587 && IEnumMediaTypes_Next(enummt
, 1, &pmt
, NULL
) == S_OK
)
589 CopyMediaType(mt
, pmt
);
590 DeleteMediaType(pmt
);
591 IEnumMediaTypes_Release(enummt
);
595 IEnumMediaTypes_Release(enummt
);
596 return VFW_S_NO_MORE_ITEMS
;
599 static inline BOOL
compare_media_types(const AM_MEDIA_TYPE
*a
, const AM_MEDIA_TYPE
*b
)
601 return !memcmp(a
, b
, offsetof(AM_MEDIA_TYPE
, pbFormat
))
602 && !memcmp(a
->pbFormat
, b
->pbFormat
, a
->cbFormat
);
605 static HRESULT WINAPI
sample_grabber_source_DecideAllocator(struct strmbase_source
*iface
,
606 IMemInputPin
*peer
, IMemAllocator
**allocator
)
608 struct sample_grabber
*filter
= impl_from_source_pin(&iface
->pin
);
609 const AM_MEDIA_TYPE
*mt
= &iface
->pin
.mt
;
611 if (!compare_media_types(mt
, &filter
->sink
.pin
.mt
))
613 IFilterGraph2
*graph
;
616 if (FAILED(hr
= IFilterGraph_QueryInterface(filter
->filter
.graph
,
617 &IID_IFilterGraph2
, (void **)&graph
)))
619 ERR("Failed to get IFilterGraph2 interface, hr %#lx.\n", hr
);
623 hr
= IFilterGraph2_ReconnectEx(graph
, &filter
->sink
.pin
.IPin_iface
, mt
);
624 IFilterGraph2_Release(graph
);
631 static const struct strmbase_source_ops source_ops
=
633 .base
.pin_query_interface
= sample_grabber_source_query_interface
,
634 .base
.pin_query_accept
= sample_grabber_source_query_accept
,
635 .base
.pin_get_media_type
= sample_grabber_source_get_media_type
,
636 .pfnAttemptConnection
= BaseOutputPinImpl_AttemptConnection
,
637 .pfnDecideAllocator
= sample_grabber_source_DecideAllocator
,
640 HRESULT
sample_grabber_create(IUnknown
*outer
, IUnknown
**out
)
642 struct sample_grabber
*object
;
644 if (!(object
= calloc(1, sizeof(*object
))))
645 return E_OUTOFMEMORY
;
647 strmbase_filter_init(&object
->filter
, outer
, &CLSID_SampleGrabber
, &filter_ops
);
648 object
->ISampleGrabber_iface
.lpVtbl
= &ISampleGrabber_VTable
;
649 object
->IMemInputPin_iface
.lpVtbl
= &IMemInputPin_VTable
;
651 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"In", &sink_ops
, NULL
);
652 wcscpy(object
->sink
.pin
.name
, L
"Input");
654 strmbase_source_init(&object
->source
, &object
->filter
, L
"Out", &source_ops
);
655 wcscpy(object
->source
.pin
.name
, L
"Output");
657 strmbase_passthrough_init(&object
->passthrough
, (IUnknown
*)&object
->source
.pin
.IPin_iface
);
658 ISeekingPassThru_Init(&object
->passthrough
.ISeekingPassThru_iface
, FALSE
,
659 &object
->sink
.pin
.IPin_iface
);
661 object
->grabberMethod
= -1;
662 object
->oneShot
= OneShot_None
;
663 object
->bufferLen
= -1;
665 TRACE("Created sample grabber %p.\n", object
);
666 *out
= &object
->filter
.IUnknown_inner
;