1 //------------------------------------------------------------------------------
4 // Desc: DirectShow base classes - implements CSource, which is a Quartz
5 // source filter 'template.'
7 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
8 //------------------------------------------------------------------------------
13 // Hold the filter critical section (m_pFilter->pStateLock()) to serialise
14 // access to functions. Note that, in general, this lock may be held
15 // by a function when the worker thread may want to hold it. Therefore
16 // if you wish to access shared state from the worker thread you will
17 // need to add another critical section object. The execption is during
18 // the threads processing loop, when it is safe to get the filter critical
19 // section from within FillBuffer().
25 // CSource::Constructor
27 // Initialise the pin count for the filter. The user will create the pins in
29 CSource::CSource(TCHAR
*pName
, LPUNKNOWN lpunk
, CLSID clsid
)
30 : CBaseFilter(pName
, lpunk
, &m_cStateLock
, clsid
),
36 CSource::CSource(TCHAR
*pName
, LPUNKNOWN lpunk
, CLSID clsid
, HRESULT
*phr
)
37 : CBaseFilter(pName
, lpunk
, &m_cStateLock
, clsid
),
41 UNREFERENCED_PARAMETER(phr
);
45 CSource::CSource(CHAR
*pName
, LPUNKNOWN lpunk
, CLSID clsid
)
46 : CBaseFilter(pName
, lpunk
, &m_cStateLock
, clsid
),
52 CSource::CSource(CHAR
*pName
, LPUNKNOWN lpunk
, CLSID clsid
, HRESULT
*phr
)
53 : CBaseFilter(pName
, lpunk
, &m_cStateLock
, clsid
),
57 UNREFERENCED_PARAMETER(phr
);
62 // CSource::Destructor
66 /* Free our pins and pin array */
67 while (m_iPins
!= 0) {
68 // deleting the pins causes them to be removed from the array...
69 delete m_paStreams
[m_iPins
- 1];
72 ASSERT(m_paStreams
== NULL
);
79 HRESULT
CSource::AddPin(CSourceStream
*pStream
)
81 CAutoLock
lock(&m_cStateLock
);
83 /* Allocate space for this pin and the old ones */
84 CSourceStream
**paStreams
= new CSourceStream
*[m_iPins
+ 1];
85 if (paStreams
== NULL
) {
88 if (m_paStreams
!= NULL
) {
89 CopyMemory((PVOID
)paStreams
, (PVOID
)m_paStreams
,
90 m_iPins
* sizeof(m_paStreams
[0]));
91 paStreams
[m_iPins
] = pStream
;
92 delete [] m_paStreams
;
94 m_paStreams
= paStreams
;
95 m_paStreams
[m_iPins
] = pStream
;
101 // Remove a pin - pStream is NOT deleted
103 HRESULT
CSource::RemovePin(CSourceStream
*pStream
)
106 for (i
= 0; i
< m_iPins
; i
++) {
107 if (m_paStreams
[i
] == pStream
) {
109 delete [] m_paStreams
;
112 /* no need to reallocate */
113 while (++i
< m_iPins
)
114 m_paStreams
[i
- 1] = m_paStreams
[i
];
126 // Set *ppPin to the IPin* that has the id Id.
127 // or to NULL if the Id cannot be matched.
128 STDMETHODIMP
CSource::FindPin(LPCWSTR Id
, IPin
**ppPin
)
130 CheckPointer(ppPin
,E_POINTER
);
131 ValidateReadWritePtr(ppPin
,sizeof(IPin
*));
132 // The -1 undoes the +1 in QueryId and ensures that totally invalid
133 // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
134 int i
= WstrToInt(Id
) -1;
140 return VFW_E_NOT_FOUND
;
147 // return the number of the pin with this IPin* or -1 if none
148 int CSource::FindPinNumber(IPin
*iPin
) {
150 for (i
=0; i
<m_iPins
; ++i
) {
151 if ((IPin
*)(m_paStreams
[i
])==iPin
) {
161 // Returns the number of pins this filter has
162 int CSource::GetPinCount(void) {
164 CAutoLock
lock(&m_cStateLock
);
172 // Return a non-addref'd pointer to pin n
173 // needed by CBaseFilter
174 CBasePin
*CSource::GetPin(int n
) {
176 CAutoLock
lock(&m_cStateLock
);
178 // n must be in the range 0..m_iPins-1
179 // if m_iPins>n && n>=0 it follows that m_iPins>0
180 // which is what used to be checked (i.e. checking that we have a pin)
181 if ((n
>= 0) && (n
< m_iPins
)) {
183 ASSERT(m_paStreams
[n
]);
184 return m_paStreams
[n
];
194 // * --- CSourceStream ----
198 // Set Id to point to a CoTaskMemAlloc'd
199 STDMETHODIMP
CSourceStream::QueryId(LPWSTR
*Id
) {
200 CheckPointer(Id
,E_POINTER
);
201 ValidateReadWritePtr(Id
,sizeof(LPWSTR
));
203 // We give the pins id's which are 1,2,...
204 // FindPinNumber returns -1 for an invalid pin
205 int i
= 1+ m_pFilter
->FindPinNumber(this);
206 if (i
<1) return VFW_E_NOT_FOUND
;
207 *Id
= (LPWSTR
)CoTaskMemAlloc(8);
209 return E_OUTOFMEMORY
;
218 // CSourceStream::Constructor
220 // increments the number of pins present on the filter
221 CSourceStream::CSourceStream(
226 : CBaseOutputPin(pObjectName
, ps
, ps
->pStateLock(), phr
, pPinName
),
229 *phr
= m_pFilter
->AddPin(this);
233 CSourceStream::CSourceStream(
238 : CBaseOutputPin(pObjectName
, ps
, ps
->pStateLock(), phr
, pPinName
),
241 *phr
= m_pFilter
->AddPin(this);
245 // CSourceStream::Destructor
247 // Decrements the number of pins on this filter
248 CSourceStream::~CSourceStream(void) {
250 m_pFilter
->RemovePin(this);
257 // Do we support this type? Provides the default support for 1 type.
258 HRESULT
CSourceStream::CheckMediaType(const CMediaType
*pMediaType
) {
260 CAutoLock
lock(m_pFilter
->pStateLock());
265 if (mt
== *pMediaType
) {
276 // By default we support only one type
277 // iPosition indexes are 0-n
278 HRESULT
CSourceStream::GetMediaType(int iPosition
, CMediaType
*pMediaType
) {
280 CAutoLock
lock(m_pFilter
->pStateLock());
286 return VFW_S_NO_MORE_ITEMS
;
288 return GetMediaType(pMediaType
);
295 // The pin is active - start up the worker thread
296 HRESULT
CSourceStream::Active(void) {
298 CAutoLock
lock(m_pFilter
->pStateLock());
302 if (m_pFilter
->IsActive()) {
303 return S_FALSE
; // succeeded, but did not allocate resources (they already exist...)
306 // do nothing if not connected - its ok not to connect to
307 // all pins of a source filter
308 if (!IsConnected()) {
312 hr
= CBaseOutputPin::Active();
317 ASSERT(!ThreadExists());
324 // Tell thread to initialize. If OnThreadCreate Fails, so does this.
336 // Pin is inactive - shut down the worker thread
337 // Waits for the worker to exit before returning.
338 HRESULT
CSourceStream::Inactive(void) {
340 CAutoLock
lock(m_pFilter
->pStateLock());
344 // do nothing if not connected - its ok not to connect to
345 // all pins of a source filter
346 if (!IsConnected()) {
350 // !!! need to do this before trying to stop the thread, because
351 // we may be stuck waiting for our own allocator!!!
353 hr
= CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
358 if (ThreadExists()) {
370 Close(); // Wait for the thread to exit, then tidy up.
373 // hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
385 // When this returns the thread exits
386 // Return codes > 0 indicate an error occured
387 DWORD
CSourceStream::ThreadProc(void) {
389 HRESULT hr
; // the return code from calls
394 if (com
!= CMD_INIT
) {
395 DbgLog((LOG_ERROR
, 1, TEXT("Thread expected init command")));
396 Reply((DWORD
) E_UNEXPECTED
);
398 } while (com
!= CMD_INIT
);
400 DbgLog((LOG_TRACE
, 1, TEXT("CSourceStream worker thread initializing")));
402 hr
= OnThreadCreate(); // perform set up tasks
404 DbgLog((LOG_ERROR
, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
406 Reply(hr
); // send failed return code from OnThreadCreate
410 // Initialisation suceeded
424 DbgLog((LOG_ERROR
, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
425 // !!! fall through???
429 DoBufferProcessingLoop();
437 DbgLog((LOG_ERROR
, 1, TEXT("Unknown command %d received!"), cmd
));
438 Reply((DWORD
) E_NOTIMPL
);
441 } while (cmd
!= CMD_EXIT
);
443 hr
= OnThreadDestroy(); // tidy up.
445 DbgLog((LOG_ERROR
, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
449 DbgLog((LOG_TRACE
, 1, TEXT("CSourceStream worker thread exiting")));
455 // DoBufferProcessingLoop
457 // Grabs a buffer and calls the users processing function.
458 // Overridable, so that different delivery styles can be catered for.
459 HRESULT
CSourceStream::DoBufferProcessingLoop(void) {
466 while (!CheckRequest(&com
)) {
468 IMediaSample
*pSample
;
470 HRESULT hr
= GetDeliveryBuffer(&pSample
,NULL
,NULL
,0);
473 continue; // go round again. Perhaps the error will go away
474 // or the allocator is decommited & we will be asked to
478 // Virtual function user will override.
479 hr
= FillBuffer(pSample
);
482 hr
= Deliver(pSample
);
485 // downstream filter returns S_FALSE if it wants us to
486 // stop or an error if it's reporting an error.
489 DbgLog((LOG_TRACE
, 2, TEXT("Deliver() returned %08x; stopping"), hr
));
493 } else if (hr
== S_FALSE
) {
494 // derived class wants us to stop pushing data
496 DeliverEndOfStream();
499 // derived class encountered an error
501 DbgLog((LOG_ERROR
, 1, TEXT("Error %08lX from FillBuffer!!!"), hr
));
502 DeliverEndOfStream();
503 m_pFilter
->NotifyEvent(EC_ERRORABORT
, hr
, 0);
507 // all paths release the sample
510 // For all commands sent to us there must be a Reply call!
512 if (com
== CMD_RUN
|| com
== CMD_PAUSE
) {
514 } else if (com
!= CMD_STOP
) {
515 Reply((DWORD
) E_UNEXPECTED
);
516 DbgLog((LOG_ERROR
, 1, TEXT("Unexpected command!!!")));
518 } while (com
!= CMD_STOP
);