1
0

source.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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-2001 Microsoft Corporation. All rights reserved.
  8. //------------------------------------------------------------------------------
  9. // Locking Strategy.
  10. //
  11. // Hold the filter critical section (m_pFilter->pStateLock()) to serialise
  12. // access to functions. Note that, in general, this lock may be held
  13. // by a function when the worker thread may want to hold it. Therefore
  14. // if you wish to access shared state from the worker thread you will
  15. // need to add another critical section object. The execption is during
  16. // the threads processing loop, when it is safe to get the filter critical
  17. // section from within FillBuffer().
  18. #include <streams.h>
  19. //
  20. // CSource::Constructor
  21. //
  22. // Initialise the pin count for the filter. The user will create the pins in
  23. // the derived class.
  24. CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
  25. : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  26. m_iPins(0),
  27. m_paStreams(NULL)
  28. {
  29. }
  30. CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
  31. : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  32. m_iPins(0),
  33. m_paStreams(NULL)
  34. {
  35. UNREFERENCED_PARAMETER(phr);
  36. }
  37. #ifdef UNICODE
  38. CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid)
  39. : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  40. m_iPins(0),
  41. m_paStreams(NULL)
  42. {
  43. }
  44. CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr)
  45. : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  46. m_iPins(0),
  47. m_paStreams(NULL)
  48. {
  49. UNREFERENCED_PARAMETER(phr);
  50. }
  51. #endif
  52. //
  53. // CSource::Destructor
  54. //
  55. CSource::~CSource()
  56. {
  57. /* Free our pins and pin array */
  58. while (m_iPins != 0) {
  59. // deleting the pins causes them to be removed from the array...
  60. delete m_paStreams[m_iPins - 1];
  61. }
  62. ASSERT(m_paStreams == NULL);
  63. }
  64. //
  65. // Add a new pin
  66. //
  67. HRESULT CSource::AddPin(__in CSourceStream *pStream)
  68. {
  69. CAutoLock lock(&m_cStateLock);
  70. /* Allocate space for this pin and the old ones */
  71. CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
  72. if (paStreams == NULL) {
  73. return E_OUTOFMEMORY;
  74. }
  75. if (m_paStreams != NULL) {
  76. CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
  77. m_iPins * sizeof(m_paStreams[0]));
  78. paStreams[m_iPins] = pStream;
  79. delete [] m_paStreams;
  80. }
  81. m_paStreams = paStreams;
  82. m_paStreams[m_iPins] = pStream;
  83. m_iPins++;
  84. return S_OK;
  85. }
  86. //
  87. // Remove a pin - pStream is NOT deleted
  88. //
  89. HRESULT CSource::RemovePin(__in CSourceStream *pStream)
  90. {
  91. int i;
  92. for (i = 0; i < m_iPins; i++) {
  93. if (m_paStreams[i] == pStream) {
  94. if (m_iPins == 1) {
  95. delete [] m_paStreams;
  96. m_paStreams = NULL;
  97. } else {
  98. /* no need to reallocate */
  99. while (++i < m_iPins)
  100. m_paStreams[i - 1] = m_paStreams[i];
  101. }
  102. m_iPins--;
  103. return S_OK;
  104. }
  105. }
  106. return S_FALSE;
  107. }
  108. //
  109. // FindPin
  110. //
  111. // Set *ppPin to the IPin* that has the id Id.
  112. // or to NULL if the Id cannot be matched.
  113. STDMETHODIMP CSource::FindPin(LPCWSTR Id, __deref_out IPin **ppPin)
  114. {
  115. CheckPointer(ppPin,E_POINTER);
  116. ValidateReadWritePtr(ppPin,sizeof(IPin *));
  117. // The -1 undoes the +1 in QueryId and ensures that totally invalid
  118. // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
  119. int i = WstrToInt(Id) -1;
  120. *ppPin = GetPin(i);
  121. if (*ppPin!=NULL){
  122. (*ppPin)->AddRef();
  123. return NOERROR;
  124. } else {
  125. return VFW_E_NOT_FOUND;
  126. }
  127. }
  128. //
  129. // FindPinNumber
  130. //
  131. // return the number of the pin with this IPin* or -1 if none
  132. int CSource::FindPinNumber(__in IPin *iPin) {
  133. int i;
  134. for (i=0; i<m_iPins; ++i) {
  135. if ((IPin *)(m_paStreams[i])==iPin) {
  136. return i;
  137. }
  138. }
  139. return -1;
  140. }
  141. //
  142. // GetPinCount
  143. //
  144. // Returns the number of pins this filter has
  145. int CSource::GetPinCount(void) {
  146. CAutoLock lock(&m_cStateLock);
  147. return m_iPins;
  148. }
  149. //
  150. // GetPin
  151. //
  152. // Return a non-addref'd pointer to pin n
  153. // needed by CBaseFilter
  154. CBasePin *CSource::GetPin(int n) {
  155. CAutoLock lock(&m_cStateLock);
  156. // n must be in the range 0..m_iPins-1
  157. // if m_iPins>n && n>=0 it follows that m_iPins>0
  158. // which is what used to be checked (i.e. checking that we have a pin)
  159. if ((n >= 0) && (n < m_iPins)) {
  160. ASSERT(m_paStreams[n]);
  161. return m_paStreams[n];
  162. }
  163. return NULL;
  164. }
  165. //
  166. // *
  167. // * --- CSourceStream ----
  168. // *
  169. //
  170. // Set Id to point to a CoTaskMemAlloc'd
  171. STDMETHODIMP CSourceStream::QueryId(__deref_out LPWSTR *Id) {
  172. CheckPointer(Id,E_POINTER);
  173. ValidateReadWritePtr(Id,sizeof(LPWSTR));
  174. // We give the pins id's which are 1,2,...
  175. // FindPinNumber returns -1 for an invalid pin
  176. int i = 1+ m_pFilter->FindPinNumber(this);
  177. if (i<1) return VFW_E_NOT_FOUND;
  178. *Id = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * 12);
  179. if (*Id==NULL) {
  180. return E_OUTOFMEMORY;
  181. }
  182. IntToWstr(i, *Id);
  183. return NOERROR;
  184. }
  185. //
  186. // CSourceStream::Constructor
  187. //
  188. // increments the number of pins present on the filter
  189. CSourceStream::CSourceStream(
  190. __in_opt LPCTSTR pObjectName,
  191. __inout HRESULT *phr,
  192. __inout CSource *ps,
  193. __in_opt LPCWSTR pPinName)
  194. : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
  195. m_pFilter(ps) {
  196. *phr = m_pFilter->AddPin(this);
  197. }
  198. #ifdef UNICODE
  199. CSourceStream::CSourceStream(
  200. __in_opt LPCSTR pObjectName,
  201. __inout HRESULT *phr,
  202. __inout CSource *ps,
  203. __in_opt LPCWSTR pPinName)
  204. : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
  205. m_pFilter(ps) {
  206. *phr = m_pFilter->AddPin(this);
  207. }
  208. #endif
  209. //
  210. // CSourceStream::Destructor
  211. //
  212. // Decrements the number of pins on this filter
  213. CSourceStream::~CSourceStream(void) {
  214. m_pFilter->RemovePin(this);
  215. }
  216. //
  217. // CheckMediaType
  218. //
  219. // Do we support this type? Provides the default support for 1 type.
  220. HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
  221. CAutoLock lock(m_pFilter->pStateLock());
  222. CMediaType mt;
  223. GetMediaType(&mt);
  224. if (mt == *pMediaType) {
  225. return NOERROR;
  226. }
  227. return E_FAIL;
  228. }
  229. //
  230. // GetMediaType/3
  231. //
  232. // By default we support only one type
  233. // iPosition indexes are 0-n
  234. HRESULT CSourceStream::GetMediaType(int iPosition, __inout CMediaType *pMediaType) {
  235. CAutoLock lock(m_pFilter->pStateLock());
  236. if (iPosition<0) {
  237. return E_INVALIDARG;
  238. }
  239. if (iPosition>0) {
  240. return VFW_S_NO_MORE_ITEMS;
  241. }
  242. return GetMediaType(pMediaType);
  243. }
  244. //
  245. // Active
  246. //
  247. // The pin is active - start up the worker thread
  248. HRESULT CSourceStream::Active(void) {
  249. CAutoLock lock(m_pFilter->pStateLock());
  250. HRESULT hr;
  251. if (m_pFilter->IsActive()) {
  252. return S_FALSE; // succeeded, but did not allocate resources (they already exist...)
  253. }
  254. // do nothing if not connected - its ok not to connect to
  255. // all pins of a source filter
  256. if (!IsConnected()) {
  257. return NOERROR;
  258. }
  259. hr = CBaseOutputPin::Active();
  260. if (FAILED(hr)) {
  261. return hr;
  262. }
  263. ASSERT(!ThreadExists());
  264. // start the thread
  265. if (!Create()) {
  266. return E_FAIL;
  267. }
  268. // Tell thread to initialize. If OnThreadCreate Fails, so does this.
  269. hr = Init();
  270. if (FAILED(hr))
  271. return hr;
  272. return Pause();
  273. }
  274. //
  275. // Inactive
  276. //
  277. // Pin is inactive - shut down the worker thread
  278. // Waits for the worker to exit before returning.
  279. HRESULT CSourceStream::Inactive(void) {
  280. CAutoLock lock(m_pFilter->pStateLock());
  281. HRESULT hr;
  282. // do nothing if not connected - its ok not to connect to
  283. // all pins of a source filter
  284. if (!IsConnected()) {
  285. return NOERROR;
  286. }
  287. // !!! need to do this before trying to stop the thread, because
  288. // we may be stuck waiting for our own allocator!!!
  289. hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
  290. if (FAILED(hr)) {
  291. return hr;
  292. }
  293. if (ThreadExists()) {
  294. hr = Stop();
  295. if (FAILED(hr)) {
  296. return hr;
  297. }
  298. hr = Exit();
  299. if (FAILED(hr)) {
  300. return hr;
  301. }
  302. Close(); // Wait for the thread to exit, then tidy up.
  303. }
  304. // hr = CBaseOutputPin::Inactive(); // call this first to Decommit the allocator
  305. //if (FAILED(hr)) {
  306. // return hr;
  307. //}
  308. return NOERROR;
  309. }
  310. //
  311. // ThreadProc
  312. //
  313. // When this returns the thread exits
  314. // Return codes > 0 indicate an error occured
  315. DWORD CSourceStream::ThreadProc(void) {
  316. HRESULT hr; // the return code from calls
  317. Command com;
  318. do {
  319. com = GetRequest();
  320. if (com != CMD_INIT) {
  321. DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
  322. Reply((DWORD) E_UNEXPECTED);
  323. }
  324. } while (com != CMD_INIT);
  325. DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
  326. hr = OnThreadCreate(); // perform set up tasks
  327. if (FAILED(hr)) {
  328. DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
  329. OnThreadDestroy();
  330. Reply(hr); // send failed return code from OnThreadCreate
  331. return 1;
  332. }
  333. // Initialisation suceeded
  334. Reply(NOERROR);
  335. Command cmd;
  336. do {
  337. cmd = GetRequest();
  338. switch (cmd) {
  339. case CMD_EXIT:
  340. Reply(NOERROR);
  341. break;
  342. case CMD_RUN:
  343. DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
  344. // !!! fall through???
  345. case CMD_PAUSE:
  346. Reply(NOERROR);
  347. DoBufferProcessingLoop();
  348. break;
  349. case CMD_STOP:
  350. Reply(NOERROR);
  351. break;
  352. default:
  353. DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
  354. Reply((DWORD) E_NOTIMPL);
  355. break;
  356. }
  357. } while (cmd != CMD_EXIT);
  358. hr = OnThreadDestroy(); // tidy up.
  359. if (FAILED(hr)) {
  360. DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
  361. return 1;
  362. }
  363. DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
  364. return 0;
  365. }
  366. //
  367. // DoBufferProcessingLoop
  368. //
  369. // Grabs a buffer and calls the users processing function.
  370. // Overridable, so that different delivery styles can be catered for.
  371. HRESULT CSourceStream::DoBufferProcessingLoop(void) {
  372. Command com;
  373. OnThreadStartPlay();
  374. do {
  375. while (!CheckRequest(&com)) {
  376. IMediaSample *pSample;
  377. HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
  378. if (FAILED(hr)) {
  379. Sleep(1);
  380. continue; // go round again. Perhaps the error will go away
  381. // or the allocator is decommited & we will be asked to
  382. // exit soon.
  383. }
  384. // Virtual function user will override.
  385. hr = FillBuffer(pSample);
  386. if (hr == S_OK) {
  387. hr = Deliver(pSample);
  388. pSample->Release();
  389. // downstream filter returns S_FALSE if it wants us to
  390. // stop or an error if it's reporting an error.
  391. if(hr != S_OK)
  392. {
  393. DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
  394. return S_OK;
  395. }
  396. } else if (hr == S_FALSE) {
  397. // derived class wants us to stop pushing data
  398. pSample->Release();
  399. DeliverEndOfStream();
  400. return S_OK;
  401. } else {
  402. // derived class encountered an error
  403. pSample->Release();
  404. DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
  405. DeliverEndOfStream();
  406. m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
  407. return hr;
  408. }
  409. // all paths release the sample
  410. }
  411. // For all commands sent to us there must be a Reply call!
  412. if (com == CMD_RUN || com == CMD_PAUSE) {
  413. Reply(NOERROR);
  414. } else if (com != CMD_STOP) {
  415. Reply((DWORD) E_UNEXPECTED);
  416. DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
  417. }
  418. } while (com != CMD_STOP);
  419. return S_FALSE;
  420. }