A Fast Bresenham Type Algorithm For Drawing Ellipses by John Kennedy
[xy_vsfilter.git] / src / filters / BaseClasses / source.cpp
blobb6049872707aeefa8fe863ede99a10ff27e465b0
1 //------------------------------------------------------------------------------
2 // File: Source.cpp
3 //
4 // Desc: DirectShow base classes - implements CSource, which is a Quartz
5 // source filter 'template.'
6 //
7 // Copyright (c) 1992-2002 Microsoft Corporation. All rights reserved.
8 //------------------------------------------------------------------------------
11 // Locking Strategy.
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().
21 #include <streams.h>
25 // CSource::Constructor
27 // Initialise the pin count for the filter. The user will create the pins in
28 // the derived class.
29 CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
30 : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
31 m_iPins(0),
32 m_paStreams(NULL)
36 CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
37 : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
38 m_iPins(0),
39 m_paStreams(NULL)
41 UNREFERENCED_PARAMETER(phr);
44 #ifdef UNICODE
45 CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
46 : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
47 m_iPins(0),
48 m_paStreams(NULL)
52 CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
53 : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
54 m_iPins(0),
55 m_paStreams(NULL)
57 UNREFERENCED_PARAMETER(phr);
59 #endif
62 // CSource::Destructor
64 CSource::~CSource()
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);
77 // Add a new pin
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) {
86 return E_OUTOFMEMORY;
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;
96 m_iPins++;
97 return S_OK;
101 // Remove a pin - pStream is NOT deleted
103 HRESULT CSource::RemovePin(CSourceStream *pStream)
105 int i;
106 for (i = 0; i < m_iPins; i++) {
107 if (m_paStreams[i] == pStream) {
108 if (m_iPins == 1) {
109 delete [] m_paStreams;
110 m_paStreams = NULL;
111 } else {
112 /* no need to reallocate */
113 while (++i < m_iPins)
114 m_paStreams[i - 1] = m_paStreams[i];
116 m_iPins--;
117 return S_OK;
120 return S_FALSE;
124 // FindPin
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;
135 *ppPin = GetPin(i);
136 if (*ppPin!=NULL){
137 (*ppPin)->AddRef();
138 return NOERROR;
139 } else {
140 return VFW_E_NOT_FOUND;
145 // FindPinNumber
147 // return the number of the pin with this IPin* or -1 if none
148 int CSource::FindPinNumber(IPin *iPin) {
149 int i;
150 for (i=0; i<m_iPins; ++i) {
151 if ((IPin *)(m_paStreams[i])==iPin) {
152 return i;
155 return -1;
159 // GetPinCount
161 // Returns the number of pins this filter has
162 int CSource::GetPinCount(void) {
164 CAutoLock lock(&m_cStateLock);
165 return m_iPins;
170 // GetPin
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];
186 return NULL;
193 // *
194 // * --- CSourceStream ----
195 // *
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);
208 if (*Id==NULL) {
209 return E_OUTOFMEMORY;
211 IntToWstr(i, *Id);
212 return NOERROR;
218 // CSourceStream::Constructor
220 // increments the number of pins present on the filter
221 CSourceStream::CSourceStream(
222 TCHAR *pObjectName,
223 HRESULT *phr,
224 CSource *ps,
225 LPCWSTR pPinName)
226 : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
227 m_pFilter(ps) {
229 *phr = m_pFilter->AddPin(this);
232 #ifdef UNICODE
233 CSourceStream::CSourceStream(
234 char *pObjectName,
235 HRESULT *phr,
236 CSource *ps,
237 LPCWSTR pPinName)
238 : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
239 m_pFilter(ps) {
241 *phr = m_pFilter->AddPin(this);
243 #endif
245 // CSourceStream::Destructor
247 // Decrements the number of pins on this filter
248 CSourceStream::~CSourceStream(void) {
250 m_pFilter->RemovePin(this);
255 // CheckMediaType
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());
262 CMediaType mt;
263 GetMediaType(&mt);
265 if (mt == *pMediaType) {
266 return NOERROR;
269 return E_FAIL;
274 // GetMediaType/3
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());
282 if (iPosition<0) {
283 return E_INVALIDARG;
285 if (iPosition>0) {
286 return VFW_S_NO_MORE_ITEMS;
288 return GetMediaType(pMediaType);
293 // Active
295 // The pin is active - start up the worker thread
296 HRESULT CSourceStream::Active(void) {
298 CAutoLock lock(m_pFilter->pStateLock());
300 HRESULT hr;
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()) {
309 return NOERROR;
312 hr = CBaseOutputPin::Active();
313 if (FAILED(hr)) {
314 return hr;
317 ASSERT(!ThreadExists());
319 // start the thread
320 if (!Create()) {
321 return E_FAIL;
324 // Tell thread to initialize. If OnThreadCreate Fails, so does this.
325 hr = Init();
326 if (FAILED(hr))
327 return hr;
329 return Pause();
334 // Inactive
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());
342 HRESULT hr;
344 // do nothing if not connected - its ok not to connect to
345 // all pins of a source filter
346 if (!IsConnected()) {
347 return NOERROR;
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
354 if (FAILED(hr)) {
355 return hr;
358 if (ThreadExists()) {
359 hr = Stop();
361 if (FAILED(hr)) {
362 return hr;
365 hr = Exit();
366 if (FAILED(hr)) {
367 return hr;
370 Close(); // Wait for the thread to exit, then tidy up.
373 // hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
374 //if (FAILED(hr)) {
375 // return hr;
378 return NOERROR;
383 // ThreadProc
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
390 Command com;
392 do {
393 com = GetRequest();
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
403 if (FAILED(hr)) {
404 DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
405 OnThreadDestroy();
406 Reply(hr); // send failed return code from OnThreadCreate
407 return 1;
410 // Initialisation suceeded
411 Reply(NOERROR);
413 Command cmd;
414 do {
415 cmd = GetRequest();
417 switch (cmd) {
419 case CMD_EXIT:
420 Reply(NOERROR);
421 break;
423 case CMD_RUN:
424 DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
425 // !!! fall through???
427 case CMD_PAUSE:
428 Reply(NOERROR);
429 DoBufferProcessingLoop();
430 break;
432 case CMD_STOP:
433 Reply(NOERROR);
434 break;
436 default:
437 DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
438 Reply((DWORD) E_NOTIMPL);
439 break;
441 } while (cmd != CMD_EXIT);
443 hr = OnThreadDestroy(); // tidy up.
444 if (FAILED(hr)) {
445 DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
446 return 1;
449 DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
450 return 0;
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) {
461 Command com;
463 OnThreadStartPlay();
465 do {
466 while (!CheckRequest(&com)) {
468 IMediaSample *pSample;
470 HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
471 if (FAILED(hr)) {
472 Sleep(1);
473 continue; // go round again. Perhaps the error will go away
474 // or the allocator is decommited & we will be asked to
475 // exit soon.
478 // Virtual function user will override.
479 hr = FillBuffer(pSample);
481 if (hr == S_OK) {
482 hr = Deliver(pSample);
483 pSample->Release();
485 // downstream filter returns S_FALSE if it wants us to
486 // stop or an error if it's reporting an error.
487 if(hr != S_OK)
489 DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
490 return S_OK;
493 } else if (hr == S_FALSE) {
494 // derived class wants us to stop pushing data
495 pSample->Release();
496 DeliverEndOfStream();
497 return S_OK;
498 } else {
499 // derived class encountered an error
500 pSample->Release();
501 DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
502 DeliverEndOfStream();
503 m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
504 return hr;
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) {
513 Reply(NOERROR);
514 } else if (com != CMD_STOP) {
515 Reply((DWORD) E_UNEXPECTED);
516 DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
518 } while (com != CMD_STOP);
520 return S_FALSE;