Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 lines
12 KiB

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