blob: 282c35d051ebac6342825214af3f0b2ee90bee92 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001//------------------------------------------------------------------------------
2// File: AMFilter.cpp
3//
4// Desc: DirectShow base classes - implements class hierarchy for streams
5// architecture.
6//
7// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
8//------------------------------------------------------------------------------
9
10
11//=====================================================================
12//=====================================================================
13// The following classes are declared in this header:
14//
15//
16// CBaseMediaFilter Basic IMediaFilter support (abstract class)
17// CBaseFilter Support for IBaseFilter (incl. IMediaFilter)
18// CEnumPins Enumerate input and output pins
19// CEnumMediaTypes Enumerate the preferred pin formats
20// CBasePin Abstract base class for IPin interface
21// CBaseOutputPin Adds data provider member functions
22// CBaseInputPin Implements IMemInputPin interface
23// CMediaSample Basic transport unit for IMemInputPin
24// CBaseAllocator General list guff for most allocators
25// CMemAllocator Implements memory buffer allocation
26//
27//=====================================================================
28//=====================================================================
29
30#include <pjmedia-videodev/config.h>
31
32#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
33
34#include <streams.h>
35#include <strsafe.h>
36
37#ifdef DXMPERF
38#include "dxmperf.h"
39#endif // DXMPERF
40
41
42//=====================================================================
43// Helpers
44//=====================================================================
45STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator)
46{
47 return CoCreateInstance(CLSID_MemoryAllocator,
48 0,
49 CLSCTX_INPROC_SERVER,
50 IID_IMemAllocator,
51 (void **)ppAllocator);
52}
53
54// Put this one here rather than in ctlutil.cpp to avoid linking
55// anything brought in by ctlutil.cpp
56STDAPI CreatePosPassThru(
57 __in_opt LPUNKNOWN pAgg,
58 BOOL bRenderer,
59 IPin *pPin,
60 __deref_out IUnknown **ppPassThru
61)
62{
63 *ppPassThru = NULL;
64 IUnknown *pUnkSeek;
65 HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,
66 pAgg,
67 CLSCTX_INPROC_SERVER,
68 IID_IUnknown,
69 (void **)&pUnkSeek
70 );
71 if (FAILED(hr)) {
72 return hr;
73 }
74
75 ISeekingPassThru *pPassThru;
76 hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);
77 if (FAILED(hr)) {
78 pUnkSeek->Release();
79 return hr;
80 }
81 hr = pPassThru->Init(bRenderer, pPin);
82 pPassThru->Release();
83 if (FAILED(hr)) {
84 pUnkSeek->Release();
85 return hr;
86 }
87 *ppPassThru = pUnkSeek;
88 return S_OK;
89}
90
91
92
93#define CONNECT_TRACE_LEVEL 3
94
95//=====================================================================
96//=====================================================================
97// Implements CBaseMediaFilter
98//=====================================================================
99//=====================================================================
100
101
102/* Constructor */
103
104CBaseMediaFilter::CBaseMediaFilter(__in_opt LPCTSTR pName,
105 __inout_opt LPUNKNOWN pUnk,
106 __in CCritSec *pLock,
107 REFCLSID clsid) :
108 CUnknown(pName, pUnk),
109 m_pLock(pLock),
110 m_clsid(clsid),
111 m_State(State_Stopped),
112 m_pClock(NULL)
113{
114}
115
116
117/* Destructor */
118
119CBaseMediaFilter::~CBaseMediaFilter()
120{
121 // must be stopped, but can't call Stop here since
122 // our critsec has been destroyed.
123
124 /* Release any clock we were using */
125
126 if (m_pClock) {
127 m_pClock->Release();
128 m_pClock = NULL;
129 }
130}
131
132
133/* Override this to say what interfaces we support and where */
134
135STDMETHODIMP
136CBaseMediaFilter::NonDelegatingQueryInterface(
137 REFIID riid,
138 __deref_out void ** ppv)
139{
140 if (riid == IID_IMediaFilter) {
141 return GetInterface((IMediaFilter *) this, ppv);
142 } else if (riid == IID_IPersist) {
143 return GetInterface((IPersist *) this, ppv);
144 } else {
145 return CUnknown::NonDelegatingQueryInterface(riid, ppv);
146 }
147}
148
149/* Return the filter's clsid */
150STDMETHODIMP
151CBaseMediaFilter::GetClassID(__out CLSID *pClsID)
152{
153 CheckPointer(pClsID,E_POINTER);
154 ValidateReadWritePtr(pClsID,sizeof(CLSID));
155 *pClsID = m_clsid;
156 return NOERROR;
157}
158
159/* Override this if your state changes are not done synchronously */
160
161STDMETHODIMP
162CBaseMediaFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)
163{
164 UNREFERENCED_PARAMETER(dwMSecs);
165 CheckPointer(State,E_POINTER);
166 ValidateReadWritePtr(State,sizeof(FILTER_STATE));
167
168 *State = m_State;
169 return S_OK;
170}
171
172
173/* Set the clock we will use for synchronisation */
174
175STDMETHODIMP
176CBaseMediaFilter::SetSyncSource(__inout_opt IReferenceClock *pClock)
177{
178 CAutoLock cObjectLock(m_pLock);
179
180 // Ensure the new one does not go away - even if the same as the old
181 if (pClock) {
182 pClock->AddRef();
183 }
184
185 // if we have a clock, release it
186 if (m_pClock) {
187 m_pClock->Release();
188 }
189
190 // Set the new reference clock (might be NULL)
191 // Should we query it to ensure it is a clock? Consider for a debug build.
192 m_pClock = pClock;
193
194 return NOERROR;
195}
196
197/* Return the clock we are using for synchronisation */
198STDMETHODIMP
199CBaseMediaFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)
200{
201 CheckPointer(pClock,E_POINTER);
202 ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));
203 CAutoLock cObjectLock(m_pLock);
204
205 if (m_pClock) {
206 // returning an interface... addref it...
207 m_pClock->AddRef();
208 }
209 *pClock = (IReferenceClock*)m_pClock;
210 return NOERROR;
211}
212
213
214/* Put the filter into a stopped state */
215
216STDMETHODIMP
217CBaseMediaFilter::Stop()
218{
219 CAutoLock cObjectLock(m_pLock);
220
221 m_State = State_Stopped;
222 return S_OK;
223}
224
225
226/* Put the filter into a paused state */
227
228STDMETHODIMP
229CBaseMediaFilter::Pause()
230{
231 CAutoLock cObjectLock(m_pLock);
232
233 m_State = State_Paused;
234 return S_OK;
235}
236
237
238// Put the filter into a running state.
239
240// The time parameter is the offset to be added to the samples'
241// stream time to get the reference time at which they should be presented.
242//
243// you can either add these two and compare it against the reference clock,
244// or you can call CBaseMediaFilter::StreamTime and compare that against
245// the sample timestamp.
246
247STDMETHODIMP
248CBaseMediaFilter::Run(REFERENCE_TIME tStart)
249{
250 CAutoLock cObjectLock(m_pLock);
251
252 // remember the stream time offset
253 m_tStart = tStart;
254
255 if (m_State == State_Stopped){
256 HRESULT hr = Pause();
257
258 if (FAILED(hr)) {
259 return hr;
260 }
261 }
262 m_State = State_Running;
263 return S_OK;
264}
265
266
267//
268// return the current stream time - samples with start timestamps of this
269// time or before should be rendered by now
270HRESULT
271CBaseMediaFilter::StreamTime(CRefTime& rtStream)
272{
273 // Caller must lock for synchronization
274 // We can't grab the filter lock because we want to be able to call
275 // this from worker threads without deadlocking
276
277 if (m_pClock == NULL) {
278 return VFW_E_NO_CLOCK;
279 }
280
281 // get the current reference time
282 HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);
283 if (FAILED(hr)) {
284 return hr;
285 }
286
287 // subtract the stream offset to get stream time
288 rtStream -= m_tStart;
289
290 return S_OK;
291}
292
293
294//=====================================================================
295//=====================================================================
296// Implements CBaseFilter
297//=====================================================================
298//=====================================================================
299
300
301/* Override this to say what interfaces we support and where */
302
303STDMETHODIMP CBaseFilter::NonDelegatingQueryInterface(REFIID riid,
304 __deref_out void **ppv)
305{
306 /* Do we have this interface */
307
308 if (riid == IID_IBaseFilter) {
309 return GetInterface((IBaseFilter *) this, ppv);
310 } else if (riid == IID_IMediaFilter) {
311 return GetInterface((IMediaFilter *) this, ppv);
312 } else if (riid == IID_IPersist) {
313 return GetInterface((IPersist *) this, ppv);
314 } else if (riid == IID_IAMovieSetup) {
315 return GetInterface((IAMovieSetup *) this, ppv);
316 } else {
317 return CUnknown::NonDelegatingQueryInterface(riid, ppv);
318 }
319}
320
321#ifdef DEBUG
322STDMETHODIMP_(ULONG) CBaseFilter::NonDelegatingRelease()
323{
324 if (m_cRef == 1) {
325 KASSERT(m_pGraph == NULL);
326 }
327 return CUnknown::NonDelegatingRelease();
328}
329#endif
330
331
332/* Constructor */
333
334CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,
335 __inout_opt LPUNKNOWN pUnk,
336 __in CCritSec *pLock,
337 REFCLSID clsid) :
338 CUnknown( pName, pUnk ),
339 m_pLock(pLock),
340 m_clsid(clsid),
341 m_State(State_Stopped),
342 m_pClock(NULL),
343 m_pGraph(NULL),
344 m_pSink(NULL),
345 m_pName(NULL),
346 m_PinVersion(1)
347{
348#ifdef DXMPERF
349 PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );
350#endif // DXMPERF
351
352 ASSERT(pLock != NULL);
353}
354
355/* Passes in a redundant HRESULT argument */
356
357CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,
358 __in_opt LPUNKNOWN pUnk,
359 __in CCritSec *pLock,
360 REFCLSID clsid,
361 __inout HRESULT *phr) :
362 CUnknown( pName, pUnk ),
363 m_pLock(pLock),
364 m_clsid(clsid),
365 m_State(State_Stopped),
366 m_pClock(NULL),
367 m_pGraph(NULL),
368 m_pSink(NULL),
369 m_pName(NULL),
370 m_PinVersion(1)
371{
372#ifdef DXMPERF
373 PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );
374#endif // DXMPERF
375
376 ASSERT(pLock != NULL);
377 UNREFERENCED_PARAMETER(phr);
378}
379
380#ifdef UNICODE
381CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,
382 __in_opt LPUNKNOWN pUnk,
383 __in CCritSec *pLock,
384 REFCLSID clsid) :
385 CUnknown( pName, pUnk ),
386 m_pLock(pLock),
387 m_clsid(clsid),
388 m_State(State_Stopped),
389 m_pClock(NULL),
390 m_pGraph(NULL),
391 m_pSink(NULL),
392 m_pName(NULL),
393 m_PinVersion(1)
394{
395#ifdef DXMPERF
396 PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );
397#endif // DXMPERF
398
399 ASSERT(pLock != NULL);
400}
401CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,
402 __in_opt LPUNKNOWN pUnk,
403 __in CCritSec *pLock,
404 REFCLSID clsid,
405 __inout HRESULT *phr) :
406 CUnknown( pName, pUnk ),
407 m_pLock(pLock),
408 m_clsid(clsid),
409 m_State(State_Stopped),
410 m_pClock(NULL),
411 m_pGraph(NULL),
412 m_pSink(NULL),
413 m_pName(NULL),
414 m_PinVersion(1)
415{
416#ifdef DXMPERF
417 PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );
418#endif // DXMPERF
419
420 ASSERT(pLock != NULL);
421 UNREFERENCED_PARAMETER(phr);
422}
423#endif
424
425/* Destructor */
426
427CBaseFilter::~CBaseFilter()
428{
429#ifdef DXMPERF
430 PERFLOG_DTOR( L"CBaseFilter", (IBaseFilter *) this );
431#endif // DXMPERF
432
433 // NOTE we do NOT hold references on the filtergraph for m_pGraph or m_pSink
434 // When we did we had the circular reference problem. Nothing would go away.
435
436 delete[] m_pName;
437
438 // must be stopped, but can't call Stop here since
439 // our critsec has been destroyed.
440
441 /* Release any clock we were using */
442 if (m_pClock) {
443 m_pClock->Release();
444 m_pClock = NULL;
445 }
446}
447
448/* Return the filter's clsid */
449STDMETHODIMP
450CBaseFilter::GetClassID(__out CLSID *pClsID)
451{
452 CheckPointer(pClsID,E_POINTER);
453 ValidateReadWritePtr(pClsID,sizeof(CLSID));
454 *pClsID = m_clsid;
455 return NOERROR;
456}
457
458/* Override this if your state changes are not done synchronously */
459STDMETHODIMP
460CBaseFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)
461{
462 UNREFERENCED_PARAMETER(dwMSecs);
463 CheckPointer(State,E_POINTER);
464 ValidateReadWritePtr(State,sizeof(FILTER_STATE));
465
466 *State = m_State;
467 return S_OK;
468}
469
470
471/* Set the clock we will use for synchronisation */
472
473STDMETHODIMP
474CBaseFilter::SetSyncSource(__in_opt IReferenceClock *pClock)
475{
476 CAutoLock cObjectLock(m_pLock);
477
478 // Ensure the new one does not go away - even if the same as the old
479 if (pClock) {
480 pClock->AddRef();
481 }
482
483 // if we have a clock, release it
484 if (m_pClock) {
485 m_pClock->Release();
486 }
487
488 // Set the new reference clock (might be NULL)
489 // Should we query it to ensure it is a clock? Consider for a debug build.
490 m_pClock = pClock;
491
492 return NOERROR;
493}
494
495/* Return the clock we are using for synchronisation */
496STDMETHODIMP
497CBaseFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)
498{
499 CheckPointer(pClock,E_POINTER);
500 ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));
501 CAutoLock cObjectLock(m_pLock);
502
503 if (m_pClock) {
504 // returning an interface... addref it...
505 m_pClock->AddRef();
506 }
507 *pClock = (IReferenceClock*)m_pClock;
508 return NOERROR;
509}
510
511
512
513// override CBaseMediaFilter Stop method, to deactivate any pins this
514// filter has.
515STDMETHODIMP
516CBaseFilter::Stop()
517{
518 CAutoLock cObjectLock(m_pLock);
519 HRESULT hr = NOERROR;
520
521 // notify all pins of the state change
522 if (m_State != State_Stopped) {
523 int cPins = GetPinCount();
524 for (int c = 0; c < cPins; c++) {
525
526 CBasePin *pPin = GetPin(c);
527 if (NULL == pPin) {
528 break;
529 }
530
531 // Disconnected pins are not activated - this saves pins worrying
532 // about this state themselves. We ignore the return code to make
533 // sure everyone is inactivated regardless. The base input pin
534 // class can return an error if it has no allocator but Stop can
535 // be used to resync the graph state after something has gone bad
536
537 if (pPin->IsConnected()) {
538 HRESULT hrTmp = pPin->Inactive();
539 if (FAILED(hrTmp) && SUCCEEDED(hr)) {
540 hr = hrTmp;
541 }
542 }
543 }
544 }
545
546#ifdef DXMPERF
547 PERFLOG_STOP( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );
548#endif // DXMPERF
549
550 m_State = State_Stopped;
551 return hr;
552}
553
554
555// override CBaseMediaFilter Pause method to activate any pins
556// this filter has (also called from Run)
557
558STDMETHODIMP
559CBaseFilter::Pause()
560{
561 CAutoLock cObjectLock(m_pLock);
562
563 // notify all pins of the change to active state
564 if (m_State == State_Stopped) {
565 int cPins = GetPinCount();
566 for (int c = 0; c < cPins; c++) {
567
568 CBasePin *pPin = GetPin(c);
569 if (NULL == pPin) {
570 break;
571 }
572
573 // Disconnected pins are not activated - this saves pins
574 // worrying about this state themselves
575
576 if (pPin->IsConnected()) {
577 HRESULT hr = pPin->Active();
578 if (FAILED(hr)) {
579 return hr;
580 }
581 }
582 }
583 }
584
585
586#ifdef DXMPERF
587 PERFLOG_PAUSE( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );
588#endif // DXMPERF
589
590 m_State = State_Paused;
591 return S_OK;
592}
593
594// Put the filter into a running state.
595
596// The time parameter is the offset to be added to the samples'
597// stream time to get the reference time at which they should be presented.
598//
599// you can either add these two and compare it against the reference clock,
600// or you can call CBaseFilter::StreamTime and compare that against
601// the sample timestamp.
602
603STDMETHODIMP
604CBaseFilter::Run(REFERENCE_TIME tStart)
605{
606 CAutoLock cObjectLock(m_pLock);
607
608 // remember the stream time offset
609 m_tStart = tStart;
610
611 if (m_State == State_Stopped){
612 HRESULT hr = Pause();
613
614 if (FAILED(hr)) {
615 return hr;
616 }
617 }
618 // notify all pins of the change to active state
619 if (m_State != State_Running) {
620 int cPins = GetPinCount();
621 for (int c = 0; c < cPins; c++) {
622
623 CBasePin *pPin = GetPin(c);
624 if (NULL == pPin) {
625 break;
626 }
627
628 // Disconnected pins are not activated - this saves pins
629 // worrying about this state themselves
630
631 if (pPin->IsConnected()) {
632 HRESULT hr = pPin->Run(tStart);
633 if (FAILED(hr)) {
634 return hr;
635 }
636 }
637 }
638 }
639
640#ifdef DXMPERF
641 PERFLOG_RUN( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, tStart, m_State );
642#endif // DXMPERF
643
644 m_State = State_Running;
645 return S_OK;
646}
647
648//
649// return the current stream time - samples with start timestamps of this
650// time or before should be rendered by now
651HRESULT
652CBaseFilter::StreamTime(CRefTime& rtStream)
653{
654 // Caller must lock for synchronization
655 // We can't grab the filter lock because we want to be able to call
656 // this from worker threads without deadlocking
657
658 if (m_pClock == NULL) {
659 return VFW_E_NO_CLOCK;
660 }
661
662 // get the current reference time
663 HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);
664 if (FAILED(hr)) {
665 return hr;
666 }
667
668 // subtract the stream offset to get stream time
669 rtStream -= m_tStart;
670
671 return S_OK;
672}
673
674
675/* Create an enumerator for the pins attached to this filter */
676
677STDMETHODIMP
678CBaseFilter::EnumPins(__deref_out IEnumPins **ppEnum)
679{
680 CheckPointer(ppEnum,E_POINTER);
681 ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));
682
683 /* Create a new ref counted enumerator */
684
685 *ppEnum = new CEnumPins(this,
686 NULL);
687
688 return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;
689}
690
691
692// default behaviour of FindPin is to assume pins are named
693// by their pin names
694STDMETHODIMP
695CBaseFilter::FindPin(
696 LPCWSTR Id,
697 __deref_out IPin ** ppPin
698)
699{
700 CheckPointer(ppPin,E_POINTER);
701 ValidateReadWritePtr(ppPin,sizeof(IPin *));
702
703 // We're going to search the pin list so maintain integrity
704 CAutoLock lck(m_pLock);
705 int iCount = GetPinCount();
706 for (int i = 0; i < iCount; i++) {
707 CBasePin *pPin = GetPin(i);
708 if (NULL == pPin) {
709 break;
710 }
711
712 if (0 == lstrcmpW(pPin->Name(), Id)) {
713 // Found one that matches
714 //
715 // AddRef() and return it
716 *ppPin = pPin;
717 pPin->AddRef();
718 return S_OK;
719 }
720 }
721 *ppPin = NULL;
722 return VFW_E_NOT_FOUND;
723}
724
725/* Return information about this filter */
726
727STDMETHODIMP
728CBaseFilter::QueryFilterInfo(__out FILTER_INFO * pInfo)
729{
730 CheckPointer(pInfo,E_POINTER);
731 ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));
732
733 if (m_pName) {
734 (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
735 } else {
736 pInfo->achName[0] = L'\0';
737 }
738 pInfo->pGraph = m_pGraph;
739 if (m_pGraph)
740 m_pGraph->AddRef();
741 return NOERROR;
742}
743
744
745/* Provide the filter with a filter graph */
746
747STDMETHODIMP
748CBaseFilter::JoinFilterGraph(
749 __inout_opt IFilterGraph * pGraph,
750 __in_opt LPCWSTR pName)
751{
752 CAutoLock cObjectLock(m_pLock);
753
754 // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)
755
756 m_pGraph = pGraph;
757 if (m_pGraph) {
758 HRESULT hr = m_pGraph->QueryInterface(IID_IMediaEventSink,
759 (void**) &m_pSink);
760 if (FAILED(hr)) {
761 ASSERT(m_pSink == NULL);
762 }
763 else m_pSink->Release(); // we do NOT keep a reference on it.
764 } else {
765 // if graph pointer is null, then we should
766 // also release the IMediaEventSink on the same object - we don't
767 // refcount it, so just set it to null
768 m_pSink = NULL;
769 }
770
771
772 if (m_pName) {
773 delete[] m_pName;
774 m_pName = NULL;
775 }
776
777 if (pName) {
778 size_t namelen;
779 HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &namelen);
780 if (FAILED(hr)) {
781 return hr;
782 }
783 m_pName = new WCHAR[namelen + 1];
784 if (m_pName) {
785 (void)StringCchCopyW(m_pName, namelen + 1, pName);
786 } else {
787 return E_OUTOFMEMORY;
788 }
789 }
790
791#ifdef DXMPERF
792 PERFLOG_JOINGRAPH( m_pName ? m_pName : L"CBaseFilter",(IBaseFilter *) this, pGraph );
793#endif // DXMPERF
794
795 return NOERROR;
796}
797
798
799// return a Vendor information string. Optional - may return E_NOTIMPL.
800// memory returned should be freed using CoTaskMemFree
801// default implementation returns E_NOTIMPL
802STDMETHODIMP
803CBaseFilter::QueryVendorInfo(
804 __deref_out LPWSTR* pVendorInfo)
805{
806 UNREFERENCED_PARAMETER(pVendorInfo);
807 return E_NOTIMPL;
808}
809
810
811// send an event notification to the filter graph if we know about it.
812// returns S_OK if delivered, S_FALSE if the filter graph does not sink
813// events, or an error otherwise.
814HRESULT
815CBaseFilter::NotifyEvent(
816 long EventCode,
817 LONG_PTR EventParam1,
818 LONG_PTR EventParam2)
819{
820 // Snapshot so we don't have to lock up
821 IMediaEventSink *pSink = m_pSink;
822 if (pSink) {
823 if (EC_COMPLETE == EventCode) {
824 EventParam2 = (LONG_PTR)(IBaseFilter*)this;
825 }
826
827 return pSink->Notify(EventCode, EventParam1, EventParam2);
828 } else {
829 return E_NOTIMPL;
830 }
831}
832
833// Request reconnect
834// pPin is the pin to reconnect
835// pmt is the type to reconnect with - can be NULL
836// Calls ReconnectEx on the filter graph
837HRESULT
838CBaseFilter::ReconnectPin(
839 IPin *pPin,
840 __in_opt AM_MEDIA_TYPE const *pmt
841)
842{
843 IFilterGraph2 *pGraph2;
844 if (m_pGraph != NULL) {
845 HRESULT hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void **)&pGraph2);
846 if (SUCCEEDED(hr)) {
847 hr = pGraph2->ReconnectEx(pPin, pmt);
848 pGraph2->Release();
849 return hr;
850 } else {
851 return m_pGraph->Reconnect(pPin);
852 }
853 } else {
854 return E_NOINTERFACE;
855 }
856}
857
858
859
860/* This is the same idea as the media type version does for type enumeration
861 on pins but for the list of pins available. So if the list of pins you
862 provide changes dynamically then either override this virtual function
863 to provide the version number, or more simply call IncrementPinVersion */
864
865LONG CBaseFilter::GetPinVersion()
866{
867 return m_PinVersion;
868}
869
870
871/* Increment the current pin version cookie */
872
873void CBaseFilter::IncrementPinVersion()
874{
875 InterlockedIncrement(&m_PinVersion);
876}
877
878/* register filter */
879
880STDMETHODIMP CBaseFilter::Register()
881{
882 // get setup data, if it exists
883 //
884 LPAMOVIESETUP_FILTER psetupdata = GetSetupData();
885
886 // check we've got data
887 //
888 if( NULL == psetupdata ) return S_FALSE;
889
890 // init is ref counted so call just in case
891 // we're being called cold.
892 //
893 HRESULT hr = CoInitialize( (LPVOID)NULL );
894 ASSERT( SUCCEEDED(hr) );
895
896 // get hold of IFilterMapper
897 //
898 IFilterMapper *pIFM;
899 hr = CoCreateInstance( CLSID_FilterMapper
900 , NULL
901 , CLSCTX_INPROC_SERVER
902 , IID_IFilterMapper
903 , (void **)&pIFM );
904 if( SUCCEEDED(hr) )
905 {
906 hr = AMovieSetupRegisterFilter( psetupdata, pIFM, TRUE );
907 pIFM->Release();
908 }
909
910 // and clear up
911 //
912 CoFreeUnusedLibraries();
913 CoUninitialize();
914
915 return NOERROR;
916}
917
918
919/* unregister filter */
920
921STDMETHODIMP CBaseFilter::Unregister()
922{
923 // get setup data, if it exists
924 //
925 LPAMOVIESETUP_FILTER psetupdata = GetSetupData();
926
927 // check we've got data
928 //
929 if( NULL == psetupdata ) return S_FALSE;
930
931 // OLE init is ref counted so call
932 // just in case we're being called cold.
933 //
934 HRESULT hr = CoInitialize( (LPVOID)NULL );
935 ASSERT( SUCCEEDED(hr) );
936
937 // get hold of IFilterMapper
938 //
939 IFilterMapper *pIFM;
940 hr = CoCreateInstance( CLSID_FilterMapper
941 , NULL
942 , CLSCTX_INPROC_SERVER
943 , IID_IFilterMapper
944 , (void **)&pIFM );
945 if( SUCCEEDED(hr) )
946 {
947 hr = AMovieSetupRegisterFilter( psetupdata, pIFM, FALSE );
948
949 // release interface
950 //
951 pIFM->Release();
952 }
953
954 // clear up
955 //
956 CoFreeUnusedLibraries();
957 CoUninitialize();
958
959 // handle one acceptable "error" - that
960 // of filter not being registered!
961 // (couldn't find a suitable #define'd
962 // name for the error!)
963 //
964 if( 0x80070002 == hr)
965 return NOERROR;
966 else
967 return hr;
968}
969
970
971//=====================================================================
972//=====================================================================
973// Implements CEnumPins
974//=====================================================================
975//=====================================================================
976
977
978CEnumPins::CEnumPins(__in CBaseFilter *pFilter,
979 __in_opt CEnumPins *pEnumPins) :
980 m_Position(0),
981 m_PinCount(0),
982 m_pFilter(pFilter),
983 m_cRef(1), // Already ref counted
984 m_PinCache(NAME("Pin Cache"))
985{
986
987#ifdef DEBUG
988 m_dwCookie = DbgRegisterObjectCreation("CEnumPins", 0);
989#endif
990
991 /* We must be owned by a filter derived from CBaseFilter */
992
993 ASSERT(pFilter != NULL);
994
995 /* Hold a reference count on our filter */
996 m_pFilter->AddRef();
997
998 /* Are we creating a new enumerator */
999
1000 if (pEnumPins == NULL) {
1001 m_Version = m_pFilter->GetPinVersion();
1002 m_PinCount = m_pFilter->GetPinCount();
1003 } else {
1004 ASSERT(m_Position <= m_PinCount);
1005 m_Position = pEnumPins->m_Position;
1006 m_PinCount = pEnumPins->m_PinCount;
1007 m_Version = pEnumPins->m_Version;
1008 m_PinCache.AddTail(&(pEnumPins->m_PinCache));
1009 }
1010}
1011
1012
1013/* Destructor releases the reference count on our filter NOTE since we hold
1014 a reference count on the filter who created us we know it is safe to
1015 release it, no access can be made to it afterwards though as we have just
1016 caused the last reference count to go and the object to be deleted */
1017
1018CEnumPins::~CEnumPins()
1019{
1020 m_pFilter->Release();
1021
1022#ifdef DEBUG
1023 DbgRegisterObjectDestruction(m_dwCookie);
1024#endif
1025}
1026
1027
1028/* Override this to say what interfaces we support where */
1029
1030STDMETHODIMP
1031CEnumPins::QueryInterface(REFIID riid, __deref_out void **ppv)
1032{
1033 CheckPointer(ppv, E_POINTER);
1034
1035 /* Do we have this interface */
1036
1037 if (riid == IID_IEnumPins || riid == IID_IUnknown) {
1038 return GetInterface((IEnumPins *) this, ppv);
1039 } else {
1040 *ppv = NULL;
1041 return E_NOINTERFACE;
1042 }
1043}
1044
1045STDMETHODIMP_(ULONG)
1046CEnumPins::AddRef()
1047{
1048 return InterlockedIncrement(&m_cRef);
1049}
1050
1051STDMETHODIMP_(ULONG)
1052CEnumPins::Release()
1053{
1054 ULONG cRef = InterlockedDecrement(&m_cRef);
1055 if (cRef == 0) {
1056 delete this;
1057 }
1058 return cRef;
1059}
1060
1061/* One of an enumerator's basic member functions allows us to create a cloned
1062 interface that initially has the same state. Since we are taking a snapshot
1063 of an object (current position and all) we must lock access at the start */
1064
1065STDMETHODIMP
1066CEnumPins::Clone(__deref_out IEnumPins **ppEnum)
1067{
1068 CheckPointer(ppEnum,E_POINTER);
1069 ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));
1070 HRESULT hr = NOERROR;
1071
1072 /* Check we are still in sync with the filter */
1073 if (AreWeOutOfSync() == TRUE) {
1074 *ppEnum = NULL;
1075 hr = VFW_E_ENUM_OUT_OF_SYNC;
1076 } else {
1077 *ppEnum = new CEnumPins(m_pFilter,
1078 this);
1079 if (*ppEnum == NULL) {
1080 hr = E_OUTOFMEMORY;
1081 }
1082 }
1083 return hr;
1084}
1085
1086
1087/* Return the next pin after the current position */
1088
1089STDMETHODIMP
1090CEnumPins::Next(ULONG cPins, // place this many pins...
1091 __out_ecount(cPins) IPin **ppPins, // ...in this array
1092 __out_opt ULONG *pcFetched) // actual count passed returned here
1093{
1094 CheckPointer(ppPins,E_POINTER);
1095 ValidateReadWritePtr(ppPins,cPins * sizeof(IPin *));
1096
1097 ASSERT(ppPins);
1098
1099 if (pcFetched!=NULL) {
1100 ValidateWritePtr(pcFetched, sizeof(ULONG));
1101 *pcFetched = 0; // default unless we succeed
1102 }
1103 // now check that the parameter is valid
1104 else if (cPins>1) { // pcFetched == NULL
1105 return E_INVALIDARG;
1106 }
1107 ULONG cFetched = 0; // increment as we get each one.
1108
1109 /* Check we are still in sync with the filter */
1110 if (AreWeOutOfSync() == TRUE) {
1111 // If we are out of sync, we should refresh the enumerator.
1112 // This will reset the position and update the other members, but
1113 // will not clear cache of pins we have already returned.
1114 Refresh();
1115 }
1116
1117 /* Return each pin interface NOTE GetPin returns CBasePin * not addrefed
1118 so we must QI for the IPin (which increments its reference count)
1119 If while we are retrieving a pin from the filter an error occurs we
1120 assume that our internal state is stale with respect to the filter
1121 (for example someone has deleted a pin) so we
1122 return VFW_E_ENUM_OUT_OF_SYNC */
1123
1124 while (cFetched < cPins && m_PinCount > m_Position) {
1125
1126 /* Get the next pin object from the filter */
1127
1128 CBasePin *pPin = m_pFilter->GetPin(m_Position++);
1129 if (pPin == NULL) {
1130 // If this happend, and it's not the first time through, then we've got a problem,
1131 // since we should really go back and release the iPins, which we have previously
1132 // AddRef'ed.
1133 ASSERT( cFetched==0 );
1134 return VFW_E_ENUM_OUT_OF_SYNC;
1135 }
1136
1137 /* We only want to return this pin, if it is not in our cache */
1138 if (0 == m_PinCache.Find(pPin))
1139 {
1140 /* From the object get an IPin interface */
1141
1142 *ppPins = pPin;
1143 pPin->AddRef();
1144
1145 cFetched++;
1146 ppPins++;
1147
1148 m_PinCache.AddTail(pPin);
1149 }
1150 }
1151
1152 if (pcFetched!=NULL) {
1153 *pcFetched = cFetched;
1154 }
1155
1156 return (cPins==cFetched ? NOERROR : S_FALSE);
1157}
1158
1159
1160/* Skip over one or more entries in the enumerator */
1161
1162STDMETHODIMP
1163CEnumPins::Skip(ULONG cPins)
1164{
1165 /* Check we are still in sync with the filter */
1166 if (AreWeOutOfSync() == TRUE) {
1167 return VFW_E_ENUM_OUT_OF_SYNC;
1168 }
1169
1170 /* Work out how many pins are left to skip over */
1171 /* We could position at the end if we are asked to skip too many... */
1172 /* ..which would match the base implementation for CEnumMediaTypes::Skip */
1173
1174 ULONG PinsLeft = m_PinCount - m_Position;
1175 if (cPins > PinsLeft) {
1176 return S_FALSE;
1177 }
1178 m_Position += cPins;
1179 return NOERROR;
1180}
1181
1182
1183/* Set the current position back to the start */
1184/* Reset has 4 simple steps:
1185 *
1186 * Set position to head of list
1187 * Sync enumerator with object being enumerated
1188 * Clear the cache of pins already returned
1189 * return S_OK
1190 */
1191
1192STDMETHODIMP
1193CEnumPins::Reset()
1194{
1195 m_Version = m_pFilter->GetPinVersion();
1196 m_PinCount = m_pFilter->GetPinCount();
1197
1198 m_Position = 0;
1199
1200 // Clear the cache
1201 m_PinCache.RemoveAll();
1202
1203 return S_OK;
1204}
1205
1206
1207/* Set the current position back to the start */
1208/* Refresh has 3 simple steps:
1209 *
1210 * Set position to head of list
1211 * Sync enumerator with object being enumerated
1212 * return S_OK
1213 */
1214
1215STDMETHODIMP
1216CEnumPins::Refresh()
1217{
1218 m_Version = m_pFilter->GetPinVersion();
1219 m_PinCount = m_pFilter->GetPinCount();
1220
1221 m_Position = 0;
1222 return S_OK;
1223}
1224
1225
1226//=====================================================================
1227//=====================================================================
1228// Implements CEnumMediaTypes
1229//=====================================================================
1230//=====================================================================
1231
1232
1233CEnumMediaTypes::CEnumMediaTypes(__in CBasePin *pPin,
1234 __in_opt CEnumMediaTypes *pEnumMediaTypes) :
1235 m_Position(0),
1236 m_pPin(pPin),
1237 m_cRef(1)
1238{
1239
1240#ifdef DEBUG
1241 m_dwCookie = DbgRegisterObjectCreation("CEnumMediaTypes", 0);
1242#endif
1243
1244 /* We must be owned by a pin derived from CBasePin */
1245
1246 ASSERT(pPin != NULL);
1247
1248 /* Hold a reference count on our pin */
1249 m_pPin->AddRef();
1250
1251 /* Are we creating a new enumerator */
1252
1253 if (pEnumMediaTypes == NULL) {
1254 m_Version = m_pPin->GetMediaTypeVersion();
1255 return;
1256 }
1257
1258 m_Position = pEnumMediaTypes->m_Position;
1259 m_Version = pEnumMediaTypes->m_Version;
1260}
1261
1262
1263/* Destructor releases the reference count on our base pin. NOTE since we hold
1264 a reference count on the pin who created us we know it is safe to release
1265 it, no access can be made to it afterwards though as we might have just
1266 caused the last reference count to go and the object to be deleted */
1267
1268CEnumMediaTypes::~CEnumMediaTypes()
1269{
1270#ifdef DEBUG
1271 DbgRegisterObjectDestruction(m_dwCookie);
1272#endif
1273 m_pPin->Release();
1274}
1275
1276
1277/* Override this to say what interfaces we support where */
1278
1279STDMETHODIMP
1280CEnumMediaTypes::QueryInterface(REFIID riid, __deref_out void **ppv)
1281{
1282 CheckPointer(ppv, E_POINTER);
1283
1284 /* Do we have this interface */
1285
1286 if (riid == IID_IEnumMediaTypes || riid == IID_IUnknown) {
1287 return GetInterface((IEnumMediaTypes *) this, ppv);
1288 } else {
1289 *ppv = NULL;
1290 return E_NOINTERFACE;
1291 }
1292}
1293
1294STDMETHODIMP_(ULONG)
1295CEnumMediaTypes::AddRef()
1296{
1297 return InterlockedIncrement(&m_cRef);
1298}
1299
1300STDMETHODIMP_(ULONG)
1301CEnumMediaTypes::Release()
1302{
1303 ULONG cRef = InterlockedDecrement(&m_cRef);
1304 if (cRef == 0) {
1305 delete this;
1306 }
1307 return cRef;
1308}
1309
1310/* One of an enumerator's basic member functions allows us to create a cloned
1311 interface that initially has the same state. Since we are taking a snapshot
1312 of an object (current position and all) we must lock access at the start */
1313
1314STDMETHODIMP
1315CEnumMediaTypes::Clone(__deref_out IEnumMediaTypes **ppEnum)
1316{
1317 CheckPointer(ppEnum,E_POINTER);
1318 ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
1319 HRESULT hr = NOERROR;
1320
1321 /* Check we are still in sync with the pin */
1322 if (AreWeOutOfSync() == TRUE) {
1323 *ppEnum = NULL;
1324 hr = VFW_E_ENUM_OUT_OF_SYNC;
1325 } else {
1326
1327 *ppEnum = new CEnumMediaTypes(m_pPin,
1328 this);
1329
1330 if (*ppEnum == NULL) {
1331 hr = E_OUTOFMEMORY;
1332 }
1333 }
1334 return hr;
1335}
1336
1337
1338/* Enumerate the next pin(s) after the current position. The client using this
1339 interface passes in a pointer to an array of pointers each of which will
1340 be filled in with a pointer to a fully initialised media type format
1341 Return NOERROR if it all works,
1342 S_FALSE if fewer than cMediaTypes were enumerated.
1343 VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by
1344 state changes in the filter
1345 The actual count always correctly reflects the number of types in the array.
1346*/
1347
1348STDMETHODIMP
1349CEnumMediaTypes::Next(ULONG cMediaTypes, // place this many types...
1350 __out_ecount(cMediaTypes) AM_MEDIA_TYPE **ppMediaTypes, // ...in this array
1351 __out ULONG *pcFetched) // actual count passed
1352{
1353 CheckPointer(ppMediaTypes,E_POINTER);
1354 ValidateReadWritePtr(ppMediaTypes,cMediaTypes * sizeof(AM_MEDIA_TYPE *));
1355 /* Check we are still in sync with the pin */
1356 if (AreWeOutOfSync() == TRUE) {
1357 return VFW_E_ENUM_OUT_OF_SYNC;
1358 }
1359
1360 if (pcFetched!=NULL) {
1361 ValidateWritePtr(pcFetched, sizeof(ULONG));
1362 *pcFetched = 0; // default unless we succeed
1363 }
1364 // now check that the parameter is valid
1365 else if (cMediaTypes>1) { // pcFetched == NULL
1366 return E_INVALIDARG;
1367 }
1368 ULONG cFetched = 0; // increment as we get each one.
1369
1370 /* Return each media type by asking the filter for them in turn - If we
1371 have an error code retured to us while we are retrieving a media type
1372 we assume that our internal state is stale with respect to the filter
1373 (for example the window size changing) so we return
1374 VFW_E_ENUM_OUT_OF_SYNC */
1375
1376 while (cMediaTypes) {
1377
1378 CMediaType cmt;
1379
1380 HRESULT hr = m_pPin->GetMediaType(m_Position++, &cmt);
1381 if (S_OK != hr) {
1382 break;
1383 }
1384
1385 /* We now have a CMediaType object that contains the next media type
1386 but when we assign it to the array position we CANNOT just assign
1387 the AM_MEDIA_TYPE structure because as soon as the object goes out of
1388 scope it will delete the memory we have just copied. The function
1389 we use is CreateMediaType which allocates a task memory block */
1390
1391 /* Transfer across the format block manually to save an allocate
1392 and free on the format block and generally go faster */
1393
1394 *ppMediaTypes = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
1395 if (*ppMediaTypes == NULL) {
1396 break;
1397 }
1398
1399 /* Do a regular copy */
1400 **ppMediaTypes = cmt;
1401
1402 /* Make sure the destructor doesn't free these */
1403 cmt.pbFormat = NULL;
1404 cmt.cbFormat = NULL;
1405 cmt.pUnk = NULL;
1406
1407
1408 ppMediaTypes++;
1409 cFetched++;
1410 cMediaTypes--;
1411 }
1412
1413 if (pcFetched!=NULL) {
1414 *pcFetched = cFetched;
1415 }
1416
1417 return ( cMediaTypes==0 ? NOERROR : S_FALSE );
1418}
1419
1420
1421/* Skip over one or more entries in the enumerator */
1422
1423STDMETHODIMP
1424CEnumMediaTypes::Skip(ULONG cMediaTypes)
1425{
1426 // If we're skipping 0 elements we're guaranteed to skip the
1427 // correct number of elements
1428 if (cMediaTypes == 0) {
1429 return S_OK;
1430 }
1431
1432 /* Check we are still in sync with the pin */
1433 if (AreWeOutOfSync() == TRUE) {
1434 return VFW_E_ENUM_OUT_OF_SYNC;
1435 }
1436
1437 m_Position += cMediaTypes;
1438
1439 /* See if we're over the end */
1440 CMediaType cmt;
1441 return S_OK == m_pPin->GetMediaType(m_Position - 1, &cmt) ? S_OK : S_FALSE;
1442}
1443
1444
1445/* Set the current position back to the start */
1446/* Reset has 3 simple steps:
1447 *
1448 * set position to head of list
1449 * sync enumerator with object being enumerated
1450 * return S_OK
1451 */
1452
1453STDMETHODIMP
1454CEnumMediaTypes::Reset()
1455
1456{
1457 m_Position = 0;
1458
1459 // Bring the enumerator back into step with the current state. This
1460 // may be a noop but ensures that the enumerator will be valid on the
1461 // next call.
1462 m_Version = m_pPin->GetMediaTypeVersion();
1463 return NOERROR;
1464}
1465
1466
1467//=====================================================================
1468//=====================================================================
1469// Implements CBasePin
1470//=====================================================================
1471//=====================================================================
1472
1473
1474/* NOTE The implementation of this class calls the CUnknown constructor with
1475 a NULL outer unknown pointer. This has the effect of making us a self
1476 contained class, ie any QueryInterface, AddRef or Release calls will be
1477 routed to the class's NonDelegatingUnknown methods. You will typically
1478 find that the classes that do this then override one or more of these
1479 virtual functions to provide more specialised behaviour. A good example
1480 of this is where a class wants to keep the QueryInterface internal but
1481 still wants its lifetime controlled by the external object */
1482
1483/* Constructor */
1484
1485CBasePin::CBasePin(__in_opt LPCTSTR pObjectName,
1486 __in CBaseFilter *pFilter,
1487 __in CCritSec *pLock,
1488 __inout HRESULT *phr,
1489 __in_opt LPCWSTR pName,
1490 PIN_DIRECTION dir) :
1491 CUnknown( pObjectName, NULL ),
1492 m_pFilter(pFilter),
1493 m_pLock(pLock),
1494 m_pName(NULL),
1495 m_Connected(NULL),
1496 m_dir(dir),
1497 m_bRunTimeError(FALSE),
1498 m_pQSink(NULL),
1499 m_TypeVersion(1),
1500 m_tStart(),
1501 m_tStop(MAX_TIME),
1502 m_bCanReconnectWhenActive(false),
1503 m_bTryMyTypesFirst(false),
1504 m_dRate(1.0)
1505{
1506 /* WARNING - pFilter is often not a properly constituted object at
1507 this state (in particular QueryInterface may not work) - this
1508 is because its owner is often its containing object and we
1509 have been called from the containing object's constructor so
1510 the filter's owner has not yet had its CUnknown constructor
1511 called
1512 */
1513#ifdef DXMPERF
1514 PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );
1515#endif // DXMPERF
1516
1517 ASSERT(pFilter != NULL);
1518 ASSERT(pLock != NULL);
1519
1520 if (pName) {
1521 size_t cchName;
1522 HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);
1523 if (SUCCEEDED(hr)) {
1524 m_pName = new WCHAR[cchName + 1];
1525 if (m_pName) {
1526 (void)StringCchCopyW(m_pName, cchName + 1, pName);
1527 }
1528 }
1529 }
1530
1531#ifdef DEBUG
1532 m_cRef = 0;
1533#endif
1534}
1535
1536#ifdef UNICODE
1537CBasePin::CBasePin(__in_opt LPCSTR pObjectName,
1538 __in CBaseFilter *pFilter,
1539 __in CCritSec *pLock,
1540 __inout HRESULT *phr,
1541 __in_opt LPCWSTR pName,
1542 PIN_DIRECTION dir) :
1543 CUnknown( pObjectName, NULL ),
1544 m_pFilter(pFilter),
1545 m_pLock(pLock),
1546 m_pName(NULL),
1547 m_Connected(NULL),
1548 m_dir(dir),
1549 m_bRunTimeError(FALSE),
1550 m_pQSink(NULL),
1551 m_TypeVersion(1),
1552 m_tStart(),
1553 m_tStop(MAX_TIME),
1554 m_bCanReconnectWhenActive(false),
1555 m_bTryMyTypesFirst(false),
1556 m_dRate(1.0)
1557{
1558 /* WARNING - pFilter is often not a properly constituted object at
1559 this state (in particular QueryInterface may not work) - this
1560 is because its owner is often its containing object and we
1561 have been called from the containing object's constructor so
1562 the filter's owner has not yet had its CUnknown constructor
1563 called
1564 */
1565#ifdef DXMPERF
1566 PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );
1567#endif // DXMPERF
1568
1569 ASSERT(pFilter != NULL);
1570 ASSERT(pLock != NULL);
1571
1572 if (pName) {
1573 size_t cchName;
1574 HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);
1575 if (SUCCEEDED(hr)) {
1576 m_pName = new WCHAR[cchName + 1];
1577 if (m_pName) {
1578 (void)StringCchCopyW(m_pName, cchName + 1, pName);
1579 }
1580 }
1581 }
1582
1583
1584#ifdef DEBUG
1585 m_cRef = 0;
1586#endif
1587}
1588#endif
1589
1590/* Destructor since a connected pin holds a reference count on us there is
1591 no way that we can be deleted unless we are not currently connected */
1592
1593CBasePin::~CBasePin()
1594{
1595#ifdef DXMPERF
1596 PERFLOG_DTOR( m_pName ? m_pName : L"CBasePin", (IPin *) this );
1597#endif // DXMPERF
1598
1599 // We don't call disconnect because if the filter is going away
1600 // all the pins must have a reference count of zero so they must
1601 // have been disconnected anyway - (but check the assumption)
1602 ASSERT(m_Connected == FALSE);
1603
1604 delete[] m_pName;
1605
1606 // check the internal reference count is consistent
1607 ASSERT(m_cRef == 0);
1608}
1609
1610
1611/* Override this to say what interfaces we support and where */
1612
1613STDMETHODIMP
1614CBasePin::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
1615{
1616 /* Do we have this interface */
1617
1618 if (riid == IID_IPin) {
1619 return GetInterface((IPin *) this, ppv);
1620 } else if (riid == IID_IQualityControl) {
1621 return GetInterface((IQualityControl *) this, ppv);
1622 } else {
1623 return CUnknown::NonDelegatingQueryInterface(riid, ppv);
1624 }
1625}
1626
1627
1628/* Override to increment the owning filter's reference count */
1629
1630STDMETHODIMP_(ULONG)
1631CBasePin::NonDelegatingAddRef()
1632{
1633 ASSERT(InterlockedIncrement(&m_cRef) > 0);
1634 return m_pFilter->AddRef();
1635}
1636
1637
1638/* Override to decrement the owning filter's reference count */
1639
1640STDMETHODIMP_(ULONG)
1641CBasePin::NonDelegatingRelease()
1642{
1643 ASSERT(InterlockedDecrement(&m_cRef) >= 0);
1644 return m_pFilter->Release();
1645}
1646
1647
1648/* Displays pin connection information */
1649
1650#ifdef DEBUG
1651void
1652CBasePin::DisplayPinInfo(IPin *pReceivePin)
1653{
1654
1655 if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {
1656 PIN_INFO ConnectPinInfo;
1657 PIN_INFO ReceivePinInfo;
1658
1659 if (FAILED(QueryPinInfo(&ConnectPinInfo))) {
1660 StringCchCopyW(ConnectPinInfo.achName, sizeof(ConnectPinInfo.achName)/sizeof(WCHAR), L"Bad Pin");
1661 } else {
1662 QueryPinInfoReleaseFilter(ConnectPinInfo);
1663 }
1664
1665 if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {
1666 StringCchCopyW(ReceivePinInfo.achName, sizeof(ReceivePinInfo.achName)/sizeof(WCHAR), L"Bad Pin");
1667 } else {
1668 QueryPinInfoReleaseFilter(ReceivePinInfo);
1669 }
1670
1671 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying to connect Pins :")));
1672 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" <%ls>"), ConnectPinInfo.achName));
1673 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" <%ls>"), ReceivePinInfo.achName));
1674 }
1675}
1676#endif
1677
1678
1679/* Displays general information on the pin media type */
1680
1681#ifdef DEBUG
1682void CBasePin::DisplayTypeInfo(IPin *pPin, const CMediaType *pmt)
1683{
1684 UNREFERENCED_PARAMETER(pPin);
1685 if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {
1686 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying media type:")));
1687 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" major type: %hs"),
1688 GuidNames[*pmt->Type()]));
1689 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" sub type : %hs"),
1690 GuidNames[*pmt->Subtype()]));
1691 }
1692}
1693#endif
1694
1695/* Asked to connect to a pin. A pin is always attached to an owning filter
1696 object so we always delegate our locking to that object. We first of all
1697 retrieve a media type enumerator for the input pin and see if we accept
1698 any of the formats that it would ideally like, failing that we retrieve
1699 our enumerator and see if it will accept any of our preferred types */
1700
1701STDMETHODIMP
1702CBasePin::Connect(
1703 IPin * pReceivePin,
1704 __in_opt const AM_MEDIA_TYPE *pmt // optional media type
1705)
1706{
1707 CheckPointer(pReceivePin,E_POINTER);
1708 ValidateReadPtr(pReceivePin,sizeof(IPin));
1709 CAutoLock cObjectLock(m_pLock);
1710 DisplayPinInfo(pReceivePin);
1711
1712 /* See if we are already connected */
1713
1714 if (m_Connected) {
1715 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));
1716 return VFW_E_ALREADY_CONNECTED;
1717 }
1718
1719 /* See if the filter is active */
1720 if (!IsStopped() && !m_bCanReconnectWhenActive) {
1721 return VFW_E_NOT_STOPPED;
1722 }
1723
1724
1725 // Find a mutually agreeable media type -
1726 // Pass in the template media type. If this is partially specified,
1727 // each of the enumerated media types will need to be checked against
1728 // it. If it is non-null and fully specified, we will just try to connect
1729 // with this.
1730
1731 const CMediaType * ptype = (CMediaType*)pmt;
1732 HRESULT hr = AgreeMediaType(pReceivePin, ptype);
1733 if (FAILED(hr)) {
1734 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));
1735
1736 // Since the procedure is already returning an error code, there
1737 // is nothing else this function can do to report the error.
1738 EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
1739
1740#ifdef DXMPERF
1741 PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );
1742#endif // DXMPERF
1743
1744 return hr;
1745 }
1746
1747 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));
1748
1749#ifdef DXMPERF
1750 PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );
1751#endif // DXMPERF
1752
1753 return NOERROR;
1754}
1755
1756// given a specific media type, attempt a connection (includes
1757// checking that the type is acceptable to this pin)
1758HRESULT
1759CBasePin::AttemptConnection(
1760 IPin* pReceivePin, // connect to this pin
1761 const CMediaType* pmt // using this type
1762)
1763{
1764 // The caller should hold the filter lock becasue this function
1765 // uses m_Connected. The caller should also hold the filter lock
1766 // because this function calls SetMediaType(), IsStopped() and
1767 // CompleteConnect().
1768 ASSERT(CritCheckIn(m_pLock));
1769
1770 // Check that the connection is valid -- need to do this for every
1771 // connect attempt since BreakConnect will undo it.
1772 HRESULT hr = CheckConnect(pReceivePin);
1773 if (FAILED(hr)) {
1774 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));
1775
1776 // Since the procedure is already returning an error code, there
1777 // is nothing else this function can do to report the error.
1778 EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
1779
1780 return hr;
1781 }
1782
1783 DisplayTypeInfo(pReceivePin, pmt);
1784
1785 /* Check we will accept this media type */
1786
1787 hr = CheckMediaType(pmt);
1788 if (hr == NOERROR) {
1789
1790 /* Make ourselves look connected otherwise ReceiveConnection
1791 may not be able to complete the connection
1792 */
1793 m_Connected = pReceivePin;
1794 m_Connected->AddRef();
1795 hr = SetMediaType(pmt);
1796 if (SUCCEEDED(hr)) {
1797 /* See if the other pin will accept this type */
1798
1799 hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);
1800 if (SUCCEEDED(hr)) {
1801 /* Complete the connection */
1802
1803 hr = CompleteConnect(pReceivePin);
1804 if (SUCCEEDED(hr)) {
1805 return hr;
1806 } else {
1807 DbgLog((LOG_TRACE,
1808 CONNECT_TRACE_LEVEL,
1809 TEXT("Failed to complete connection")));
1810 pReceivePin->Disconnect();
1811 }
1812 }
1813 }
1814 } else {
1815 // we cannot use this media type
1816
1817 // return a specific media type error if there is one
1818 // or map a general failure code to something more helpful
1819 // (in particular S_FALSE gets changed to an error code)
1820 if (SUCCEEDED(hr) ||
1821 (hr == E_FAIL) ||
1822 (hr == E_INVALIDARG)) {
1823 hr = VFW_E_TYPE_NOT_ACCEPTED;
1824 }
1825 }
1826
1827 // BreakConnect and release any connection here in case CheckMediaType
1828 // failed, or if we set anything up during a call back during
1829 // ReceiveConnection.
1830
1831 // Since the procedure is already returning an error code, there
1832 // is nothing else this function can do to report the error.
1833 EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
1834
1835 /* If failed then undo our state */
1836 if (m_Connected) {
1837 m_Connected->Release();
1838 m_Connected = NULL;
1839 }
1840
1841 return hr;
1842}
1843
1844/* Given an enumerator we cycle through all the media types it proposes and
1845 firstly suggest them to our derived pin class and if that succeeds try
1846 them with the pin in a ReceiveConnection call. This means that if our pin
1847 proposes a media type we still check in here that we can support it. This
1848 is deliberate so that in simple cases the enumerator can hold all of the
1849 media types even if some of them are not really currently available */
1850
1851HRESULT CBasePin::TryMediaTypes(
1852 IPin *pReceivePin,
1853 __in_opt const CMediaType *pmt,
1854 IEnumMediaTypes *pEnum)
1855{
1856 /* Reset the current enumerator position */
1857
1858 HRESULT hr = pEnum->Reset();
1859 if (FAILED(hr)) {
1860 return hr;
1861 }
1862
1863 CMediaType *pMediaType = NULL;
1864 ULONG ulMediaCount = 0;
1865
1866 // attempt to remember a specific error code if there is one
1867 HRESULT hrFailure = S_OK;
1868
1869 for (;;) {
1870
1871 /* Retrieve the next media type NOTE each time round the loop the
1872 enumerator interface will allocate another AM_MEDIA_TYPE structure
1873 If we are successful then we copy it into our output object, if
1874 not then we must delete the memory allocated before returning */
1875
1876 hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);
1877 if (hr != S_OK) {
1878 if (S_OK == hrFailure) {
1879 hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
1880 }
1881 return hrFailure;
1882 }
1883
1884
1885 ASSERT(ulMediaCount == 1);
1886 ASSERT(pMediaType);
1887
1888 // check that this matches the partial type (if any)
1889
1890 if (pMediaType &&
1891 ((pmt == NULL) ||
1892 pMediaType->MatchesPartial(pmt))) {
1893
1894 hr = AttemptConnection(pReceivePin, pMediaType);
1895
1896 // attempt to remember a specific error code
1897 if (FAILED(hr) &&
1898 SUCCEEDED(hrFailure) &&
1899 (hr != E_FAIL) &&
1900 (hr != E_INVALIDARG) &&
1901 (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
1902 hrFailure = hr;
1903 }
1904 } else {
1905 hr = VFW_E_NO_ACCEPTABLE_TYPES;
1906 }
1907
1908 if(pMediaType) {
1909 DeleteMediaType(pMediaType);
1910 pMediaType = NULL;
1911 }
1912
1913 if (S_OK == hr) {
1914 return hr;
1915 }
1916 }
1917}
1918
1919
1920/* This is called to make the connection, including the taask of finding
1921 a media type for the pin connection. pmt is the proposed media type
1922 from the Connect call: if this is fully specified, we will try that.
1923 Otherwise we enumerate and try all the input pin's types first and
1924 if that fails we then enumerate and try all our preferred media types.
1925 For each media type we check it against pmt (if non-null and partially
1926 specified) as well as checking that both pins will accept it.
1927 */
1928
1929HRESULT CBasePin::AgreeMediaType(
1930 IPin *pReceivePin,
1931 const CMediaType *pmt)
1932{
1933 ASSERT(pReceivePin);
1934 IEnumMediaTypes *pEnumMediaTypes = NULL;
1935
1936 // if the media type is fully specified then use that
1937 if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {
1938
1939 // if this media type fails, then we must fail the connection
1940 // since if pmt is nonnull we are only allowed to connect
1941 // using a type that matches it.
1942
1943 return AttemptConnection(pReceivePin, pmt);
1944 }
1945
1946
1947 /* Try the other pin's enumerator */
1948
1949 HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
1950
1951 for (int i = 0; i < 2; i++) {
1952 HRESULT hr;
1953 if (i == (int)m_bTryMyTypesFirst) {
1954 hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);
1955 } else {
1956 hr = EnumMediaTypes(&pEnumMediaTypes);
1957 }
1958 if (SUCCEEDED(hr)) {
1959 ASSERT(pEnumMediaTypes);
1960 hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);
1961 pEnumMediaTypes->Release();
1962 if (SUCCEEDED(hr)) {
1963 return NOERROR;
1964 } else {
1965 // try to remember specific error codes if there are any
1966 if ((hr != E_FAIL) &&
1967 (hr != E_INVALIDARG) &&
1968 (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
1969 hrFailure = hr;
1970 }
1971 }
1972 }
1973 }
1974
1975 return hrFailure;
1976}
1977
1978
1979/* Called when we want to complete a connection to another filter. Failing
1980 this will also fail the connection and disconnect the other pin as well */
1981
1982HRESULT
1983CBasePin::CompleteConnect(IPin *pReceivePin)
1984{
1985 UNREFERENCED_PARAMETER(pReceivePin);
1986 return NOERROR;
1987}
1988
1989
1990/* This is called to set the format for a pin connection - CheckMediaType
1991 will have been called to check the connection format and if it didn't
1992 return an error code then this (virtual) function will be invoked */
1993
1994HRESULT
1995CBasePin::SetMediaType(const CMediaType *pmt)
1996{
1997 HRESULT hr = m_mt.Set(*pmt);
1998 if (FAILED(hr)) {
1999 return hr;
2000 }
2001
2002 return NOERROR;
2003}
2004
2005
2006/* This is called during Connect() to provide a virtual method that can do
2007 any specific check needed for connection such as QueryInterface. This
2008 base class method just checks that the pin directions don't match */
2009
2010HRESULT
2011CBasePin::CheckConnect(IPin * pPin)
2012{
2013 /* Check that pin directions DONT match */
2014
2015 PIN_DIRECTION pd;
2016 pPin->QueryDirection(&pd);
2017
2018 ASSERT((pd == PINDIR_OUTPUT) || (pd == PINDIR_INPUT));
2019 ASSERT((m_dir == PINDIR_OUTPUT) || (m_dir == PINDIR_INPUT));
2020
2021 // we should allow for non-input and non-output connections?
2022 if (pd == m_dir) {
2023 return VFW_E_INVALID_DIRECTION;
2024 }
2025 return NOERROR;
2026}
2027
2028
2029/* This is called when we realise we can't make a connection to the pin and
2030 must undo anything we did in CheckConnect - override to release QIs done */
2031
2032HRESULT
2033CBasePin::BreakConnect()
2034{
2035 return NOERROR;
2036}
2037
2038
2039/* Called normally by an output pin on an input pin to try and establish a
2040 connection.
2041*/
2042
2043STDMETHODIMP
2044CBasePin::ReceiveConnection(
2045 IPin * pConnector, // this is the pin who we will connect to
2046 const AM_MEDIA_TYPE *pmt // this is the media type we will exchange
2047)
2048{
2049 CheckPointer(pConnector,E_POINTER);
2050 CheckPointer(pmt,E_POINTER);
2051 ValidateReadPtr(pConnector,sizeof(IPin));
2052 ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
2053 CAutoLock cObjectLock(m_pLock);
2054
2055 /* Are we already connected */
2056 if (m_Connected) {
2057 return VFW_E_ALREADY_CONNECTED;
2058 }
2059
2060 /* See if the filter is active */
2061 if (!IsStopped() && !m_bCanReconnectWhenActive) {
2062 return VFW_E_NOT_STOPPED;
2063 }
2064
2065 HRESULT hr = CheckConnect(pConnector);
2066 if (FAILED(hr)) {
2067 // Since the procedure is already returning an error code, there
2068 // is nothing else this function can do to report the error.
2069 EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
2070
2071#ifdef DXMPERF
2072 PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
2073#endif // DXMPERF
2074
2075 return hr;
2076 }
2077
2078 /* Ask derived class if this media type is ok */
2079
2080 CMediaType * pcmt = (CMediaType*) pmt;
2081 hr = CheckMediaType(pcmt);
2082 if (hr != NOERROR) {
2083 // no -we don't support this media type
2084
2085 // Since the procedure is already returning an error code, there
2086 // is nothing else this function can do to report the error.
2087 EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
2088
2089 // return a specific media type error if there is one
2090 // or map a general failure code to something more helpful
2091 // (in particular S_FALSE gets changed to an error code)
2092 if (SUCCEEDED(hr) ||
2093 (hr == E_FAIL) ||
2094 (hr == E_INVALIDARG)) {
2095 hr = VFW_E_TYPE_NOT_ACCEPTED;
2096 }
2097
2098#ifdef DXMPERF
2099 PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
2100#endif // DXMPERF
2101
2102 return hr;
2103 }
2104
2105 /* Complete the connection */
2106
2107 m_Connected = pConnector;
2108 m_Connected->AddRef();
2109 hr = SetMediaType(pcmt);
2110 if (SUCCEEDED(hr)) {
2111 hr = CompleteConnect(pConnector);
2112 if (SUCCEEDED(hr)) {
2113
2114#ifdef DXMPERF
2115 PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );
2116#endif // DXMPERF
2117
2118 return NOERROR;
2119 }
2120 }
2121
2122 DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));
2123 m_Connected->Release();
2124 m_Connected = NULL;
2125
2126 // Since the procedure is already returning an error code, there
2127 // is nothing else this function can do to report the error.
2128 EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
2129
2130#ifdef DXMPERF
2131 PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
2132#endif // DXMPERF
2133
2134 return hr;
2135}
2136
2137
2138/* Called when we want to terminate a pin connection */
2139
2140STDMETHODIMP
2141CBasePin::Disconnect()
2142{
2143 CAutoLock cObjectLock(m_pLock);
2144
2145 /* See if the filter is active */
2146 if (!IsStopped()) {
2147 return VFW_E_NOT_STOPPED;
2148 }
2149
2150 return DisconnectInternal();
2151}
2152
2153STDMETHODIMP
2154CBasePin::DisconnectInternal()
2155{
2156 ASSERT(CritCheckIn(m_pLock));
2157
2158 if (m_Connected) {
2159 HRESULT hr = BreakConnect();
2160 if( FAILED( hr ) ) {
2161
2162#ifdef DXMPERF
2163 PERFLOG_DISCONNECT( (IPin *) this, m_Connected, hr );
2164#endif // DXMPERF
2165
2166 // There is usually a bug in the program if BreakConnect() fails.
2167 DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." );
2168 return hr;
2169 }
2170
2171 m_Connected->Release();
2172 m_Connected = NULL;
2173
2174#ifdef DXMPERF
2175 PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_OK );
2176#endif // DXMPERF
2177
2178 return S_OK;
2179 } else {
2180 // no connection - not an error
2181
2182#ifdef DXMPERF
2183 PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_FALSE );
2184#endif // DXMPERF
2185
2186 return S_FALSE;
2187 }
2188}
2189
2190
2191/* Return an AddRef()'d pointer to the connected pin if there is one */
2192STDMETHODIMP
2193CBasePin::ConnectedTo(
2194 __deref_out IPin **ppPin
2195)
2196{
2197 CheckPointer(ppPin,E_POINTER);
2198 ValidateReadWritePtr(ppPin,sizeof(IPin *));
2199 //
2200 // It's pointless to lock here.
2201 // The caller should ensure integrity.
2202 //
2203
2204 IPin *pPin = m_Connected;
2205 *ppPin = pPin;
2206 if (pPin != NULL) {
2207 pPin->AddRef();
2208 return S_OK;
2209 } else {
2210 ASSERT(*ppPin == NULL);
2211 return VFW_E_NOT_CONNECTED;
2212 }
2213}
2214
2215/* Return the media type of the connection */
2216STDMETHODIMP
2217CBasePin::ConnectionMediaType(
2218 __out AM_MEDIA_TYPE *pmt
2219)
2220{
2221 CheckPointer(pmt,E_POINTER);
2222 ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE));
2223 CAutoLock cObjectLock(m_pLock);
2224
2225 /* Copy constructor of m_mt allocates the memory */
2226 if (IsConnected()) {
2227 CopyMediaType( pmt, &m_mt );
2228 return S_OK;
2229 } else {
2230 ((CMediaType *)pmt)->InitMediaType();
2231 return VFW_E_NOT_CONNECTED;
2232 }
2233}
2234
2235/* Return information about the filter we are connect to */
2236
2237STDMETHODIMP
2238CBasePin::QueryPinInfo(
2239 __out PIN_INFO * pInfo
2240)
2241{
2242 CheckPointer(pInfo,E_POINTER);
2243 ValidateReadWritePtr(pInfo,sizeof(PIN_INFO));
2244
2245 pInfo->pFilter = m_pFilter;
2246 if (m_pFilter) {
2247 m_pFilter->AddRef();
2248 }
2249
2250 if (m_pName) {
2251 (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
2252 } else {
2253 pInfo->achName[0] = L'\0';
2254 }
2255
2256 pInfo->dir = m_dir;
2257
2258 return NOERROR;
2259}
2260
2261STDMETHODIMP
2262CBasePin::QueryDirection(
2263 __out PIN_DIRECTION * pPinDir
2264)
2265{
2266 CheckPointer(pPinDir,E_POINTER);
2267 ValidateReadWritePtr(pPinDir,sizeof(PIN_DIRECTION));
2268
2269 *pPinDir = m_dir;
2270 return NOERROR;
2271}
2272
2273// Default QueryId to return the pin's name
2274STDMETHODIMP
2275CBasePin::QueryId(
2276 __deref_out LPWSTR * Id
2277)
2278{
2279 // We're not going away because someone's got a pointer to us
2280 // so there's no need to lock
2281
2282 return AMGetWideString(Name(), Id);
2283}
2284
2285/* Does this pin support this media type WARNING this interface function does
2286 not lock the main object as it is meant to be asynchronous by nature - if
2287 the media types you support depend on some internal state that is updated
2288 dynamically then you will need to implement locking in a derived class */
2289
2290STDMETHODIMP
2291CBasePin::QueryAccept(
2292 const AM_MEDIA_TYPE *pmt
2293)
2294{
2295 CheckPointer(pmt,E_POINTER);
2296 ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
2297
2298 /* The CheckMediaType method is valid to return error codes if the media
2299 type is horrible, an example might be E_INVALIDARG. What we do here
2300 is map all the error codes into either S_OK or S_FALSE regardless */
2301
2302 HRESULT hr = CheckMediaType((CMediaType*)pmt);
2303 if (FAILED(hr)) {
2304 return S_FALSE;
2305 }
2306 // note that the only defined success codes should be S_OK and S_FALSE...
2307 return hr;
2308}
2309
2310
2311/* This can be called to return an enumerator for the pin's list of preferred
2312 media types. An input pin is not obliged to have any preferred formats
2313 although it can do. For example, the window renderer has a preferred type
2314 which describes a video image that matches the current window size. All
2315 output pins should expose at least one preferred format otherwise it is
2316 possible that neither pin has any types and so no connection is possible */
2317
2318STDMETHODIMP
2319CBasePin::EnumMediaTypes(
2320 __deref_out IEnumMediaTypes **ppEnum
2321)
2322{
2323 CheckPointer(ppEnum,E_POINTER);
2324 ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
2325
2326 /* Create a new ref counted enumerator */
2327
2328 *ppEnum = new CEnumMediaTypes(this,
2329 NULL);
2330
2331 if (*ppEnum == NULL) {
2332 return E_OUTOFMEMORY;
2333 }
2334
2335 return NOERROR;
2336}
2337
2338
2339
2340/* This is a virtual function that returns a media type corresponding with
2341 place iPosition in the list. This base class simply returns an error as
2342 we support no media types by default but derived classes should override */
2343
2344HRESULT CBasePin::GetMediaType(int iPosition, __inout CMediaType *pMediaType)
2345{
2346 UNREFERENCED_PARAMETER(iPosition);
2347 UNREFERENCED_PARAMETER(pMediaType);
2348 return E_UNEXPECTED;
2349}
2350
2351
2352/* This is a virtual function that returns the current media type version.
2353 The base class initialises the media type enumerators with the value 1
2354 By default we always returns that same value. A Derived class may change
2355 the list of media types available and after doing so it should increment
2356 the version either in a method derived from this, or more simply by just
2357 incrementing the m_TypeVersion base pin variable. The type enumerators
2358 call this when they want to see if their enumerations are out of date */
2359
2360LONG CBasePin::GetMediaTypeVersion()
2361{
2362 return m_TypeVersion;
2363}
2364
2365
2366/* Increment the cookie representing the current media type version */
2367
2368void CBasePin::IncrementTypeVersion()
2369{
2370 InterlockedIncrement(&m_TypeVersion);
2371}
2372
2373
2374/* Called by IMediaFilter implementation when the state changes from Stopped
2375 to either paused or running and in derived classes could do things like
2376 commit memory and grab hardware resource (the default is to do nothing) */
2377
2378HRESULT
2379CBasePin::Active(void)
2380{
2381 return NOERROR;
2382}
2383
2384/* Called by IMediaFilter implementation when the state changes from
2385 to either paused to running and in derived classes could do things like
2386 commit memory and grab hardware resource (the default is to do nothing) */
2387
2388HRESULT
2389CBasePin::Run(REFERENCE_TIME tStart)
2390{
2391 UNREFERENCED_PARAMETER(tStart);
2392 return NOERROR;
2393}
2394
2395
2396/* Also called by the IMediaFilter implementation when the state changes to
2397 Stopped at which point you should decommit allocators and free hardware
2398 resources you grabbed in the Active call (default is also to do nothing) */
2399
2400HRESULT
2401CBasePin::Inactive(void)
2402{
2403 m_bRunTimeError = FALSE;
2404 return NOERROR;
2405}
2406
2407
2408// Called when no more data will arrive
2409STDMETHODIMP
2410CBasePin::EndOfStream(void)
2411{
2412 return S_OK;
2413}
2414
2415
2416STDMETHODIMP
2417CBasePin::SetSink(IQualityControl * piqc)
2418{
2419 CAutoLock cObjectLock(m_pLock);
2420 if (piqc) ValidateReadPtr(piqc,sizeof(IQualityControl));
2421 m_pQSink = piqc;
2422 return NOERROR;
2423} // SetSink
2424
2425
2426STDMETHODIMP
2427CBasePin::Notify(IBaseFilter * pSender, Quality q)
2428{
2429 UNREFERENCED_PARAMETER(q);
2430 UNREFERENCED_PARAMETER(pSender);
2431 DbgBreak("IQualityControl::Notify not over-ridden from CBasePin. (IGNORE is OK)");
2432 return E_NOTIMPL;
2433} //Notify
2434
2435
2436// NewSegment notifies of the start/stop/rate applying to the data
2437// about to be received. Default implementation records data and
2438// returns S_OK.
2439// Override this to pass downstream.
2440STDMETHODIMP
2441CBasePin::NewSegment(
2442 REFERENCE_TIME tStart,
2443 REFERENCE_TIME tStop,
2444 double dRate)
2445{
2446 m_tStart = tStart;
2447 m_tStop = tStop;
2448 m_dRate = dRate;
2449
2450 return S_OK;
2451}
2452
2453
2454//=====================================================================
2455//=====================================================================
2456// Implements CBaseOutputPin
2457//=====================================================================
2458//=====================================================================
2459
2460
2461CBaseOutputPin::CBaseOutputPin(__in_opt LPCTSTR pObjectName,
2462 __in CBaseFilter *pFilter,
2463 __in CCritSec *pLock,
2464 __inout HRESULT *phr,
2465 __in_opt LPCWSTR pName) :
2466 CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
2467 m_pAllocator(NULL),
2468 m_pInputPin(NULL)
2469{
2470 ASSERT(pFilter);
2471}
2472
2473#ifdef UNICODE
2474CBaseOutputPin::CBaseOutputPin(__in_opt LPCSTR pObjectName,
2475 __in CBaseFilter *pFilter,
2476 __in CCritSec *pLock,
2477 __inout HRESULT *phr,
2478 __in_opt LPCWSTR pName) :
2479 CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
2480 m_pAllocator(NULL),
2481 m_pInputPin(NULL)
2482{
2483 ASSERT(pFilter);
2484}
2485#endif
2486
2487/* This is called after a media type has been proposed
2488
2489 Try to complete the connection by agreeing the allocator
2490*/
2491HRESULT
2492CBaseOutputPin::CompleteConnect(IPin *pReceivePin)
2493{
2494 UNREFERENCED_PARAMETER(pReceivePin);
2495 return DecideAllocator(m_pInputPin, &m_pAllocator);
2496}
2497
2498
2499/* This method is called when the output pin is about to try and connect to
2500 an input pin. It is at this point that you should try and grab any extra
2501 interfaces that you need, in this case IMemInputPin. Because this is
2502 only called if we are not currently connected we do NOT need to call
2503 BreakConnect. This also makes it easier to derive classes from us as
2504 BreakConnect is only called when we actually have to break a connection
2505 (or a partly made connection) and not when we are checking a connection */
2506
2507/* Overriden from CBasePin */
2508
2509HRESULT
2510CBaseOutputPin::CheckConnect(IPin * pPin)
2511{
2512 HRESULT hr = CBasePin::CheckConnect(pPin);
2513 if (FAILED(hr)) {
2514 return hr;
2515 }
2516
2517 // get an input pin and an allocator interface
2518 hr = pPin->QueryInterface(IID_IMemInputPin, (void **) &m_pInputPin);
2519 if (FAILED(hr)) {
2520 return hr;
2521 }
2522 return NOERROR;
2523}
2524
2525
2526/* Overriden from CBasePin */
2527
2528HRESULT
2529CBaseOutputPin::BreakConnect()
2530{
2531 /* Release any allocator we hold */
2532
2533 if (m_pAllocator) {
2534 // Always decommit the allocator because a downstream filter may or
2535 // may not decommit the connection's allocator. A memory leak could
2536 // occur if the allocator is not decommited when a connection is broken.
2537 HRESULT hr = m_pAllocator->Decommit();
2538 if( FAILED( hr ) ) {
2539 return hr;
2540 }
2541
2542 m_pAllocator->Release();
2543 m_pAllocator = NULL;
2544 }
2545
2546 /* Release any input pin interface we hold */
2547
2548 if (m_pInputPin) {
2549 m_pInputPin->Release();
2550 m_pInputPin = NULL;
2551 }
2552 return NOERROR;
2553}
2554
2555
2556/* This is called when the input pin didn't give us a valid allocator */
2557
2558HRESULT
2559CBaseOutputPin::InitAllocator(__deref_out IMemAllocator **ppAlloc)
2560{
2561 return CreateMemoryAllocator(ppAlloc);
2562}
2563
2564
2565/* Decide on an allocator, override this if you want to use your own allocator
2566 Override DecideBufferSize to call SetProperties. If the input pin fails
2567 the GetAllocator call then this will construct a CMemAllocator and call
2568 DecideBufferSize on that, and if that fails then we are completely hosed.
2569 If the you succeed the DecideBufferSize call, we will notify the input
2570 pin of the selected allocator. NOTE this is called during Connect() which
2571 therefore looks after grabbing and locking the object's critical section */
2572
2573// We query the input pin for its requested properties and pass this to
2574// DecideBufferSize to allow it to fulfill requests that it is happy
2575// with (eg most people don't care about alignment and are thus happy to
2576// use the downstream pin's alignment request).
2577
2578HRESULT
2579CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, __deref_out IMemAllocator **ppAlloc)
2580{
2581 HRESULT hr = NOERROR;
2582 *ppAlloc = NULL;
2583
2584 // get downstream prop request
2585 // the derived class may modify this in DecideBufferSize, but
2586 // we assume that he will consistently modify it the same way,
2587 // so we only get it once
2588 ALLOCATOR_PROPERTIES prop;
2589 ZeroMemory(&prop, sizeof(prop));
2590
2591 // whatever he returns, we assume prop is either all zeros
2592 // or he has filled it out.
2593 pPin->GetAllocatorRequirements(&prop);
2594
2595 // if he doesn't care about alignment, then set it to 1
2596 if (prop.cbAlign == 0) {
2597 prop.cbAlign = 1;
2598 }
2599
2600 /* Try the allocator provided by the input pin */
2601
2602 hr = pPin->GetAllocator(ppAlloc);
2603 if (SUCCEEDED(hr)) {
2604
2605 hr = DecideBufferSize(*ppAlloc, &prop);
2606 if (SUCCEEDED(hr)) {
2607 hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
2608 if (SUCCEEDED(hr)) {
2609 return NOERROR;
2610 }
2611 }
2612 }
2613
2614 /* If the GetAllocator failed we may not have an interface */
2615
2616 if (*ppAlloc) {
2617 (*ppAlloc)->Release();
2618 *ppAlloc = NULL;
2619 }
2620
2621 /* Try the output pin's allocator by the same method */
2622
2623 hr = InitAllocator(ppAlloc);
2624 if (SUCCEEDED(hr)) {
2625
2626 // note - the properties passed here are in the same
2627 // structure as above and may have been modified by
2628 // the previous call to DecideBufferSize
2629 hr = DecideBufferSize(*ppAlloc, &prop);
2630 if (SUCCEEDED(hr)) {
2631 hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
2632 if (SUCCEEDED(hr)) {
2633 return NOERROR;
2634 }
2635 }
2636 }
2637
2638 /* Likewise we may not have an interface to release */
2639
2640 if (*ppAlloc) {
2641 (*ppAlloc)->Release();
2642 *ppAlloc = NULL;
2643 }
2644 return hr;
2645}
2646
2647
2648/* This returns an empty sample buffer from the allocator WARNING the same
2649 dangers and restrictions apply here as described below for Deliver() */
2650
2651HRESULT
2652CBaseOutputPin::GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,
2653 __in_opt REFERENCE_TIME * pStartTime,
2654 __in_opt REFERENCE_TIME * pEndTime,
2655 DWORD dwFlags)
2656{
2657 if (m_pAllocator != NULL) {
2658 return m_pAllocator->GetBuffer(ppSample,pStartTime,pEndTime,dwFlags);
2659 } else {
2660 return E_NOINTERFACE;
2661 }
2662}
2663
2664
2665/* Deliver a filled-in sample to the connected input pin. NOTE the object must
2666 have locked itself before calling us otherwise we may get halfway through
2667 executing this method only to find the filter graph has got in and
2668 disconnected us from the input pin. If the filter has no worker threads
2669 then the lock is best applied on Receive(), otherwise it should be done
2670 when the worker thread is ready to deliver. There is a wee snag to worker
2671 threads that this shows up. The worker thread must lock the object when
2672 it is ready to deliver a sample, but it may have to wait until a state
2673 change has completed, but that may never complete because the state change
2674 is waiting for the worker thread to complete. The way to handle this is for
2675 the state change code to grab the critical section, then set an abort event
2676 for the worker thread, then release the critical section and wait for the
2677 worker thread to see the event we set and then signal that it has finished
2678 (with another event). At which point the state change code can complete */
2679
2680// note (if you've still got any breath left after reading that) that you
2681// need to release the sample yourself after this call. if the connected
2682// input pin needs to hold onto the sample beyond the call, it will addref
2683// the sample itself.
2684
2685// of course you must release this one and call GetDeliveryBuffer for the
2686// next. You cannot reuse it directly.
2687
2688HRESULT
2689CBaseOutputPin::Deliver(IMediaSample * pSample)
2690{
2691 if (m_pInputPin == NULL) {
2692 return VFW_E_NOT_CONNECTED;
2693 }
2694
2695#ifdef DXMPERF
2696 PERFLOG_DELIVER( m_pName ? m_pName : L"CBaseOutputPin", (IPin *) this, (IPin *) m_pInputPin, pSample, &m_mt );
2697#endif // DXMPERF
2698
2699 return m_pInputPin->Receive(pSample);
2700}
2701
2702
2703// called from elsewhere in our filter to pass EOS downstream to
2704// our connected input pin
2705HRESULT
2706CBaseOutputPin::DeliverEndOfStream(void)
2707{
2708 // remember this is on IPin not IMemInputPin
2709 if (m_Connected == NULL) {
2710 return VFW_E_NOT_CONNECTED;
2711 }
2712 return m_Connected->EndOfStream();
2713}
2714
2715
2716/* Commit the allocator's memory, this is called through IMediaFilter
2717 which is responsible for locking the object before calling us */
2718
2719HRESULT
2720CBaseOutputPin::Active(void)
2721{
2722 if (m_pAllocator == NULL) {
2723 return VFW_E_NO_ALLOCATOR;
2724 }
2725 return m_pAllocator->Commit();
2726}
2727
2728
2729/* Free up or unprepare allocator's memory, this is called through
2730 IMediaFilter which is responsible for locking the object first */
2731
2732HRESULT
2733CBaseOutputPin::Inactive(void)
2734{
2735 m_bRunTimeError = FALSE;
2736 if (m_pAllocator == NULL) {
2737 return VFW_E_NO_ALLOCATOR;
2738 }
2739 return m_pAllocator->Decommit();
2740}
2741
2742// we have a default handling of EndOfStream which is to return
2743// an error, since this should be called on input pins only
2744STDMETHODIMP
2745CBaseOutputPin::EndOfStream(void)
2746{
2747 return E_UNEXPECTED;
2748}
2749
2750
2751// BeginFlush should be called on input pins only
2752STDMETHODIMP
2753CBaseOutputPin::BeginFlush(void)
2754{
2755 return E_UNEXPECTED;
2756}
2757
2758// EndFlush should be called on input pins only
2759STDMETHODIMP
2760CBaseOutputPin::EndFlush(void)
2761{
2762 return E_UNEXPECTED;
2763}
2764
2765// call BeginFlush on the connected input pin
2766HRESULT
2767CBaseOutputPin::DeliverBeginFlush(void)
2768{
2769 // remember this is on IPin not IMemInputPin
2770 if (m_Connected == NULL) {
2771 return VFW_E_NOT_CONNECTED;
2772 }
2773 return m_Connected->BeginFlush();
2774}
2775
2776// call EndFlush on the connected input pin
2777HRESULT
2778CBaseOutputPin::DeliverEndFlush(void)
2779{
2780 // remember this is on IPin not IMemInputPin
2781 if (m_Connected == NULL) {
2782 return VFW_E_NOT_CONNECTED;
2783 }
2784 return m_Connected->EndFlush();
2785}
2786// deliver NewSegment to connected pin
2787HRESULT
2788CBaseOutputPin::DeliverNewSegment(
2789 REFERENCE_TIME tStart,
2790 REFERENCE_TIME tStop,
2791 double dRate)
2792{
2793 if (m_Connected == NULL) {
2794 return VFW_E_NOT_CONNECTED;
2795 }
2796 return m_Connected->NewSegment(tStart, tStop, dRate);
2797}
2798
2799
2800//=====================================================================
2801//=====================================================================
2802// Implements CBaseInputPin
2803//=====================================================================
2804//=====================================================================
2805
2806
2807/* Constructor creates a default allocator object */
2808
2809CBaseInputPin::CBaseInputPin(__in_opt LPCTSTR pObjectName,
2810 __in CBaseFilter *pFilter,
2811 __in CCritSec *pLock,
2812 __inout HRESULT *phr,
2813 __in_opt LPCWSTR pPinName) :
2814 CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
2815 m_pAllocator(NULL),
2816 m_bReadOnly(FALSE),
2817 m_bFlushing(FALSE)
2818{
2819 ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
2820}
2821
2822#ifdef UNICODE
2823CBaseInputPin::CBaseInputPin(__in LPCSTR pObjectName,
2824 __in CBaseFilter *pFilter,
2825 __in CCritSec *pLock,
2826 __inout HRESULT *phr,
2827 __in_opt LPCWSTR pPinName) :
2828 CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
2829 m_pAllocator(NULL),
2830 m_bReadOnly(FALSE),
2831 m_bFlushing(FALSE)
2832{
2833 ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
2834}
2835#endif
2836
2837/* Destructor releases it's reference count on the default allocator */
2838
2839CBaseInputPin::~CBaseInputPin()
2840{
2841 if (m_pAllocator != NULL) {
2842 m_pAllocator->Release();
2843 m_pAllocator = NULL;
2844 }
2845}
2846
2847
2848// override this to publicise our interfaces
2849STDMETHODIMP
2850CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
2851{
2852 /* Do we know about this interface */
2853
2854 if (riid == IID_IMemInputPin) {
2855 return GetInterface((IMemInputPin *) this, ppv);
2856 } else {
2857 return CBasePin::NonDelegatingQueryInterface(riid, ppv);
2858 }
2859}
2860
2861
2862/* Return the allocator interface that this input pin would like the output
2863 pin to use. NOTE subsequent calls to GetAllocator should all return an
2864 interface onto the SAME object so we create one object at the start
2865
2866 Note:
2867 The allocator is Release()'d on disconnect and replaced on
2868 NotifyAllocator().
2869
2870 Override this to provide your own allocator.
2871*/
2872
2873STDMETHODIMP
2874CBaseInputPin::GetAllocator(
2875 __deref_out IMemAllocator **ppAllocator)
2876{
2877 CheckPointer(ppAllocator,E_POINTER);
2878 ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
2879 CAutoLock cObjectLock(m_pLock);
2880
2881 if (m_pAllocator == NULL) {
2882 HRESULT hr = CreateMemoryAllocator(&m_pAllocator);
2883 if (FAILED(hr)) {
2884 return hr;
2885 }
2886 }
2887 ASSERT(m_pAllocator != NULL);
2888 *ppAllocator = m_pAllocator;
2889 m_pAllocator->AddRef();
2890 return NOERROR;
2891}
2892
2893
2894/* Tell the input pin which allocator the output pin is actually going to use
2895 Override this if you care - NOTE the locking we do both here and also in
2896 GetAllocator is unnecessary but derived classes that do something useful
2897 will undoubtedly have to lock the object so this might help remind people */
2898
2899STDMETHODIMP
2900CBaseInputPin::NotifyAllocator(
2901 IMemAllocator * pAllocator,
2902 BOOL bReadOnly)
2903{
2904 CheckPointer(pAllocator,E_POINTER);
2905 ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
2906 CAutoLock cObjectLock(m_pLock);
2907
2908 IMemAllocator *pOldAllocator = m_pAllocator;
2909 pAllocator->AddRef();
2910 m_pAllocator = pAllocator;
2911
2912 if (pOldAllocator != NULL) {
2913 pOldAllocator->Release();
2914 }
2915
2916 // the readonly flag indicates whether samples from this allocator should
2917 // be regarded as readonly - if true, then inplace transforms will not be
2918 // allowed.
2919 m_bReadOnly = (BYTE)bReadOnly;
2920 return NOERROR;
2921}
2922
2923
2924HRESULT
2925CBaseInputPin::BreakConnect()
2926{
2927 /* We don't need our allocator any more */
2928 if (m_pAllocator) {
2929 // Always decommit the allocator because a downstream filter may or
2930 // may not decommit the connection's allocator. A memory leak could
2931 // occur if the allocator is not decommited when a pin is disconnected.
2932 HRESULT hr = m_pAllocator->Decommit();
2933 if( FAILED( hr ) ) {
2934 return hr;
2935 }
2936
2937 m_pAllocator->Release();
2938 m_pAllocator = NULL;
2939 }
2940
2941 return S_OK;
2942}
2943
2944
2945/* Do something with this media sample - this base class checks to see if the
2946 format has changed with this media sample and if so checks that the filter
2947 will accept it, generating a run time error if not. Once we have raised a
2948 run time error we set a flag so that no more samples will be accepted
2949
2950 It is important that any filter should override this method and implement
2951 synchronization so that samples are not processed when the pin is
2952 disconnected etc
2953*/
2954
2955STDMETHODIMP
2956CBaseInputPin::Receive(IMediaSample *pSample)
2957{
2958 CheckPointer(pSample,E_POINTER);
2959 ValidateReadPtr(pSample,sizeof(IMediaSample));
2960 ASSERT(pSample);
2961
2962 HRESULT hr = CheckStreaming();
2963 if (S_OK != hr) {
2964 return hr;
2965 }
2966
2967#ifdef DXMPERF
2968 PERFLOG_RECEIVE( m_pName ? m_pName : L"CBaseInputPin", (IPin *) m_Connected, (IPin *) this, pSample, &m_mt );
2969#endif // DXMPERF
2970
2971
2972 /* Check for IMediaSample2 */
2973 IMediaSample2 *pSample2;
2974 if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {
2975 hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);
2976 pSample2->Release();
2977 if (FAILED(hr)) {
2978 return hr;
2979 }
2980 } else {
2981 /* Get the properties the hard way */
2982 m_SampleProps.cbData = sizeof(m_SampleProps);
2983 m_SampleProps.dwTypeSpecificFlags = 0;
2984 m_SampleProps.dwStreamId = AM_STREAM_MEDIA;
2985 m_SampleProps.dwSampleFlags = 0;
2986 if (S_OK == pSample->IsDiscontinuity()) {
2987 m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
2988 }
2989 if (S_OK == pSample->IsPreroll()) {
2990 m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;
2991 }
2992 if (S_OK == pSample->IsSyncPoint()) {
2993 m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
2994 }
2995 if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,
2996 &m_SampleProps.tStop))) {
2997 m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |
2998 AM_SAMPLE_STOPVALID;
2999 }
3000 if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {
3001 m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
3002 }
3003 pSample->GetPointer(&m_SampleProps.pbBuffer);
3004 m_SampleProps.lActual = pSample->GetActualDataLength();
3005 m_SampleProps.cbBuffer = pSample->GetSize();
3006 }
3007
3008 /* Has the format changed in this sample */
3009
3010 if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {
3011 return NOERROR;
3012 }
3013
3014 /* Check the derived class accepts this format */
3015 /* This shouldn't fail as the source must call QueryAccept first */
3016
3017 hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);
3018
3019 if (hr == NOERROR) {
3020 return NOERROR;
3021 }
3022
3023 /* Raise a runtime error if we fail the media type */
3024
3025 m_bRunTimeError = TRUE;
3026 EndOfStream();
3027 m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);
3028 return VFW_E_INVALIDMEDIATYPE;
3029}
3030
3031
3032/* Receive multiple samples */
3033STDMETHODIMP
3034CBaseInputPin::ReceiveMultiple (
3035 __in_ecount(nSamples) IMediaSample **pSamples,
3036 long nSamples,
3037 __out long *nSamplesProcessed)
3038{
3039 CheckPointer(pSamples,E_POINTER);
3040 ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));
3041
3042 HRESULT hr = S_OK;
3043 *nSamplesProcessed = 0;
3044 while (nSamples-- > 0) {
3045 hr = Receive(pSamples[*nSamplesProcessed]);
3046
3047 /* S_FALSE means don't send any more */
3048 if (hr != S_OK) {
3049 break;
3050 }
3051 (*nSamplesProcessed)++;
3052 }
3053 return hr;
3054}
3055
3056/* See if Receive() might block */
3057STDMETHODIMP
3058CBaseInputPin::ReceiveCanBlock()
3059{
3060 /* Ask all the output pins if they block
3061 If there are no output pin assume we do block
3062 */
3063 int cPins = m_pFilter->GetPinCount();
3064 int cOutputPins = 0;
3065 for (int c = 0; c < cPins; c++) {
3066 CBasePin *pPin = m_pFilter->GetPin(c);
3067 if (NULL == pPin) {
3068 break;
3069 }
3070 PIN_DIRECTION pd;
3071 HRESULT hr = pPin->QueryDirection(&pd);
3072 if (FAILED(hr)) {
3073 return hr;
3074 }
3075
3076 if (pd == PINDIR_OUTPUT) {
3077
3078 IPin *pConnected;
3079 hr = pPin->ConnectedTo(&pConnected);
3080 if (SUCCEEDED(hr)) {
3081 ASSERT(pConnected != NULL);
3082 cOutputPins++;
3083 IMemInputPin *pInputPin;
3084 hr = pConnected->QueryInterface(
3085 IID_IMemInputPin,
3086 (void **)&pInputPin);
3087 pConnected->Release();
3088 if (SUCCEEDED(hr)) {
3089 hr = pInputPin->ReceiveCanBlock();
3090 pInputPin->Release();
3091 if (hr != S_FALSE) {
3092 return S_OK;
3093 }
3094 } else {
3095 /* There's a transport we don't understand here */
3096 return S_OK;
3097 }
3098 }
3099 }
3100 }
3101 return cOutputPins == 0 ? S_OK : S_FALSE;
3102}
3103
3104// Default handling for BeginFlush - call at the beginning
3105// of your implementation (makes sure that all Receive calls
3106// fail). After calling this, you need to free any queued data
3107// and then call downstream.
3108STDMETHODIMP
3109CBaseInputPin::BeginFlush(void)
3110{
3111 // BeginFlush is NOT synchronized with streaming but is part of
3112 // a control action - hence we synchronize with the filter
3113 CAutoLock lck(m_pLock);
3114
3115 // if we are already in mid-flush, this is probably a mistake
3116 // though not harmful - try to pick it up for now so I can think about it
3117 ASSERT(!m_bFlushing);
3118
3119 // first thing to do is ensure that no further Receive calls succeed
3120 m_bFlushing = TRUE;
3121
3122 // now discard any data and call downstream - must do that
3123 // in derived classes
3124 return S_OK;
3125}
3126
3127// default handling for EndFlush - call at end of your implementation
3128// - before calling this, ensure that there is no queued data and no thread
3129// pushing any more without a further receive, then call downstream,
3130// then call this method to clear the m_bFlushing flag and re-enable
3131// receives
3132STDMETHODIMP
3133CBaseInputPin::EndFlush(void)
3134{
3135 // Endlush is NOT synchronized with streaming but is part of
3136 // a control action - hence we synchronize with the filter
3137 CAutoLock lck(m_pLock);
3138
3139 // almost certainly a mistake if we are not in mid-flush
3140 ASSERT(m_bFlushing);
3141
3142 // before calling, sync with pushing thread and ensure
3143 // no more data is going downstream, then call EndFlush on
3144 // downstream pins.
3145
3146 // now re-enable Receives
3147 m_bFlushing = FALSE;
3148
3149 // No more errors
3150 m_bRunTimeError = FALSE;
3151
3152 return S_OK;
3153}
3154
3155
3156STDMETHODIMP
3157CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)
3158{
3159 UNREFERENCED_PARAMETER(q);
3160 CheckPointer(pSender,E_POINTER);
3161 ValidateReadPtr(pSender,sizeof(IBaseFilter));
3162 DbgBreak("IQuality::Notify called on an input pin");
3163 return NOERROR;
3164} // Notify
3165
3166/* Free up or unprepare allocator's memory, this is called through
3167 IMediaFilter which is responsible for locking the object first */
3168
3169HRESULT
3170CBaseInputPin::Inactive(void)
3171{
3172 m_bRunTimeError = FALSE;
3173 if (m_pAllocator == NULL) {
3174 return VFW_E_NO_ALLOCATOR;
3175 }
3176
3177 m_bFlushing = FALSE;
3178
3179 return m_pAllocator->Decommit();
3180}
3181
3182// what requirements do we have of the allocator - override if you want
3183// to support other people's allocators but need a specific alignment
3184// or prefix.
3185STDMETHODIMP
3186CBaseInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps)
3187{
3188 UNREFERENCED_PARAMETER(pProps);
3189 return E_NOTIMPL;
3190}
3191
3192// Check if it's OK to process data
3193//
3194HRESULT
3195CBaseInputPin::CheckStreaming()
3196{
3197 // Shouldn't be able to get any data if we're not connected!
3198 ASSERT(IsConnected());
3199
3200 // Don't process stuff in Stopped state
3201 if (IsStopped()) {
3202 return VFW_E_WRONG_STATE;
3203 }
3204 if (m_bFlushing) {
3205 return S_FALSE;
3206 }
3207 if (m_bRunTimeError) {
3208 return VFW_E_RUNTIME_ERROR;
3209 }
3210 return S_OK;
3211}
3212
3213// Pass on the Quality notification q to
3214// a. Our QualityControl sink (if we have one) or else
3215// b. to our upstream filter
3216// and if that doesn't work, throw it away with a bad return code
3217HRESULT
3218CBaseInputPin::PassNotify(Quality& q)
3219{
3220 // We pass the message on, which means that we find the quality sink
3221 // for our input pin and send it there
3222
3223 DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));
3224 if (m_pQSink!=NULL) {
3225 return m_pQSink->Notify(m_pFilter, q);
3226 } else {
3227 // no sink set, so pass it upstream
3228 HRESULT hr;
3229 IQualityControl * pIQC;
3230
3231 hr = VFW_E_NOT_FOUND; // default
3232 if (m_Connected) {
3233 m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
3234
3235 if (pIQC!=NULL) {
3236 hr = pIQC->Notify(m_pFilter, q);
3237 pIQC->Release();
3238 }
3239 }
3240 return hr;
3241 }
3242
3243} // PassNotify
3244
3245//=====================================================================
3246//=====================================================================
3247// Memory allocation class, implements CMediaSample
3248//=====================================================================
3249//=====================================================================
3250
3251
3252/* NOTE The implementation of this class calls the CUnknown constructor with
3253 a NULL outer unknown pointer. This has the effect of making us a self
3254 contained class, ie any QueryInterface, AddRef or Release calls will be
3255 routed to the class's NonDelegatingUnknown methods. You will typically
3256 find that the classes that do this then override one or more of these
3257 virtual functions to provide more specialised behaviour. A good example
3258 of this is where a class wants to keep the QueryInterface internal but
3259 still wants it's lifetime controlled by the external object */
3260
3261/* The last two parameters have default values of NULL and zero */
3262
3263CMediaSample::CMediaSample(__in_opt LPCTSTR pName,
3264 __in_opt CBaseAllocator *pAllocator,
3265 __inout_opt HRESULT *phr,
3266 __in_bcount_opt(length) LPBYTE pBuffer,
3267 LONG length) :
3268 m_pBuffer(pBuffer), // Initialise the buffer
3269 m_cbBuffer(length), // And it's length
3270 m_lActual(length), // By default, actual = length
3271 m_pMediaType(NULL), // No media type change
3272 m_dwFlags(0), // Nothing set
3273 m_cRef(0), // 0 ref count
3274 m_dwTypeSpecificFlags(0), // Type specific flags
3275 m_dwStreamId(AM_STREAM_MEDIA), // Stream id
3276 m_pAllocator(pAllocator) // Allocator
3277{
3278#ifdef DXMPERF
3279 PERFLOG_CTOR( pName ? pName : L"CMediaSample", (IMediaSample *) this );
3280#endif // DXMPERF
3281
3282 /* We must have an owner and it must also be derived from class
3283 CBaseAllocator BUT we do not hold a reference count on it */
3284
3285 ASSERT(pAllocator);
3286
3287 if (length < 0) {
3288 *phr = VFW_E_BUFFER_OVERFLOW;
3289 m_cbBuffer = 0;
3290 }
3291}
3292
3293#ifdef UNICODE
3294CMediaSample::CMediaSample(__in_opt LPCSTR pName,
3295 __in_opt CBaseAllocator *pAllocator,
3296 __inout_opt HRESULT *phr,
3297 __in_bcount_opt(length) LPBYTE pBuffer,
3298 LONG length) :
3299 m_pBuffer(pBuffer), // Initialise the buffer
3300 m_cbBuffer(length), // And it's length
3301 m_lActual(length), // By default, actual = length
3302 m_pMediaType(NULL), // No media type change
3303 m_dwFlags(0), // Nothing set
3304 m_cRef(0), // 0 ref count
3305 m_dwTypeSpecificFlags(0), // Type specific flags
3306 m_dwStreamId(AM_STREAM_MEDIA), // Stream id
3307 m_pAllocator(pAllocator) // Allocator
3308{
3309#ifdef DXMPERF
3310 PERFLOG_CTOR( L"CMediaSample", (IMediaSample *) this );
3311#endif // DXMPERF
3312
3313 /* We must have an owner and it must also be derived from class
3314 CBaseAllocator BUT we do not hold a reference count on it */
3315
3316 ASSERT(pAllocator);
3317}
3318#endif
3319
3320/* Destructor deletes the media type memory */
3321
3322CMediaSample::~CMediaSample()
3323{
3324#ifdef DXMPERF
3325 PERFLOG_DTOR( L"CMediaSample", (IMediaSample *) this );
3326#endif // DXMPERF
3327
3328 if (m_pMediaType) {
3329 DeleteMediaType(m_pMediaType);
3330 }
3331}
3332
3333/* Override this to publicise our interfaces */
3334
3335STDMETHODIMP
3336CMediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)
3337{
3338 if (riid == IID_IMediaSample ||
3339 riid == IID_IMediaSample2 ||
3340 riid == IID_IUnknown) {
3341 return GetInterface((IMediaSample *) this, ppv);
3342 } else {
3343 *ppv = NULL;
3344 return E_NOINTERFACE;
3345 }
3346}
3347
3348STDMETHODIMP_(ULONG)
3349CMediaSample::AddRef()
3350{
3351 return InterlockedIncrement(&m_cRef);
3352}
3353
3354
3355// -- CMediaSample lifetimes --
3356//
3357// On final release of this sample buffer it is not deleted but
3358// returned to the freelist of the owning memory allocator
3359//
3360// The allocator may be waiting for the last buffer to be placed on the free
3361// list in order to decommit all the memory, so the ReleaseBuffer() call may
3362// result in this sample being deleted. We also need to hold a refcount on
3363// the allocator to stop that going away until we have finished with this.
3364// However, we cannot release the allocator before the ReleaseBuffer, as the
3365// release may cause us to be deleted. Similarly we can't do it afterwards.
3366//
3367// Thus we must leave it to the allocator to hold an addref on our behalf.
3368// When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer
3369// is called, he releases himself, possibly causing us and him to be deleted.
3370
3371
3372STDMETHODIMP_(ULONG)
3373CMediaSample::Release()
3374{
3375 /* Decrement our own private reference count */
3376 LONG lRef;
3377 if (m_cRef == 1) {
3378 lRef = 0;
3379 m_cRef = 0;
3380 } else {
3381 lRef = InterlockedDecrement(&m_cRef);
3382 }
3383 ASSERT(lRef >= 0);
3384
3385 DbgLog((LOG_MEMORY,3,TEXT(" Unknown %X ref-- = %d"),
3386 this, m_cRef));
3387
3388 /* Did we release our final reference count */
3389 if (lRef == 0) {
3390 /* Free all resources */
3391 if (m_dwFlags & Sample_TypeChanged) {
3392 SetMediaType(NULL);
3393 }
3394 ASSERT(m_pMediaType == NULL);
3395 m_dwFlags = 0;
3396 m_dwTypeSpecificFlags = 0;
3397 m_dwStreamId = AM_STREAM_MEDIA;
3398
3399 /* This may cause us to be deleted */
3400 // Our refcount is reliably 0 thus no-one will mess with us
3401 m_pAllocator->ReleaseBuffer(this);
3402 }
3403 return (ULONG)lRef;
3404}
3405
3406
3407// set the buffer pointer and length. Used by allocators that
3408// want variable sized pointers or pointers into already-read data.
3409// This is only available through a CMediaSample* not an IMediaSample*
3410// and so cannot be changed by clients.
3411HRESULT
3412CMediaSample::SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes)
3413{
3414 if (cBytes < 0) {
3415 return VFW_E_BUFFER_OVERFLOW;
3416 }
3417 m_pBuffer = ptr; // new buffer area (could be null)
3418 m_cbBuffer = cBytes; // length of buffer
3419 m_lActual = cBytes; // length of data in buffer (assume full)
3420
3421 return S_OK;
3422}
3423
3424
3425// get me a read/write pointer to this buffer's memory. I will actually
3426// want to use sizeUsed bytes.
3427STDMETHODIMP
3428CMediaSample::GetPointer(__deref_out BYTE ** ppBuffer)
3429{
3430 ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));
3431
3432 // creator must have set pointer either during
3433 // constructor or by SetPointer
3434 ASSERT(m_pBuffer);
3435
3436 *ppBuffer = m_pBuffer;
3437 return NOERROR;
3438}
3439
3440
3441// return the size in bytes of this buffer
3442STDMETHODIMP_(LONG)
3443CMediaSample::GetSize(void)
3444{
3445 return m_cbBuffer;
3446}
3447
3448
3449// get the stream time at which this sample should start and finish.
3450STDMETHODIMP
3451CMediaSample::GetTime(
3452 __out REFERENCE_TIME * pTimeStart, // put time here
3453 __out REFERENCE_TIME * pTimeEnd
3454)
3455{
3456 ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));
3457 ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));
3458
3459 if (!(m_dwFlags & Sample_StopValid)) {
3460 if (!(m_dwFlags & Sample_TimeValid)) {
3461 return VFW_E_SAMPLE_TIME_NOT_SET;
3462 } else {
3463 *pTimeStart = m_Start;
3464
3465 // Make sure old stuff works
3466 *pTimeEnd = m_Start + 1;
3467 return VFW_S_NO_STOP_TIME;
3468 }
3469 }
3470
3471 *pTimeStart = m_Start;
3472 *pTimeEnd = m_End;
3473 return NOERROR;
3474}
3475
3476
3477// Set the stream time at which this sample should start and finish.
3478// NULL pointers means the time is reset
3479STDMETHODIMP
3480CMediaSample::SetTime(
3481 __in_opt REFERENCE_TIME * pTimeStart,
3482 __in_opt REFERENCE_TIME * pTimeEnd
3483)
3484{
3485 if (pTimeStart == NULL) {
3486 ASSERT(pTimeEnd == NULL);
3487 m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);
3488 } else {
3489 if (pTimeEnd == NULL) {
3490 m_Start = *pTimeStart;
3491 m_dwFlags |= Sample_TimeValid;
3492 m_dwFlags &= ~Sample_StopValid;
3493 } else {
3494 ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));
3495 ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));
3496 ASSERT(*pTimeEnd >= *pTimeStart);
3497
3498 m_Start = *pTimeStart;
3499 m_End = *pTimeEnd;
3500 m_dwFlags |= Sample_TimeValid | Sample_StopValid;
3501 }
3502 }
3503 return NOERROR;
3504}
3505
3506
3507// get the media times (eg bytes) for this sample
3508STDMETHODIMP
3509CMediaSample::GetMediaTime(
3510 __out LONGLONG * pTimeStart,
3511 __out LONGLONG * pTimeEnd
3512)
3513{
3514 ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));
3515 ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));
3516
3517 if (!(m_dwFlags & Sample_MediaTimeValid)) {
3518 return VFW_E_MEDIA_TIME_NOT_SET;
3519 }
3520
3521 *pTimeStart = m_MediaStart;
3522 *pTimeEnd = (m_MediaStart + m_MediaEnd);
3523 return NOERROR;
3524}
3525
3526
3527// Set the media times for this sample
3528STDMETHODIMP
3529CMediaSample::SetMediaTime(
3530 __in_opt LONGLONG * pTimeStart,
3531 __in_opt LONGLONG * pTimeEnd
3532)
3533{
3534 if (pTimeStart == NULL) {
3535 ASSERT(pTimeEnd == NULL);
3536 m_dwFlags &= ~Sample_MediaTimeValid;
3537 } else {
3538 if (NULL == pTimeEnd) {
3539 return E_POINTER;
3540 }
3541 ValidateReadPtr(pTimeStart,sizeof(LONGLONG));
3542 ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));
3543 ASSERT(*pTimeEnd >= *pTimeStart);
3544
3545 m_MediaStart = *pTimeStart;
3546 m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);
3547 m_dwFlags |= Sample_MediaTimeValid;
3548 }
3549 return NOERROR;
3550}
3551
3552
3553STDMETHODIMP
3554CMediaSample::IsSyncPoint(void)
3555{
3556 if (m_dwFlags & Sample_SyncPoint) {
3557 return S_OK;
3558 } else {
3559 return S_FALSE;
3560 }
3561}
3562
3563
3564STDMETHODIMP
3565CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)
3566{
3567 if (bIsSyncPoint) {
3568 m_dwFlags |= Sample_SyncPoint;
3569 } else {
3570 m_dwFlags &= ~Sample_SyncPoint;
3571 }
3572 return NOERROR;
3573}
3574
3575// returns S_OK if there is a discontinuity in the data (this same is
3576// not a continuation of the previous stream of data
3577// - there has been a seek).
3578STDMETHODIMP
3579CMediaSample::IsDiscontinuity(void)
3580{
3581 if (m_dwFlags & Sample_Discontinuity) {
3582 return S_OK;
3583 } else {
3584 return S_FALSE;
3585 }
3586}
3587
3588// set the discontinuity property - TRUE if this sample is not a
3589// continuation, but a new sample after a seek.
3590STDMETHODIMP
3591CMediaSample::SetDiscontinuity(BOOL bDiscont)
3592{
3593 // should be TRUE or FALSE
3594 if (bDiscont) {
3595 m_dwFlags |= Sample_Discontinuity;
3596 } else {
3597 m_dwFlags &= ~Sample_Discontinuity;
3598 }
3599 return S_OK;
3600}
3601
3602STDMETHODIMP
3603CMediaSample::IsPreroll(void)
3604{
3605 if (m_dwFlags & Sample_Preroll) {
3606 return S_OK;
3607 } else {
3608 return S_FALSE;
3609 }
3610}
3611
3612
3613STDMETHODIMP
3614CMediaSample::SetPreroll(BOOL bIsPreroll)
3615{
3616 if (bIsPreroll) {
3617 m_dwFlags |= Sample_Preroll;
3618 } else {
3619 m_dwFlags &= ~Sample_Preroll;
3620 }
3621 return NOERROR;
3622}
3623
3624STDMETHODIMP_(LONG)
3625CMediaSample::GetActualDataLength(void)
3626{
3627 return m_lActual;
3628}
3629
3630
3631STDMETHODIMP
3632CMediaSample::SetActualDataLength(LONG lActual)
3633{
3634 if (lActual > m_cbBuffer || lActual < 0) {
3635 ASSERT(lActual <= GetSize());
3636 return VFW_E_BUFFER_OVERFLOW;
3637 }
3638 m_lActual = lActual;
3639 return NOERROR;
3640}
3641
3642
3643/* These allow for limited format changes in band */
3644
3645STDMETHODIMP
3646CMediaSample::GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType)
3647{
3648 ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));
3649 ASSERT(ppMediaType);
3650
3651 /* Do we have a new media type for them */
3652
3653 if (!(m_dwFlags & Sample_TypeChanged)) {
3654 ASSERT(m_pMediaType == NULL);
3655 *ppMediaType = NULL;
3656 return S_FALSE;
3657 }
3658
3659 ASSERT(m_pMediaType);
3660
3661 /* Create a copy of our media type */
3662
3663 *ppMediaType = CreateMediaType(m_pMediaType);
3664 if (*ppMediaType == NULL) {
3665 return E_OUTOFMEMORY;
3666 }
3667 return NOERROR;
3668}
3669
3670
3671/* Mark this sample as having a different format type */
3672
3673STDMETHODIMP
3674CMediaSample::SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType)
3675{
3676 /* Delete the current media type */
3677
3678 if (m_pMediaType) {
3679 DeleteMediaType(m_pMediaType);
3680 m_pMediaType = NULL;
3681 }
3682
3683 /* Mechanism for resetting the format type */
3684
3685 if (pMediaType == NULL) {
3686 m_dwFlags &= ~Sample_TypeChanged;
3687 return NOERROR;
3688 }
3689
3690 ASSERT(pMediaType);
3691 ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));
3692
3693 /* Take a copy of the media type */
3694
3695 m_pMediaType = CreateMediaType(pMediaType);
3696 if (m_pMediaType == NULL) {
3697 m_dwFlags &= ~Sample_TypeChanged;
3698 return E_OUTOFMEMORY;
3699 }
3700
3701 m_dwFlags |= Sample_TypeChanged;
3702 return NOERROR;
3703}
3704
3705// Set and get properties (IMediaSample2)
3706STDMETHODIMP CMediaSample::GetProperties(
3707 DWORD cbProperties,
3708 __out_bcount(cbProperties) BYTE * pbProperties
3709)
3710{
3711 if (0 != cbProperties) {
3712 CheckPointer(pbProperties, E_POINTER);
3713 // Return generic stuff up to the length
3714 AM_SAMPLE2_PROPERTIES Props;
3715 Props.cbData = min(cbProperties, sizeof(Props));
3716 Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;
3717 Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;
3718 Props.pbBuffer = m_pBuffer;
3719 Props.cbBuffer = m_cbBuffer;
3720 Props.lActual = m_lActual;
3721 Props.tStart = m_Start;
3722 Props.tStop = m_End;
3723 Props.dwStreamId = m_dwStreamId;
3724 if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {
3725 Props.pMediaType = m_pMediaType;
3726 } else {
3727 Props.pMediaType = NULL;
3728 }
3729 CopyMemory(pbProperties, &Props, Props.cbData);
3730 }
3731 return S_OK;
3732}
3733
3734#define CONTAINS_FIELD(type, field, offset) \
3735 ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)
3736
3737HRESULT CMediaSample::SetProperties(
3738 DWORD cbProperties,
3739 __in_bcount(cbProperties) const BYTE * pbProperties
3740)
3741{
3742
3743 /* Generic properties */
3744 AM_MEDIA_TYPE *pMediaType = NULL;
3745
3746 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {
3747 CheckPointer(pbProperties, E_POINTER);
3748 AM_SAMPLE2_PROPERTIES *pProps =
3749 (AM_SAMPLE2_PROPERTIES *)pbProperties;
3750
3751 /* Don't use more data than is actually there */
3752 if (pProps->cbData < cbProperties) {
3753 cbProperties = pProps->cbData;
3754 }
3755 /* We only handle IMediaSample2 */
3756 if (cbProperties > sizeof(*pProps) ||
3757 pProps->cbData > sizeof(*pProps)) {
3758 return E_INVALIDARG;
3759 }
3760 /* Do checks first, the assignments (for backout) */
3761 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
3762 /* Check the flags */
3763 if (pProps->dwSampleFlags &
3764 (~Sample_ValidFlags | Sample_MediaTimeValid)) {
3765 return E_INVALIDARG;
3766 }
3767 /* Check a flag isn't being set for a property
3768 not being provided
3769 */
3770 if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&
3771 !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&
3772 !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
3773 return E_INVALIDARG;
3774 }
3775 }
3776 /* NB - can't SET the pointer or size */
3777 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {
3778
3779 /* Check pbBuffer */
3780 if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {
3781 return E_INVALIDARG;
3782 }
3783 }
3784 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {
3785
3786 /* Check cbBuffer */
3787 if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {
3788 return E_INVALIDARG;
3789 }
3790 }
3791 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&
3792 CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
3793
3794 /* Check lActual */
3795 if (pProps->cbBuffer < pProps->lActual) {
3796 return E_INVALIDARG;
3797 }
3798 }
3799
3800 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
3801
3802 /* Check pMediaType */
3803 if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
3804 CheckPointer(pProps->pMediaType, E_POINTER);
3805 pMediaType = CreateMediaType(pProps->pMediaType);
3806 if (pMediaType == NULL) {
3807 return E_OUTOFMEMORY;
3808 }
3809 }
3810 }
3811
3812 /* Now do the assignments */
3813 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {
3814 m_dwStreamId = pProps->dwStreamId;
3815 }
3816 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
3817 /* Set the flags */
3818 m_dwFlags = pProps->dwSampleFlags |
3819 (m_dwFlags & Sample_MediaTimeValid);
3820 m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
3821 } else {
3822 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {
3823 m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
3824 }
3825 }
3826
3827 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
3828 /* Set lActual */
3829 m_lActual = pProps->lActual;
3830 }
3831
3832 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
3833
3834 /* Set the times */
3835 m_End = pProps->tStop;
3836 }
3837 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {
3838
3839 /* Set the times */
3840 m_Start = pProps->tStart;
3841 }
3842
3843 if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
3844 /* Set pMediaType */
3845 if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
3846 if (m_pMediaType != NULL) {
3847 DeleteMediaType(m_pMediaType);
3848 }
3849 m_pMediaType = pMediaType;
3850 }
3851 }
3852
3853 /* Fix up the type changed flag to correctly reflect the current state
3854 If, for instance the input contained no type change but the
3855 output does then if we don't do this we'd lose the
3856 output media type.
3857 */
3858 if (m_pMediaType) {
3859 m_dwFlags |= Sample_TypeChanged;
3860 } else {
3861 m_dwFlags &= ~Sample_TypeChanged;
3862 }
3863 }
3864
3865 return S_OK;
3866}
3867
3868
3869//
3870// The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),
3871// IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the
3872// connected input pin. The application thread calls Block(). The
3873// following class members can only be called by the streaming thread.
3874//
3875// Deliver()
3876// DeliverNewSegment()
3877// StartUsingOutputPin()
3878// StopUsingOutputPin()
3879// ChangeOutputFormat()
3880// ChangeMediaType()
3881// DynamicReconnect()
3882//
3883// The following class members can only be called by the application thread.
3884//
3885// Block()
3886// SynchronousBlockOutputPin()
3887// AsynchronousBlockOutputPin()
3888//
3889
3890CDynamicOutputPin::CDynamicOutputPin(
3891 __in_opt LPCTSTR pObjectName,
3892 __in CBaseFilter *pFilter,
3893 __in CCritSec *pLock,
3894 __inout HRESULT *phr,
3895 __in_opt LPCWSTR pName) :
3896 CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
3897 m_hStopEvent(NULL),
3898 m_pGraphConfig(NULL),
3899 m_bPinUsesReadOnlyAllocator(FALSE),
3900 m_BlockState(NOT_BLOCKED),
3901 m_hUnblockOutputPinEvent(NULL),
3902 m_hNotifyCallerPinBlockedEvent(NULL),
3903 m_dwBlockCallerThreadID(0),
3904 m_dwNumOutstandingOutputPinUsers(0)
3905{
3906 HRESULT hr = Initialize();
3907 if( FAILED( hr ) ) {
3908 *phr = hr;
3909 return;
3910 }
3911}
3912
3913#ifdef UNICODE
3914CDynamicOutputPin::CDynamicOutputPin(
3915 __in_opt LPCSTR pObjectName,
3916 __in CBaseFilter *pFilter,
3917 __in CCritSec *pLock,
3918 __inout HRESULT *phr,
3919 __in_opt LPCWSTR pName) :
3920 CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
3921 m_hStopEvent(NULL),
3922 m_pGraphConfig(NULL),
3923 m_bPinUsesReadOnlyAllocator(FALSE),
3924 m_BlockState(NOT_BLOCKED),
3925 m_hUnblockOutputPinEvent(NULL),
3926 m_hNotifyCallerPinBlockedEvent(NULL),
3927 m_dwBlockCallerThreadID(0),
3928 m_dwNumOutstandingOutputPinUsers(0)
3929{
3930 HRESULT hr = Initialize();
3931 if( FAILED( hr ) ) {
3932 *phr = hr;
3933 return;
3934 }
3935}
3936#endif
3937
3938CDynamicOutputPin::~CDynamicOutputPin()
3939{
3940 if(NULL != m_hUnblockOutputPinEvent) {
3941 // This call should not fail because we have access to m_hUnblockOutputPinEvent
3942 // and m_hUnblockOutputPinEvent is a valid event.
3943 EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));
3944 }
3945
3946 if(NULL != m_hNotifyCallerPinBlockedEvent) {
3947 // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent
3948 // and m_hNotifyCallerPinBlockedEvent is a valid event.
3949 EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
3950 }
3951}
3952
3953HRESULT CDynamicOutputPin::Initialize(void)
3954{
3955 m_hUnblockOutputPinEvent = ::CreateEvent( NULL, // The event will have the default security descriptor.
3956 TRUE, // This is a manual reset event.
3957 TRUE, // The event is initially signaled.
3958 NULL ); // The event is not named.
3959
3960 // CreateEvent() returns NULL if an error occurs.
3961 if(NULL == m_hUnblockOutputPinEvent) {
3962 return AmGetLastErrorToHResult();
3963 }
3964
3965 // Set flag to say we can reconnect while streaming.
3966 SetReconnectWhenActive(true);
3967
3968 return S_OK;
3969}
3970
3971STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
3972{
3973 if(riid == IID_IPinFlowControl) {
3974 return GetInterface(static_cast<IPinFlowControl*>(this), ppv);
3975 } else {
3976 return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
3977 }
3978}
3979
3980STDMETHODIMP CDynamicOutputPin::Disconnect(void)
3981{
3982 CAutoLock cObjectLock(m_pLock);
3983 return DisconnectInternal();
3984}
3985
3986STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
3987{
3988 const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;
3989
3990 // Check for illegal flags.
3991 if(dwBlockFlags & ~VALID_FLAGS) {
3992 return E_INVALIDARG;
3993 }
3994
3995 // Make sure the event is unsignaled.
3996 if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {
3997 if( !::ResetEvent( hEvent ) ) {
3998 return AmGetLastErrorToHResult();
3999 }
4000 }
4001
4002 // No flags are set if we are unblocking the output pin.
4003 if(0 == dwBlockFlags) {
4004
4005 // This parameter should be NULL because unblock operations are always synchronous.
4006 // There is no need to notify the caller when the event is done.
4007 if(NULL != hEvent) {
4008 return E_INVALIDARG;
4009 }
4010 }
4011
4012 #ifdef DEBUG
4013 AssertValid();
4014 #endif // DEBUG
4015
4016 HRESULT hr;
4017
4018 if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {
4019 // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.
4020 // If hEvent is not NULL, the block is asynchronous.
4021 if(NULL == hEvent) {
4022 hr = SynchronousBlockOutputPin();
4023 } else {
4024 hr = AsynchronousBlockOutputPin(hEvent);
4025 }
4026 } else {
4027 hr = UnblockOutputPin();
4028 }
4029
4030 #ifdef DEBUG
4031 AssertValid();
4032 #endif // DEBUG
4033
4034 if(FAILED(hr)) {
4035 return hr;
4036 }
4037
4038 return S_OK;
4039}
4040
4041HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)
4042{
4043 HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL, // The event will have the default security attributes.
4044 FALSE, // This is an automatic reset event.
4045 FALSE, // The event is initially unsignaled.
4046 NULL ); // The event is not named.
4047
4048 // CreateEvent() returns NULL if an error occurs.
4049 if(NULL == hNotifyCallerPinBlockedEvent) {
4050 return AmGetLastErrorToHResult();
4051 }
4052
4053 HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);
4054 if(FAILED(hr)) {
4055 // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
4056 // and hNotifyCallerPinBlockedEvent is a valid event.
4057 EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
4058
4059 return hr;
4060 }
4061
4062 hr = WaitEvent(hNotifyCallerPinBlockedEvent);
4063
4064 // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
4065 // and hNotifyCallerPinBlockedEvent is a valid event.
4066 EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
4067
4068 if(FAILED(hr)) {
4069 return hr;
4070 }
4071
4072 return S_OK;
4073}
4074
4075HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)
4076{
4077 // This function holds the m_BlockStateLock because it uses
4078 // m_dwBlockCallerThreadID, m_BlockState and
4079 // m_hNotifyCallerPinBlockedEvent.
4080 CAutoLock alBlockStateLock(&m_BlockStateLock);
4081
4082 if(NOT_BLOCKED != m_BlockState) {
4083 if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {
4084 return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;
4085 } else {
4086 return VFW_E_PIN_ALREADY_BLOCKED;
4087 }
4088 }
4089
4090 BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),
4091 hNotifyCallerPinBlockedEvent,
4092 ::GetCurrentProcess(),
4093 &m_hNotifyCallerPinBlockedEvent,
4094 EVENT_MODIFY_STATE,
4095 FALSE,
4096 0 );
4097 if( !fSuccess ) {
4098 return AmGetLastErrorToHResult();
4099 }
4100
4101 m_BlockState = PENDING;
4102 m_dwBlockCallerThreadID = ::GetCurrentThreadId();
4103
4104 // The output pin cannot be blocked if the streaming thread is
4105 // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()
4106 // or IMemInputPin::ReceiveMultiple() on the connected input pin. Also, it
4107 // cannot be blocked if the streaming thread is calling DynamicReconnect(),
4108 // ChangeMediaType() or ChangeOutputFormat().
4109 if(!StreamingThreadUsingOutputPin()) {
4110
4111 // The output pin can be immediately blocked.
4112 BlockOutputPin();
4113 }
4114
4115 return S_OK;
4116}
4117
4118void CDynamicOutputPin::BlockOutputPin(void)
4119{
4120 // The caller should always hold the m_BlockStateLock because this function
4121 // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.
4122 ASSERT(CritCheckIn(&m_BlockStateLock));
4123
4124 // This function should not be called if the streaming thread is modifying
4125 // the connection state or it's passing data downstream.
4126 ASSERT(!StreamingThreadUsingOutputPin());
4127
4128 // This should not fail because we successfully created the event
4129 // and we have the security permissions to change it's state.
4130 EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));
4131
4132 // This event should not fail because AsynchronousBlockOutputPin() successfully
4133 // duplicated this handle and we have the appropriate security permissions.
4134 EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
4135 EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
4136
4137 m_BlockState = BLOCKED;
4138 m_hNotifyCallerPinBlockedEvent = NULL;
4139}
4140
4141HRESULT CDynamicOutputPin::UnblockOutputPin(void)
4142{
4143 // UnblockOutputPin() holds the m_BlockStateLock because it
4144 // uses m_BlockState, m_dwBlockCallerThreadID and
4145 // m_hNotifyCallerPinBlockedEvent.
4146 CAutoLock alBlockStateLock(&m_BlockStateLock);
4147
4148 if(NOT_BLOCKED == m_BlockState) {
4149 return S_FALSE;
4150 }
4151
4152 // This should not fail because we successfully created the event
4153 // and we have the security permissions to change it's state.
4154 EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));
4155
4156 // Cancel the block operation if it's still pending.
4157 if(NULL != m_hNotifyCallerPinBlockedEvent) {
4158 // This event should not fail because AsynchronousBlockOutputPin() successfully
4159 // duplicated this handle and we have the appropriate security permissions.
4160 EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
4161 EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
4162 }
4163
4164 m_BlockState = NOT_BLOCKED;
4165 m_dwBlockCallerThreadID = 0;
4166 m_hNotifyCallerPinBlockedEvent = NULL;
4167
4168 return S_OK;
4169}
4170
4171HRESULT CDynamicOutputPin::StartUsingOutputPin(void)
4172{
4173 // The caller should not hold m_BlockStateLock. If the caller does,
4174 // a deadlock could occur.
4175 ASSERT(CritCheckOut(&m_BlockStateLock));
4176
4177 CAutoLock alBlockStateLock(&m_BlockStateLock);
4178
4179 #ifdef DEBUG
4180 AssertValid();
4181 #endif // DEBUG
4182
4183 // Are we in the middle of a block operation?
4184 while(BLOCKED == m_BlockState) {
4185 m_BlockStateLock.Unlock();
4186
4187 // If this ASSERT fires, a deadlock could occur. The caller should make sure
4188 // that this thread never acquires the Block State lock more than once.
4189 ASSERT(CritCheckOut( &m_BlockStateLock ));
4190
4191 // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event
4192 // is fired. It returns WAIT_OBJECT_0 + 1 if the stop event if fired.
4193 // See the Windows SDK documentation for more information on
4194 // WaitForMultipleObjects().
4195 const DWORD UNBLOCK = WAIT_OBJECT_0;
4196 const DWORD STOP = WAIT_OBJECT_0 + 1;
4197
4198 HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };
4199 DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);
4200
4201 DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );
4202
4203 m_BlockStateLock.Lock();
4204
4205 #ifdef DEBUG
4206 AssertValid();
4207 #endif // DEBUG
4208
4209 switch( dwReturnValue ) {
4210 case UNBLOCK:
4211 break;
4212
4213 case STOP:
4214 return VFW_E_STATE_CHANGED;
4215
4216 case WAIT_FAILED:
4217 return AmGetLastErrorToHResult();
4218
4219 default:
4220 DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );
4221 return E_UNEXPECTED;
4222 }
4223 }
4224
4225 m_dwNumOutstandingOutputPinUsers++;
4226
4227 #ifdef DEBUG
4228 AssertValid();
4229 #endif // DEBUG
4230
4231 return S_OK;
4232}
4233
4234void CDynamicOutputPin::StopUsingOutputPin(void)
4235{
4236 CAutoLock alBlockStateLock(&m_BlockStateLock);
4237
4238 #ifdef DEBUG
4239 AssertValid();
4240 #endif // DEBUG
4241
4242 m_dwNumOutstandingOutputPinUsers--;
4243
4244 if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {
4245 BlockOutputPin();
4246 }
4247
4248 #ifdef DEBUG
4249 AssertValid();
4250 #endif // DEBUG
4251}
4252
4253bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)
4254{
4255 CAutoLock alBlockStateLock(&m_BlockStateLock);
4256
4257 return (m_dwNumOutstandingOutputPinUsers > 0);
4258}
4259
4260void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)
4261{
4262 // This pointer is not addrefed because filters are not allowed to
4263 // hold references to the filter graph manager. See the documentation for
4264 // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.
4265 m_pGraphConfig = pGraphConfig;
4266
4267 m_hStopEvent = hStopEvent;
4268}
4269
4270HRESULT CDynamicOutputPin::Active(void)
4271{
4272 // Make sure the user initialized the object by calling SetConfigInfo().
4273 if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {
4274 DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized. Call SetConfigInfo() to initialize them. );
4275 return E_FAIL;
4276 }
4277
4278 // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
4279 // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
4280 // handle is invalid if 1) the event does not exist or the user does not have the security
4281 // permissions to use the event.
4282 EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
4283
4284 return CBaseOutputPin::Active();
4285}
4286
4287HRESULT CDynamicOutputPin::Inactive(void)
4288{
4289 // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
4290 // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
4291 // handle is invalid if 1) the event does not exist or the user does not have the security
4292 // permissions to use the event.
4293 EXECUTE_ASSERT(SetEvent(m_hStopEvent));
4294
4295 return CBaseOutputPin::Inactive();
4296}
4297
4298HRESULT CDynamicOutputPin::DeliverBeginFlush(void)
4299{
4300 // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
4301 // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
4302 // An event handle is invalid if 1) the event does not exist or the user does not have the security
4303 // permissions to use the event.
4304 EXECUTE_ASSERT(SetEvent(m_hStopEvent));
4305
4306 return CBaseOutputPin::DeliverBeginFlush();
4307}
4308
4309HRESULT CDynamicOutputPin::DeliverEndFlush(void)
4310{
4311 // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
4312 // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
4313 // An event handle is invalid if 1) the event does not exist or the user does not have the security
4314 // permissions to use the event.
4315 EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
4316
4317 return CBaseOutputPin::DeliverEndFlush();
4318}
4319
4320
4321// ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly
4322// reconnects the output pin.
4323HRESULT CDynamicOutputPin::ChangeOutputFormat
4324 (
4325 const AM_MEDIA_TYPE *pmt,
4326 REFERENCE_TIME tSegmentStart,
4327 REFERENCE_TIME tSegmentStop,
4328 double dSegmentRate
4329 )
4330{
4331 // The caller should call StartUsingOutputPin() before calling this
4332 // method.
4333 ASSERT(StreamingThreadUsingOutputPin());
4334
4335 // Callers should always pass a valid media type to ChangeOutputFormat() .
4336 ASSERT(NULL != pmt);
4337
4338 CMediaType cmt(*pmt);
4339 HRESULT hr = ChangeMediaType(&cmt);
4340 if (FAILED(hr)) {
4341 return hr;
4342 }
4343
4344 hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);
4345 if( FAILED( hr ) ) {
4346 return hr;
4347 }
4348
4349 return S_OK;
4350}
4351
4352HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)
4353{
4354 // The caller should call StartUsingOutputPin() before calling this
4355 // method.
4356 ASSERT(StreamingThreadUsingOutputPin());
4357
4358 // This function assumes the filter graph is running.
4359 ASSERT(!IsStopped());
4360
4361 if(!IsConnected()) {
4362 return VFW_E_NOT_CONNECTED;
4363 }
4364
4365 /* First check if the downstream pin will accept a dynamic
4366 format change
4367 */
4368 QzCComPtr<IPinConnection> pConnection;
4369
4370 m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);
4371 if(pConnection != NULL) {
4372
4373 if(S_OK == pConnection->DynamicQueryAccept(pmt)) {
4374
4375 HRESULT hr = ChangeMediaTypeHelper(pmt);
4376 if(FAILED(hr)) {
4377 return hr;
4378 }
4379
4380 return S_OK;
4381 }
4382 }
4383
4384 /* Can't do the dynamic connection */
4385 return DynamicReconnect(pmt);
4386}
4387
4388HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)
4389{
4390 // The caller should call StartUsingOutputPin() before calling this
4391 // method.
4392 ASSERT(StreamingThreadUsingOutputPin());
4393
4394 HRESULT hr = m_Connected->ReceiveConnection(this, pmt);
4395 if(FAILED(hr)) {
4396 return hr;
4397 }
4398
4399 hr = SetMediaType(pmt);
4400 if(FAILED(hr)) {
4401 return hr;
4402 }
4403
4404 // Does this pin use the local memory transport?
4405 if(NULL != m_pInputPin) {
4406 // This function assumes that m_pInputPin and m_Connected are
4407 // two different interfaces to the same object.
4408 ASSERT(::IsEqualObject(m_Connected, m_pInputPin));
4409
4410 ALLOCATOR_PROPERTIES apInputPinRequirements;
4411 apInputPinRequirements.cbAlign = 0;
4412 apInputPinRequirements.cbBuffer = 0;
4413 apInputPinRequirements.cbPrefix = 0;
4414 apInputPinRequirements.cBuffers = 0;
4415
4416 m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);
4417
4418 // A zero allignment does not make any sense.
4419 if(0 == apInputPinRequirements.cbAlign) {
4420 apInputPinRequirements.cbAlign = 1;
4421 }
4422
4423 hr = m_pAllocator->Decommit();
4424 if(FAILED(hr)) {
4425 return hr;
4426 }
4427
4428 hr = DecideBufferSize(m_pAllocator, &apInputPinRequirements);
4429 if(FAILED(hr)) {
4430 return hr;
4431 }
4432
4433 hr = m_pAllocator->Commit();
4434 if(FAILED(hr)) {
4435 return hr;
4436 }
4437
4438 hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);
4439 if(FAILED(hr)) {
4440 return hr;
4441 }
4442 }
4443
4444 return S_OK;
4445}
4446
4447// this method has to be called from the thread that is pushing data,
4448// and it's the caller's responsibility to make sure that the thread
4449// has no outstand samples because they cannot be delivered after a
4450// reconnect
4451//
4452HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )
4453{
4454 // The caller should call StartUsingOutputPin() before calling this
4455 // method.
4456 ASSERT(StreamingThreadUsingOutputPin());
4457
4458 if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {
4459 return E_FAIL;
4460 }
4461
4462 HRESULT hr = m_pGraphConfig->Reconnect(
4463 this,
4464 NULL,
4465 pmt,
4466 NULL,
4467 m_hStopEvent,
4468 AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );
4469
4470 return hr;
4471}
4472
4473HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)
4474{
4475 HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
4476 if(SUCCEEDED(hr)) {
4477 if(!IsStopped() && m_pAllocator) {
4478 hr = m_pAllocator->Commit();
4479 ASSERT(hr != VFW_E_ALREADY_COMMITTED);
4480 }
4481 }
4482
4483 return hr;
4484}
4485
4486#ifdef DEBUG
4487void CDynamicOutputPin::AssertValid(void)
4488{
4489 // Make sure the object was correctly initialized.
4490
4491 // This ASSERT only fires if the object failed to initialize
4492 // and the user ignored the constructor's return code (phr).
4493 ASSERT(NULL != m_hUnblockOutputPinEvent);
4494
4495 // If either of these ASSERTs fire, the user did not correctly call
4496 // SetConfigInfo().
4497 ASSERT(NULL != m_hStopEvent);
4498 ASSERT(NULL != m_pGraphConfig);
4499
4500 // Make sure the block state is consistent.
4501
4502 CAutoLock alBlockStateLock(&m_BlockStateLock);
4503
4504 // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.
4505 ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));
4506
4507 // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete
4508 // immediately.
4509 ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||
4510 ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );
4511
4512 // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and
4513 // the user is not trying to block the pin.
4514 ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));
4515
4516 // If this ASSERT fires, the streaming thread is using the output pin and the
4517 // output pin is blocked.
4518 ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||
4519 ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||
4520 ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );
4521}
4522#endif // DEBUG
4523
4524HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)
4525{
4526 const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;
4527
4528 DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);
4529
4530 switch( dwReturnValue ) {
4531 case EVENT_SIGNALED:
4532 return S_OK;
4533
4534 case WAIT_FAILED:
4535 return AmGetLastErrorToHResult();
4536
4537 default:
4538 DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );
4539 return E_UNEXPECTED;
4540 }
4541}
4542
4543//=====================================================================
4544//=====================================================================
4545// Implements CBaseAllocator
4546//=====================================================================
4547//=====================================================================
4548
4549
4550/* Constructor overrides the default settings for the free list to request
4551 that it be alertable (ie the list can be cast to a handle which can be
4552 passed to WaitForSingleObject). Both of the allocator lists also ask for
4553 object locking, the all list matches the object default settings but I
4554 have included them here just so it is obvious what kind of list it is */
4555
4556CBaseAllocator::CBaseAllocator(__in_opt LPCTSTR pName,
4557 __inout_opt LPUNKNOWN pUnk,
4558 __inout HRESULT *phr,
4559 BOOL bEvent,
4560 BOOL fEnableReleaseCallback
4561 ) :
4562 CUnknown(pName, pUnk),
4563 m_lAllocated(0),
4564 m_bChanged(FALSE),
4565 m_bCommitted(FALSE),
4566 m_bDecommitInProgress(FALSE),
4567 m_lSize(0),
4568 m_lCount(0),
4569 m_lAlignment(0),
4570 m_lPrefix(0),
4571 m_hSem(NULL),
4572 m_lWaiting(0),
4573 m_fEnableReleaseCallback(fEnableReleaseCallback),
4574 m_pNotify(NULL)
4575{
4576#ifdef DXMPERF
4577 PERFLOG_CTOR( pName ? pName : L"CBaseAllocator", (IMemAllocator *) this );
4578#endif // DXMPERF
4579
4580 if (bEvent) {
4581 m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
4582 if (m_hSem == NULL) {
4583 *phr = E_OUTOFMEMORY;
4584 return;
4585 }
4586 }
4587}
4588
4589#ifdef UNICODE
4590CBaseAllocator::CBaseAllocator(__in_opt LPCSTR pName,
4591 __inout_opt LPUNKNOWN pUnk,
4592 __inout HRESULT *phr,
4593 BOOL bEvent,
4594 BOOL fEnableReleaseCallback) :
4595 CUnknown(pName, pUnk),
4596 m_lAllocated(0),
4597 m_bChanged(FALSE),
4598 m_bCommitted(FALSE),
4599 m_bDecommitInProgress(FALSE),
4600 m_lSize(0),
4601 m_lCount(0),
4602 m_lAlignment(0),
4603 m_lPrefix(0),
4604 m_hSem(NULL),
4605 m_lWaiting(0),
4606 m_fEnableReleaseCallback(fEnableReleaseCallback),
4607 m_pNotify(NULL)
4608{
4609#ifdef DXMPERF
4610 PERFLOG_CTOR( L"CBaseAllocator", (IMemAllocator *) this );
4611#endif // DXMPERF
4612
4613 if (bEvent) {
4614 m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
4615 if (m_hSem == NULL) {
4616 *phr = E_OUTOFMEMORY;
4617 return;
4618 }
4619 }
4620}
4621#endif
4622
4623/* Destructor */
4624
4625CBaseAllocator::~CBaseAllocator()
4626{
4627 // we can't call Decommit here since that would mean a call to a
4628 // pure virtual in destructor.
4629 // We must assume that the derived class has gone into decommit state in
4630 // its destructor.
4631#ifdef DXMPERF
4632 PERFLOG_DTOR( L"CBaseAllocator", (IMemAllocator *) this );
4633#endif // DXMPERF
4634
4635 ASSERT(!m_bCommitted);
4636 if (m_hSem != NULL) {
4637 EXECUTE_ASSERT(CloseHandle(m_hSem));
4638 }
4639 if (m_pNotify) {
4640 m_pNotify->Release();
4641 }
4642}
4643
4644
4645/* Override this to publicise our interfaces */
4646
4647STDMETHODIMP
4648CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
4649{
4650 /* Do we know about this interface */
4651
4652 if (riid == IID_IMemAllocator ||
4653 riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {
4654 return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);
4655 } else {
4656 return CUnknown::NonDelegatingQueryInterface(riid, ppv);
4657 }
4658}
4659
4660
4661/* This sets the size and count of the required samples. The memory isn't
4662 actually allocated until Commit() is called, if memory has already been
4663 allocated then assuming no samples are outstanding the user may call us
4664 to change the buffering, the memory will be released in Commit() */
4665
4666STDMETHODIMP
4667CBaseAllocator::SetProperties(
4668 __in ALLOCATOR_PROPERTIES* pRequest,
4669 __out ALLOCATOR_PROPERTIES* pActual)
4670{
4671 CheckPointer(pRequest, E_POINTER);
4672 CheckPointer(pActual, E_POINTER);
4673 ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));
4674 CAutoLock cObjectLock(this);
4675
4676 ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
4677
4678 ASSERT(pRequest->cbBuffer > 0);
4679
4680 /* Check the alignment requested */
4681 if (pRequest->cbAlign != 1) {
4682 DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),
4683 pRequest->cbAlign));
4684 return VFW_E_BADALIGN;
4685 }
4686
4687 /* Can't do this if already committed, there is an argument that says we
4688 should not reject the SetProperties call if there are buffers still
4689 active. However this is called by the source filter, which is the same
4690 person who is holding the samples. Therefore it is not unreasonable
4691 for them to free all their samples before changing the requirements */
4692
4693 if (m_bCommitted) {
4694 return VFW_E_ALREADY_COMMITTED;
4695 }
4696
4697 /* Must be no outstanding buffers */
4698
4699 if (m_lAllocated != m_lFree.GetCount()) {
4700 return VFW_E_BUFFERS_OUTSTANDING;
4701 }
4702
4703 /* There isn't any real need to check the parameters as they
4704 will just be rejected when the user finally calls Commit */
4705
4706 pActual->cbBuffer = m_lSize = pRequest->cbBuffer;
4707 pActual->cBuffers = m_lCount = pRequest->cBuffers;
4708 pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
4709 pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
4710
4711 m_bChanged = TRUE;
4712 return NOERROR;
4713}
4714
4715STDMETHODIMP
4716CBaseAllocator::GetProperties(
4717 __out ALLOCATOR_PROPERTIES * pActual)
4718{
4719 CheckPointer(pActual,E_POINTER);
4720 ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
4721
4722 CAutoLock cObjectLock(this);
4723 pActual->cbBuffer = m_lSize;
4724 pActual->cBuffers = m_lCount;
4725 pActual->cbAlign = m_lAlignment;
4726 pActual->cbPrefix = m_lPrefix;
4727 return NOERROR;
4728}
4729
4730// get container for a sample. Blocking, synchronous call to get the
4731// next free buffer (as represented by an IMediaSample interface).
4732// on return, the time etc properties will be invalid, but the buffer
4733// pointer and size will be correct.
4734
4735HRESULT CBaseAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer,
4736 __in_opt REFERENCE_TIME *pStartTime,
4737 __in_opt REFERENCE_TIME *pEndTime,
4738 DWORD dwFlags
4739 )
4740{
4741 UNREFERENCED_PARAMETER(pStartTime);
4742 UNREFERENCED_PARAMETER(pEndTime);
4743 UNREFERENCED_PARAMETER(dwFlags);
4744 CMediaSample *pSample;
4745
4746 *ppBuffer = NULL;
4747 for (;;)
4748 {
4749 { // scope for lock
4750 CAutoLock cObjectLock(this);
4751
4752 /* Check we are committed */
4753 if (!m_bCommitted) {
4754 return VFW_E_NOT_COMMITTED;
4755 }
4756 pSample = (CMediaSample *) m_lFree.RemoveHead();
4757 if (pSample == NULL) {
4758 SetWaiting();
4759 }
4760 }
4761
4762 /* If we didn't get a sample then wait for the list to signal */
4763
4764 if (pSample) {
4765 break;
4766 }
4767 if (dwFlags & AM_GBF_NOWAIT) {
4768 return VFW_E_TIMEOUT;
4769 }
4770 ASSERT(m_hSem != NULL);
4771 WaitForSingleObject(m_hSem, INFINITE);
4772 }
4773
4774 /* Addref the buffer up to one. On release
4775 back to zero instead of being deleted, it will requeue itself by
4776 calling the ReleaseBuffer member function. NOTE the owner of a
4777 media sample must always be derived from CBaseAllocator */
4778
4779
4780 ASSERT(pSample->m_cRef == 0);
4781 pSample->m_cRef = 1;
4782 *ppBuffer = pSample;
4783
4784#ifdef DXMPERF
4785 PERFLOG_GETBUFFER( (IMemAllocator *) this, pSample );
4786#endif // DXMPERF
4787
4788 return NOERROR;
4789}
4790
4791
4792/* Final release of a CMediaSample will call this */
4793
4794STDMETHODIMP
4795CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)
4796{
4797 CheckPointer(pSample,E_POINTER);
4798 ValidateReadPtr(pSample,sizeof(IMediaSample));
4799
4800#ifdef DXMPERF
4801 PERFLOG_RELBUFFER( (IMemAllocator *) this, pSample );
4802#endif // DXMPERF
4803
4804
4805 BOOL bRelease = FALSE;
4806 {
4807 CAutoLock cal(this);
4808
4809 /* Put back on the free list */
4810
4811 m_lFree.Add((CMediaSample *)pSample);
4812 if (m_lWaiting != 0) {
4813 NotifySample();
4814 }
4815
4816 // if there is a pending Decommit, then we need to complete it by
4817 // calling Free() when the last buffer is placed on the free list
4818
4819 LONG l1 = m_lFree.GetCount();
4820 if (m_bDecommitInProgress && (l1 == m_lAllocated)) {
4821 Free();
4822 m_bDecommitInProgress = FALSE;
4823 bRelease = TRUE;
4824 }
4825 }
4826
4827 if (m_pNotify) {
4828
4829 ASSERT(m_fEnableReleaseCallback);
4830
4831 //
4832 // Note that this is not synchronized with setting up a notification
4833 // method.
4834 //
4835 m_pNotify->NotifyRelease();
4836 }
4837
4838 /* For each buffer there is one AddRef, made in GetBuffer and released
4839 here. This may cause the allocator and all samples to be deleted */
4840
4841 if (bRelease) {
4842 Release();
4843 }
4844 return NOERROR;
4845}
4846
4847STDMETHODIMP
4848CBaseAllocator::SetNotify(
4849 IMemAllocatorNotifyCallbackTemp* pNotify
4850 )
4851{
4852 ASSERT(m_fEnableReleaseCallback);
4853 CAutoLock lck(this);
4854 if (pNotify) {
4855 pNotify->AddRef();
4856 }
4857 if (m_pNotify) {
4858 m_pNotify->Release();
4859 }
4860 m_pNotify = pNotify;
4861 return S_OK;
4862}
4863
4864STDMETHODIMP
4865CBaseAllocator::GetFreeCount(
4866 __out LONG* plBuffersFree
4867 )
4868{
4869 ASSERT(m_fEnableReleaseCallback);
4870 CAutoLock cObjectLock(this);
4871 *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();
4872 return NOERROR;
4873}
4874
4875void
4876CBaseAllocator::NotifySample()
4877{
4878 if (m_lWaiting != 0) {
4879 ASSERT(m_hSem != NULL);
4880 ReleaseSemaphore(m_hSem, m_lWaiting, 0);
4881 m_lWaiting = 0;
4882 }
4883}
4884
4885STDMETHODIMP
4886CBaseAllocator::Commit()
4887{
4888 /* Check we are not decommitted */
4889 CAutoLock cObjectLock(this);
4890
4891 // cannot need to alloc or re-alloc if we are committed
4892 if (m_bCommitted) {
4893 return NOERROR;
4894 }
4895
4896 /* Allow GetBuffer calls */
4897
4898 m_bCommitted = TRUE;
4899
4900 // is there a pending decommit ? if so, just cancel it
4901 if (m_bDecommitInProgress) {
4902 m_bDecommitInProgress = FALSE;
4903
4904 // don't call Alloc at this point. He cannot allow SetProperties
4905 // between Decommit and the last free, so the buffer size cannot have
4906 // changed. And because some of the buffers are not free yet, he
4907 // cannot re-alloc anyway.
4908 return NOERROR;
4909 }
4910
4911 DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));
4912
4913 // actually need to allocate the samples
4914 HRESULT hr = Alloc();
4915 if (FAILED(hr)) {
4916 m_bCommitted = FALSE;
4917 return hr;
4918 }
4919 AddRef();
4920 return NOERROR;
4921}
4922
4923
4924STDMETHODIMP
4925CBaseAllocator::Decommit()
4926{
4927 BOOL bRelease = FALSE;
4928 {
4929 /* Check we are not already decommitted */
4930 CAutoLock cObjectLock(this);
4931 if (m_bCommitted == FALSE) {
4932 if (m_bDecommitInProgress == FALSE) {
4933 return NOERROR;
4934 }
4935 }
4936
4937 /* No more GetBuffer calls will succeed */
4938 m_bCommitted = FALSE;
4939
4940 // are any buffers outstanding?
4941 if (m_lFree.GetCount() < m_lAllocated) {
4942 // please complete the decommit when last buffer is freed
4943 m_bDecommitInProgress = TRUE;
4944 } else {
4945 m_bDecommitInProgress = FALSE;
4946
4947 // need to complete the decommit here as there are no
4948 // outstanding buffers
4949
4950 Free();
4951 bRelease = TRUE;
4952 }
4953
4954 // Tell anyone waiting that they can go now so we can
4955 // reject their call
4956#pragma warning(push)
4957#ifndef _PREFAST_
4958#pragma warning(disable:4068)
4959#endif
4960#pragma prefast(suppress:__WARNING_DEREF_NULL_PTR, "Suppress warning related to Free() invalidating 'this' which is no applicable to CBaseAllocator::Free()")
4961 NotifySample();
4962
4963#pragma warning(pop)
4964 }
4965
4966 if (bRelease) {
4967 Release();
4968 }
4969 return NOERROR;
4970}
4971
4972
4973/* Base definition of allocation which checks we are ok to go ahead and do
4974 the full allocation. We return S_FALSE if the requirements are the same */
4975
4976HRESULT
4977CBaseAllocator::Alloc(void)
4978{
4979 /* Error if he hasn't set the size yet */
4980 if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {
4981 return VFW_E_SIZENOTSET;
4982 }
4983
4984 /* should never get here while buffers outstanding */
4985 ASSERT(m_lFree.GetCount() == m_lAllocated);
4986
4987 /* If the requirements haven't changed then don't reallocate */
4988 if (m_bChanged == FALSE) {
4989 return S_FALSE;
4990 }
4991
4992 return NOERROR;
4993}
4994
4995/* Implement CBaseAllocator::CSampleList::Remove(pSample)
4996 Removes pSample from the list
4997*/
4998void
4999CBaseAllocator::CSampleList::Remove(__inout CMediaSample * pSample)
5000{
5001 CMediaSample **pSearch;
5002 for (pSearch = &m_List;
5003 *pSearch != NULL;
5004 pSearch = &(CBaseAllocator::NextSample(*pSearch))) {
5005 if (*pSearch == pSample) {
5006 *pSearch = CBaseAllocator::NextSample(pSample);
5007 CBaseAllocator::NextSample(pSample) = NULL;
5008 m_nOnList--;
5009 return;
5010 }
5011 }
5012 DbgBreak("Couldn't find sample in list");
5013}
5014
5015//=====================================================================
5016//=====================================================================
5017// Implements CMemAllocator
5018//=====================================================================
5019//=====================================================================
5020
5021
5022/* This goes in the factory template table to create new instances */
5023CUnknown *CMemAllocator::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
5024{
5025 CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);
5026 return pUnkRet;
5027}
5028
5029CMemAllocator::CMemAllocator(
5030 __in_opt LPCTSTR pName,
5031 __inout_opt LPUNKNOWN pUnk,
5032 __inout HRESULT *phr)
5033 : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
5034 m_pBuffer(NULL)
5035{
5036}
5037
5038#ifdef UNICODE
5039CMemAllocator::CMemAllocator(
5040 __in_opt LPCSTR pName,
5041 __inout_opt LPUNKNOWN pUnk,
5042 __inout HRESULT *phr)
5043 : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
5044 m_pBuffer(NULL)
5045{
5046}
5047#endif
5048
5049/* This sets the size and count of the required samples. The memory isn't
5050 actually allocated until Commit() is called, if memory has already been
5051 allocated then assuming no samples are outstanding the user may call us
5052 to change the buffering, the memory will be released in Commit() */
5053STDMETHODIMP
5054CMemAllocator::SetProperties(
5055 __in ALLOCATOR_PROPERTIES* pRequest,
5056 __out ALLOCATOR_PROPERTIES* pActual)
5057{
5058 CheckPointer(pActual,E_POINTER);
5059 ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
5060 CAutoLock cObjectLock(this);
5061
5062 ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
5063
5064 ASSERT(pRequest->cbBuffer > 0);
5065
5066 SYSTEM_INFO SysInfo;
5067 GetSystemInfo(&SysInfo);
5068
5069 /* Check the alignment request is a power of 2 */
5070 if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {
5071 DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),
5072 pRequest->cbAlign));
5073 }
5074 /* Check the alignment requested */
5075 if (pRequest->cbAlign == 0 ||
5076 (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {
5077 DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),
5078 pRequest->cbAlign, SysInfo.dwAllocationGranularity));
5079 return VFW_E_BADALIGN;
5080 }
5081
5082 /* Can't do this if already committed, there is an argument that says we
5083 should not reject the SetProperties call if there are buffers still
5084 active. However this is called by the source filter, which is the same
5085 person who is holding the samples. Therefore it is not unreasonable
5086 for them to free all their samples before changing the requirements */
5087
5088 if (m_bCommitted == TRUE) {
5089 return VFW_E_ALREADY_COMMITTED;
5090 }
5091
5092 /* Must be no outstanding buffers */
5093
5094 if (m_lFree.GetCount() < m_lAllocated) {
5095 return VFW_E_BUFFERS_OUTSTANDING;
5096 }
5097
5098 /* There isn't any real need to check the parameters as they
5099 will just be rejected when the user finally calls Commit */
5100
5101 // round length up to alignment - remember that prefix is included in
5102 // the alignment
5103 LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;
5104 LONG lRemainder = lSize % pRequest->cbAlign;
5105 if (lRemainder != 0) {
5106 lSize = lSize - lRemainder + pRequest->cbAlign;
5107 }
5108 pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);
5109
5110 pActual->cBuffers = m_lCount = pRequest->cBuffers;
5111 pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
5112 pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
5113
5114 m_bChanged = TRUE;
5115 return NOERROR;
5116}
5117
5118// override this to allocate our resources when Commit is called.
5119//
5120// note that our resources may be already allocated when this is called,
5121// since we don't free them on Decommit. We will only be called when in
5122// decommit state with all buffers free.
5123//
5124// object locked by caller
5125HRESULT
5126CMemAllocator::Alloc(void)
5127{
5128 CAutoLock lck(this);
5129
5130 /* Check he has called SetProperties */
5131 HRESULT hr = CBaseAllocator::Alloc();
5132 if (FAILED(hr)) {
5133 return hr;
5134 }
5135
5136 /* If the requirements haven't changed then don't reallocate */
5137 if (hr == S_FALSE) {
5138 ASSERT(m_pBuffer);
5139 return NOERROR;
5140 }
5141 ASSERT(hr == S_OK); // we use this fact in the loop below
5142
5143 /* Free the old resources */
5144 if (m_pBuffer) {
5145 ReallyFree();
5146 }
5147
5148 /* Make sure we've got reasonable values */
5149 if ( m_lSize < 0 || m_lPrefix < 0 || m_lCount < 0 ) {
5150 return E_OUTOFMEMORY;
5151 }
5152
5153 /* Compute the aligned size */
5154 LONG lAlignedSize = m_lSize + m_lPrefix;
5155
5156 /* Check overflow */
5157 if (lAlignedSize < m_lSize) {
5158 return E_OUTOFMEMORY;
5159 }
5160
5161 if (m_lAlignment > 1) {
5162 LONG lRemainder = lAlignedSize % m_lAlignment;
5163 if (lRemainder != 0) {
5164 LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder;
5165 if (lNewSize < lAlignedSize) {
5166 return E_OUTOFMEMORY;
5167 }
5168 lAlignedSize = lNewSize;
5169 }
5170 }
5171
5172 /* Create the contiguous memory block for the samples
5173 making sure it's properly aligned (64K should be enough!)
5174 */
5175 ASSERT(lAlignedSize % m_lAlignment == 0);
5176
5177 LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize;
5178
5179 /* Check overflow */
5180 if (lToAllocate > MAXLONG) {
5181 return E_OUTOFMEMORY;
5182 }
5183
5184 m_pBuffer = (PBYTE)VirtualAlloc(NULL,
5185 (LONG)lToAllocate,
5186 MEM_COMMIT,
5187 PAGE_READWRITE);
5188
5189 if (m_pBuffer == NULL) {
5190 return E_OUTOFMEMORY;
5191 }
5192
5193 LPBYTE pNext = m_pBuffer;
5194 CMediaSample *pSample;
5195
5196 ASSERT(m_lAllocated == 0);
5197
5198 // Create the new samples - we have allocated m_lSize bytes for each sample
5199 // plus m_lPrefix bytes per sample as a prefix. We set the pointer to
5200 // the memory after the prefix - so that GetPointer() will return a pointer
5201 // to m_lSize bytes.
5202 for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {
5203
5204
5205 pSample = new CMediaSample(
5206 NAME("Default memory media sample"),
5207 this,
5208 &hr,
5209 pNext + m_lPrefix, // GetPointer() value
5210 m_lSize); // not including prefix
5211
5212 ASSERT(SUCCEEDED(hr));
5213 if (pSample == NULL) {
5214 return E_OUTOFMEMORY;
5215 }
5216
5217 // This CANNOT fail
5218 m_lFree.Add(pSample);
5219 }
5220
5221 m_bChanged = FALSE;
5222 return NOERROR;
5223}
5224
5225
5226// override this to free up any resources we have allocated.
5227// called from the base class on Decommit when all buffers have been
5228// returned to the free list.
5229//
5230// caller has already locked the object.
5231
5232// in our case, we keep the memory until we are deleted, so
5233// we do nothing here. The memory is deleted in the destructor by
5234// calling ReallyFree()
5235void
5236CMemAllocator::Free(void)
5237{
5238 return;
5239}
5240
5241
5242// called from the destructor (and from Alloc if changing size/count) to
5243// actually free up the memory
5244void
5245CMemAllocator::ReallyFree(void)
5246{
5247 /* Should never be deleting this unless all buffers are freed */
5248
5249 ASSERT(m_lAllocated == m_lFree.GetCount());
5250
5251 /* Free up all the CMediaSamples */
5252
5253 CMediaSample *pSample;
5254 for (;;) {
5255 pSample = m_lFree.RemoveHead();
5256 if (pSample != NULL) {
5257 delete pSample;
5258 } else {
5259 break;
5260 }
5261 }
5262
5263 m_lAllocated = 0;
5264
5265 // free the block of buffer memory
5266 if (m_pBuffer) {
5267 EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));
5268 m_pBuffer = NULL;
5269 }
5270}
5271
5272
5273/* Destructor frees our memory resources */
5274
5275CMemAllocator::~CMemAllocator()
5276{
5277 Decommit();
5278 ReallyFree();
5279}
5280
5281// ------------------------------------------------------------------------
5282// filter registration through IFilterMapper. used if IFilterMapper is
5283// not found (Quartz 1.0 install)
5284
5285STDAPI
5286AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata
5287 , IFilterMapper * pIFM
5288 , BOOL bRegister )
5289{
5290 DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));
5291
5292 // check we've got data
5293 //
5294 if( NULL == psetupdata ) return S_FALSE;
5295
5296
5297 // unregister filter
5298 // (as pins are subkeys of filter's CLSID key
5299 // they do not need to be removed separately).
5300 //
5301 DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
5302 HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );
5303
5304
5305 if( bRegister )
5306 {
5307 // register filter
5308 //
5309 DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
5310 hr = pIFM->RegisterFilter( *(psetupdata->clsID)
5311 , psetupdata->strName
5312 , psetupdata->dwMerit );
5313 if( SUCCEEDED(hr) )
5314 {
5315 // all its pins
5316 //
5317 DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));
5318 for( UINT m1=0; m1 < psetupdata->nPins; m1++ )
5319 {
5320 hr = pIFM->RegisterPin( *(psetupdata->clsID)
5321 , psetupdata->lpPin[m1].strName
5322 , psetupdata->lpPin[m1].bRendered
5323 , psetupdata->lpPin[m1].bOutput
5324 , psetupdata->lpPin[m1].bZero
5325 , psetupdata->lpPin[m1].bMany
5326 , *(psetupdata->lpPin[m1].clsConnectsToFilter)
5327 , psetupdata->lpPin[m1].strConnectsToPin );
5328
5329 if( SUCCEEDED(hr) )
5330 {
5331 // and each pin's media types
5332 //
5333 DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));
5334 for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )
5335 {
5336 hr = pIFM->RegisterPinType( *(psetupdata->clsID)
5337 , psetupdata->lpPin[m1].strName
5338 , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)
5339 , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );
5340 if( FAILED(hr) ) break;
5341 }
5342 if( FAILED(hr) ) break;
5343 }
5344 if( FAILED(hr) ) break;
5345 }
5346 }
5347 }
5348
5349 // handle one acceptable "error" - that
5350 // of filter not being registered!
5351 // (couldn't find a suitable #define'd
5352 // name for the error!)
5353 //
5354 if( 0x80070002 == hr)
5355 return NOERROR;
5356 else
5357 return hr;
5358}
5359
5360// Remove warnings about unreferenced inline functions
5361#pragma warning(disable:4514)
5362
5363#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */