Source code of Windows XP (NT5)
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.

1153 lines
32 KiB

  1. // Copyright (c) 1997 - 1998 Microsoft Corporation. All Rights Reserved.
  2. // MMStream.cpp : Implementation of CMMStream
  3. #include "stdafx.h"
  4. #include <ddraw.h>
  5. #include "strmobjs.h"
  6. #include <strmif.h>
  7. #include <control.h>
  8. #include <uuids.h>
  9. #include <vfwmsgs.h>
  10. #include <amutil.h>
  11. #include <ddrawex.h>
  12. #include <util.h>
  13. #include "ammstrm.h"
  14. #include "amguids.h"
  15. /////////////////////////////////////////////////////////////////////////////
  16. // Utilities
  17. #if 0
  18. int
  19. WINAPI
  20. lstrcmpWInternal(
  21. LPCWSTR lpString1,
  22. LPCWSTR lpString2
  23. )
  24. {
  25. do {
  26. WCHAR c1 = *lpString1;
  27. WCHAR c2 = *lpString2;
  28. if (c1 != c2)
  29. return (int) c1 - (int) c2;
  30. } while (*lpString1++ && *lpString2++);
  31. return 0;
  32. }
  33. #endif
  34. /* Helper - get the major type of a pin */
  35. void GetMajorType(IPin *pPin, GUID *pmajortype)
  36. {
  37. IEnumMediaTypes *pEnum;
  38. *pmajortype = GUID_NULL;
  39. if (SUCCEEDED(pPin->EnumMediaTypes(&pEnum))) {
  40. AM_MEDIA_TYPE *pmt;
  41. DWORD dwReturned;
  42. if (S_OK == pEnum->Next(1, &pmt, &dwReturned)) {
  43. *pmajortype = pmt->majortype;
  44. DeleteMediaType(pmt);
  45. }
  46. pEnum->Release();
  47. }
  48. }
  49. /* Hack to try to connect all the pins of a filter up */
  50. HRESULT ConnectToAPin(IGraphBuilder *pBuilder, IPin *pThisPin)
  51. {
  52. /* Try and get the pin's type */
  53. IMediaStream *pStream;
  54. GUID majortype;
  55. GetMajorType(pThisPin, &majortype);
  56. CComPtr<IEnumFilters> pEnumFilters;
  57. HRESULT hr = pBuilder->EnumFilters(&pEnumFilters);
  58. if (FAILED(hr)) {
  59. return hr;
  60. }
  61. for (; ; ) {
  62. ULONG nFilters;
  63. CComPtr<IBaseFilter> pFilter;
  64. HRESULT hrNext = pEnumFilters->Next(1, &pFilter, &nFilters);
  65. if (hrNext == VFW_E_ENUM_OUT_OF_SYNC) {
  66. pEnumFilters->Reset();
  67. continue;
  68. }
  69. if (S_OK != hrNext) {
  70. return VFW_E_CANNOT_CONNECT;
  71. }
  72. CComPtr<IEnumPins> pEnumPins;
  73. if (S_OK != pFilter->EnumPins(&pEnumPins)) {
  74. return VFW_E_CANNOT_CONNECT;
  75. }
  76. for (; ; ) {
  77. ULONG nPins;
  78. CComPtr<IPin> pPin;
  79. if (S_OK != pEnumPins->Next(1, &pPin, &nPins)) {
  80. break;
  81. }
  82. /*
  83. Reject all pins which are not default rendered
  84. */
  85. {
  86. PIN_INFO PinInfo;
  87. pPin->QueryPinInfo(&PinInfo);
  88. if (PinInfo.pFilter) {
  89. PinInfo.pFilter->Release();
  90. }
  91. if (PinInfo.dir != PINDIR_OUTPUT || PinInfo.achName[0] == L'~') {
  92. continue;
  93. }
  94. }
  95. /* Check the type - this is a big HACK to speed things up
  96. - otherwise we bring in a ton of audio codecs to render
  97. video!
  98. */
  99. GUID majortype2;
  100. if (majortype != GUID_NULL) {
  101. GetMajorType(pPin, &majortype2);
  102. if (majortype != majortype2 && majortype2 != GUID_NULL &&
  103. majortype2 != MEDIATYPE_Stream) {
  104. }
  105. }
  106. if (SUCCEEDED(pBuilder->Connect(pPin, pThisPin))) {
  107. /* Set the latency */
  108. IAMBufferNegotiation *pNegotiate;
  109. if (SUCCEEDED(pPin->QueryInterface(IID_IAMBufferNegotiation,
  110. (void **)&pNegotiate))) {
  111. ALLOCATOR_PROPERTIES prop;
  112. prop.cBuffers = 1; /* For Audio? */
  113. prop.cbBuffer = -1;
  114. prop.cbAlign = -1;
  115. prop.cbPrefix = -1;
  116. pNegotiate->SuggestAllocatorProperties(&prop);
  117. pNegotiate->Release();
  118. }
  119. return S_OK;
  120. }
  121. }
  122. }
  123. }
  124. HRESULT ConnectFilterPins(IGraphBuilder *pBuilder, IBaseFilter *pFilter)
  125. {
  126. HRESULT hrTotal = VFW_E_CANNOT_CONNECT;
  127. /* For each pin try to connect it up - just once */
  128. CComPtr<IEnumPins> pEnumPins;
  129. HRESULT hr = pFilter->EnumPins(&pEnumPins);
  130. if (FAILED(hr)) {
  131. return S_OK;
  132. }
  133. for (; ; ) {
  134. ULONG nPins;
  135. CComPtr<IPin> pPin;
  136. if (S_OK != pEnumPins->Next(1, &pPin, &nPins)) {
  137. return hrTotal;
  138. }
  139. hr = ConnectToAPin(pBuilder, pPin);
  140. if (S_OK == hr) {
  141. hrTotal = hr;
  142. }
  143. }
  144. }
  145. /////////////////////////////////////////////////////////////////////////////
  146. // CMMStream
  147. CMMStream::CMMStream() :
  148. m_StreamTypeSet(false),
  149. m_bSeekingSet(false),
  150. m_dwInitializeFlags(0),
  151. m_StreamType(STREAMTYPE_READ),
  152. m_hEOS(NULL),
  153. m_dwIDispSafety(0),
  154. m_dwIPropBagSafety(0),
  155. m_AudioState(Disabled),
  156. m_VideoState(Disabled),
  157. m_MMStreamState(STREAMSTATE_STOP)
  158. {
  159. }
  160. /* Create things here */
  161. HRESULT CMMStream::FinalConstruct()
  162. {
  163. HRESULT hr = _BaseClass::FinalConstruct();
  164. if (FAILED(hr)) {
  165. return hr;
  166. }
  167. hr = CoCreateInstance(
  168. CLSID_MediaStreamFilter,
  169. NULL,
  170. CLSCTX_INPROC_SERVER,
  171. IID_IMediaStreamFilter,
  172. (void **)&m_pMediaStreamFilter
  173. );
  174. EXECUTE_ASSERT(SUCCEEDED(m_pMediaStreamFilter->QueryInterface(
  175. IID_IBaseFilter,
  176. (void **)&m_pBaseFilter)));
  177. return S_OK;
  178. }
  179. // IAMMMStream
  180. STDMETHODIMP CMMStream::Initialize(
  181. STREAM_TYPE StreamType,
  182. DWORD dwFlags,
  183. IGraphBuilder *pFilterGraph
  184. )
  185. {
  186. TRACEINTERFACE(_T("IMultiMediaStream::Initialize(%d, 0x%8.8X, 0x%8.8X)\n"),
  187. StreamType, dwFlags, pFilterGraph);
  188. AUTO_CRIT_LOCK;
  189. if (m_StreamTypeSet && StreamType != m_StreamType ||
  190. (0 != (dwFlags & ~AMMSF_NOGRAPHTHREAD))) {
  191. return E_INVALIDARG;
  192. }
  193. if (m_pGraphBuilder && pFilterGraph != NULL) {
  194. return E_INVALIDARG;
  195. }
  196. m_dwInitializeFlags = dwFlags;
  197. if (pFilterGraph) {
  198. m_pGraphBuilder = pFilterGraph;
  199. CompleteAddGraph();
  200. }
  201. m_StreamTypeSet = true;
  202. m_StreamType = StreamType;
  203. return S_OK;
  204. }
  205. STDMETHODIMP CMMStream::GetFilter(
  206. IMediaStreamFilter **ppFilter
  207. )
  208. {
  209. TRACEINTERFACE(_T("IAMMultiMediaStream::GetFilter(0x%8.8X)\n"), ppFilter);
  210. if (ppFilter == NULL) {
  211. return E_POINTER;
  212. }
  213. m_pMediaStreamFilter->AddRef();
  214. *ppFilter = m_pMediaStreamFilter;
  215. return S_OK;
  216. }
  217. STDMETHODIMP CMMStream::GetFilterGraph(IGraphBuilder **ppGraphBuilder)
  218. {
  219. TRACEINTERFACE(_T("AMIMultiMediaStream::GetFilterGraph(0x%8.8X)\n"), ppGraphBuilder);
  220. if (ppGraphBuilder == NULL) {
  221. return E_POINTER;
  222. }
  223. if (m_pGraphBuilder) {
  224. m_pGraphBuilder->AddRef();
  225. *ppGraphBuilder = m_pGraphBuilder;
  226. } else {
  227. *ppGraphBuilder = NULL;
  228. }
  229. return S_OK;
  230. }
  231. HRESULT CMMStream::AddDefaultRenderer(REFMSPID PurposeId, DWORD dwFlags, IMediaStream **ppNewStream)
  232. {
  233. TRACEFUNC(_T("IAMMultiMediaStream::AddDefaultRenderer(%s, 0x%8.8X, 0x%8.8X)\n"),
  234. TextFromPurposeId(PurposeId), dwFlags, ppNewStream);
  235. HRESULT hr;
  236. if (ppNewStream) {
  237. hr = E_INVALIDARG;
  238. } else {
  239. CLSID clsid;
  240. hr = GetClsidFromPurposeid(PurposeId, true, &clsid);
  241. if (SUCCEEDED(hr)) {
  242. hr = AddFilter(clsid, NULL);
  243. }
  244. }
  245. return hr;
  246. }
  247. HRESULT CMMStream::AddAMMediaStream(IAMMediaStream *pAMStream, REFMSPID PurposeId, IMediaStream **ppNewStream)
  248. {
  249. TRACEFUNC(_T("IMultiMediaStream::AddAMMediaStream(0x%8.8X, %s, 0x%8.8X)\n"),
  250. pAMStream, TextFromPurposeId(PurposeId), ppNewStream);
  251. HRESULT hr;
  252. MSPID StreamPurposeId;
  253. EXECUTE_ASSERT(SUCCEEDED(pAMStream->GetInformation(&StreamPurposeId, NULL)));
  254. if (PurposeId != StreamPurposeId) {
  255. hr = MS_E_PURPOSEID;
  256. } else {
  257. hr = pAMStream->JoinAMMultiMediaStream(this);
  258. if (SUCCEEDED(hr)) {
  259. hr = m_pMediaStreamFilter->AddMediaStream(pAMStream);
  260. if (SUCCEEDED(hr)) {
  261. if (ppNewStream) {
  262. pAMStream->AddRef();
  263. *ppNewStream = pAMStream;
  264. }
  265. } else {
  266. pAMStream->JoinAMMultiMediaStream(NULL);
  267. }
  268. }
  269. }
  270. return hr;
  271. }
  272. HRESULT CMMStream::AddDefaultStream(
  273. IUnknown *pStreamObject,
  274. DWORD dwFlags,
  275. REFMSPID PurposeId,
  276. IMediaStream **ppNewStream)
  277. {
  278. TRACEFUNC(_T("IMultiMediaStream::AddDefaultStream(0x%8.8X, %8.8X, %s, 0x%8.8X)\n"),
  279. pStreamObject, dwFlags, TextFromPurposeId(PurposeId), ppNewStream);
  280. CLSID clsidStreamObject;
  281. HRESULT hr = GetClsidFromPurposeid(PurposeId, false, &clsidStreamObject);
  282. if (SUCCEEDED(hr)) {
  283. IAMMediaStream *pMediaStream;
  284. hr = CoCreateInstance(
  285. clsidStreamObject,
  286. NULL,
  287. CLSCTX_INPROC_SERVER,
  288. IID_IAMMediaStream,
  289. (void **)&pMediaStream
  290. );
  291. if (SUCCEEDED(hr)) {
  292. hr = pMediaStream->Initialize(pStreamObject, dwFlags, PurposeId, m_StreamType);
  293. if (SUCCEEDED(hr)) {
  294. hr = AddAMMediaStream(pMediaStream, PurposeId, ppNewStream);
  295. }
  296. pMediaStream->Release();
  297. }
  298. }
  299. return hr;
  300. }
  301. //
  302. // This call can take on various flavors depending upon the flags and the pStreamObject
  303. // passed to it. Basically the algorithm is:
  304. //
  305. // If flag "add default renderer" then
  306. // Add renderer associated with purpose ID
  307. // else
  308. // If flag "add peer" then
  309. // (pStreamObject must be an IMediaStream object)
  310. // if PurposeId != NULL then
  311. // Create default stream for purpose ID
  312. // else
  313. // Get purpose ID of pStreamObject
  314. // Create default stream for that purpose ID
  315. // Initialize new stream
  316. // else
  317. // If pStreamObject is an IAMMediaStream then
  318. // Add it to our media stream
  319. // else
  320. // Attempt to create an IAMMediaStream by looking up the purpose ID
  321. //
  322. //
  323. STDMETHODIMP CMMStream::AddMediaStream(
  324. IUnknown *pStreamObject,
  325. const MSPID *pOptionalPurposeId,
  326. DWORD dwFlags,
  327. IMediaStream **ppNewStream
  328. )
  329. {
  330. if (dwFlags & ~(AMMSF_ADDDEFAULTRENDERER |
  331. AMMSF_CREATEPEER |
  332. AMMSF_STOPIFNOSAMPLES |
  333. AMMSF_NOSTALL)
  334. ) {
  335. return E_INVALIDARG;
  336. }
  337. TRACEINTERFACE(_T("IAMMultiMediaStream::AddMediaStream(0x%8.8X, %s, %8.8X, 0x%8.8X)\n"),
  338. pStreamObject,
  339. pOptionalPurposeId ? TextFromPurposeId(*pOptionalPurposeId) : _T("NULL"),
  340. dwFlags, ppNewStream);
  341. AUTO_CRIT_LOCK;
  342. if (ppNewStream) {
  343. *ppNewStream = NULL;
  344. }
  345. HRESULT hr = CheckGraph();
  346. CComPtr<IMediaStream> pSourceMediaStream;
  347. CComPtr<IAMMediaStream> pSourceAMMediaStream;
  348. if (pStreamObject) {
  349. pStreamObject->QueryInterface(IID_IMediaStream, (void **)&pSourceMediaStream);
  350. pStreamObject->QueryInterface(IID_IAMMediaStream, (void **)&pSourceAMMediaStream);
  351. }
  352. if (SUCCEEDED(hr)) {
  353. MSPID PurposeId;
  354. if (pOptionalPurposeId) {
  355. PurposeId = *pOptionalPurposeId;
  356. } else {
  357. hr = pSourceMediaStream ? pSourceMediaStream->GetInformation(&PurposeId, NULL) : E_INVALIDARG;
  358. }
  359. if (SUCCEEDED(hr)) {
  360. if (dwFlags & AMMSF_ADDDEFAULTRENDERER) {
  361. hr = AddDefaultRenderer(PurposeId, dwFlags, ppNewStream);
  362. } else {
  363. if (pSourceAMMediaStream && ((dwFlags & AMMSF_CREATEPEER) == 0)) {
  364. hr = AddAMMediaStream(pSourceAMMediaStream, PurposeId, ppNewStream);
  365. } else {
  366. hr = AddDefaultStream(pStreamObject, dwFlags, PurposeId, ppNewStream);
  367. }
  368. }
  369. }
  370. }
  371. return hr;
  372. }
  373. // Note that backout in this area is extemely tricky
  374. STDMETHODIMP CMMStream::OpenFile(
  375. LPCWSTR pszFileName,
  376. DWORD dwFlags
  377. )
  378. {
  379. TRACEINTERFACE(_T("IMultiMediaStream::OpenFile(%ls, 0x%8.8X)\n"),
  380. pszFileName, dwFlags);
  381. if (m_StreamType == STREAMTYPE_WRITE) {
  382. return MS_E_INVALIDSTREAMTYPE;
  383. }
  384. if (pszFileName == NULL) {
  385. return E_POINTER;
  386. }
  387. AUTO_CRIT_LOCK;
  388. HRESULT hr = CheckGraph();
  389. if (FAILED(hr)) {
  390. return hr;
  391. }
  392. CComPtr<IBaseFilter> pSource;
  393. hr = m_pGraphBuilder->AddSourceFilter(
  394. pszFileName,
  395. L"Source",
  396. &pSource
  397. );
  398. if (SUCCEEDED(hr)) {
  399. hr = CompleteOpen(pSource, dwFlags);
  400. if (FAILED(hr)) {
  401. m_pGraphBuilder->RemoveFilter(pSource);
  402. }
  403. }
  404. return hr;
  405. }
  406. STDMETHODIMP CMMStream::OpenMoniker(
  407. IBindCtx *pCtx,
  408. IMoniker *pMoniker,
  409. DWORD dwFlags
  410. )
  411. {
  412. TRACEINTERFACE(_T("IMultiMediaStream::OpenMoniker(0x%8.8X, 0x%8.8X, 0x%8.8X)\n"),
  413. pCtx, pMoniker, dwFlags);
  414. if (m_StreamType == STREAMTYPE_WRITE) {
  415. return MS_E_INVALIDSTREAMTYPE;
  416. }
  417. AUTO_CRIT_LOCK;
  418. HRESULT hr = CheckGraph();
  419. if (FAILED(hr)) {
  420. return hr;
  421. }
  422. IFilterGraph2 *pGraph2;
  423. hr = m_pGraphBuilder->QueryInterface(
  424. IID_IFilterGraph2,
  425. (void **)&pGraph2);
  426. if (FAILED(hr)) {
  427. return hr;
  428. }
  429. CComPtr<IBaseFilter> pFilter;
  430. hr = pGraph2->AddSourceFilterForMoniker(
  431. pMoniker,
  432. pCtx,
  433. L"Source",
  434. &pFilter);
  435. pGraph2->Release();
  436. if (FAILED(hr)) {
  437. return hr;
  438. }
  439. if (SUCCEEDED(hr)) {
  440. hr = CompleteOpen(pFilter, dwFlags);
  441. if (FAILED(hr)) {
  442. m_pGraphBuilder->RemoveFilter(pFilter);
  443. }
  444. }
  445. return hr;
  446. }
  447. STDMETHODIMP CMMStream::Render(
  448. DWORD dwFlags
  449. )
  450. {
  451. TRACEINTERFACE(_T("IMultiMediaStream::Render(0x%8.8X)\n"),
  452. dwFlags);
  453. if ((dwFlags & ~AMMSF_NOCLOCK) && m_StreamType == STREAMTYPE_WRITE) {
  454. return E_INVALIDARG;
  455. }
  456. AUTO_CRIT_LOCK;
  457. HRESULT hr = CheckGraph();
  458. if (FAILED(hr)) {
  459. return hr;
  460. }
  461. if (m_StreamType == STREAMTYPE_READ) {
  462. /* Render all the filters we can find */
  463. IEnumFilters *pEnum;
  464. HRESULT hr = m_pGraphBuilder->EnumFilters(&pEnum);
  465. if (SUCCEEDED(hr)) {
  466. hr = VFW_E_CANNOT_CONNECT;
  467. IBaseFilter *pFilter;
  468. ULONG nFilters;
  469. for (; ; ) {
  470. HRESULT hrNext = pEnum->Next(1, &pFilter, &nFilters);
  471. if (hrNext == VFW_E_ENUM_OUT_OF_SYNC) {
  472. pEnum->Reset();
  473. } else if (hrNext == S_OK) {
  474. if (S_OK == CompleteOpen(pFilter, dwFlags)) {
  475. hr = S_OK;
  476. }
  477. pFilter->Release();
  478. } else {
  479. break;
  480. }
  481. }
  482. pEnum->Release();
  483. }
  484. return hr;
  485. }
  486. for (int iStream = 0; ; iStream++) {
  487. CComPtr<IMediaStream> pStream;
  488. hr = EnumMediaStreams(iStream, &pStream);
  489. if (S_OK == hr) {
  490. CComQIPtr <IPin, &IID_IPin> pOutputPin(pStream);
  491. if (pOutputPin) {
  492. // Some streams may already have been rendered
  493. IPin *pConnected;
  494. if (SUCCEEDED(pOutputPin->ConnectedTo(&pConnected))) {
  495. pConnected->Release();
  496. } else {
  497. hr = m_pGraphBuilder->Render(pOutputPin);
  498. }
  499. if (FAILED(hr)) {
  500. // Kind of difficult to back out!
  501. return hr;
  502. }
  503. }
  504. } else {
  505. hr = S_OK;
  506. break;
  507. }
  508. }
  509. if (dwFlags & AMMSF_NOCLOCK) {
  510. CComPtr<IMediaFilter> pMediaFilter;
  511. EXECUTE_ASSERT(SUCCEEDED(m_pGraphBuilder->QueryInterface(
  512. IID_IMediaFilter, (void **)&pMediaFilter)));
  513. EXECUTE_ASSERT(SUCCEEDED(pMediaFilter->SetSyncSource(NULL)));
  514. }
  515. return hr;
  516. }
  517. // IMultiMediaStream
  518. STDMETHODIMP CMMStream::GetInformation(
  519. DWORD *pdwFlags,
  520. STREAM_TYPE *pStreamType
  521. )
  522. {
  523. TRACEINTERFACE(_T("IMultiMediaStream::GetInformation(0x%8.8X, 0x%8.8X)\n"),
  524. pdwFlags, pStreamType);
  525. AUTO_CRIT_LOCK;
  526. if (m_pGraphBuilder == NULL) {
  527. return E_UNEXPECTED;
  528. }
  529. DWORD dwFlags = MMSSF_ASYNCHRONOUS;
  530. IReferenceClock *pClock;
  531. IMediaFilter *pGraphFilter;
  532. if (SUCCEEDED(m_pGraphBuilder->QueryInterface(
  533. IID_IMediaFilter, (void **)&pGraphFilter))) {
  534. if (S_OK == pGraphFilter->GetSyncSource(&pClock)) {
  535. if (pClock) {
  536. pClock->Release();
  537. }
  538. dwFlags |= MMSSF_HASCLOCK;
  539. }
  540. pGraphFilter->Release();
  541. }
  542. if (m_pMediaSeeking) {
  543. LONGLONG llDuration;
  544. if (S_OK == m_pMediaSeeking->GetDuration(&llDuration)) {
  545. dwFlags |= MMSSF_SUPPORTSEEK;
  546. }
  547. }
  548. if (pdwFlags) {
  549. *pdwFlags = dwFlags;
  550. }
  551. if (pStreamType) {
  552. *pStreamType = m_StreamType;
  553. }
  554. return S_OK;
  555. }
  556. STDMETHODIMP CMMStream::GetMediaStream(
  557. REFMSPID idPurpose,
  558. IMediaStream **ppMediaStream
  559. )
  560. {
  561. TRACEINTERFACE(_T("IMultiMediaStream::GetMediaStream(%s, 0x%8.8X)\n"),
  562. TextFromPurposeId(idPurpose), ppMediaStream);
  563. return m_pMediaStreamFilter->GetMediaStream(idPurpose, ppMediaStream);
  564. }
  565. STDMETHODIMP CMMStream::EnumMediaStreams(
  566. long Index,
  567. IMediaStream **ppMediaStream
  568. )
  569. {
  570. TRACEINTERFACE(_T("IMultiMediaStream::EnumMediaStreams(%d, 0x%8.8X)\n"),
  571. Index, ppMediaStream);
  572. return m_pMediaStreamFilter->EnumMediaStreams(Index, ppMediaStream);
  573. }
  574. //
  575. // WARNING! Do NOT take the cricical section in this function since InternalUpdate
  576. // from the base sample Update() method calls this.
  577. //
  578. STDMETHODIMP CMMStream::GetState(STREAM_STATE *pCurrentState)
  579. {
  580. TRACEINTERFACE(_T("IMultiMediaStream::GetState(0x%8.8X)\n"),
  581. pCurrentState);
  582. *pCurrentState = m_MMStreamState;
  583. return S_OK;
  584. }
  585. STDMETHODIMP CMMStream::SetState(
  586. STREAM_STATE NewState
  587. )
  588. {
  589. TRACEINTERFACE(_T("IMultiMediaStream::SetState(%d)\n"),
  590. NewState);
  591. HRESULT hr;
  592. switch (NewState) {
  593. case STREAMSTATE_STOP:
  594. hr = m_pMediaControl->Stop();
  595. break;
  596. case STREAMSTATE_RUN:
  597. hr = m_pMediaControl->Pause();
  598. if (SUCCEEDED(hr)) {
  599. long state;
  600. // Wait 1 second if necessary
  601. m_pMediaControl->GetState(1000 * 10000, &state);
  602. hr = m_pMediaControl->Run();
  603. }
  604. break;
  605. default:
  606. hr = E_INVALIDARG;
  607. break;
  608. }
  609. if (SUCCEEDED(hr)) {
  610. m_MMStreamState = NewState;
  611. }
  612. return hr;
  613. }
  614. STDMETHODIMP CMMStream::GetTime(
  615. STREAM_TIME *pCurrentTime
  616. )
  617. {
  618. TRACEINTERFACE(_T("IMultiMediaStream::GetTime(0x%8.8X)\n"),
  619. pCurrentTime);
  620. // This is supposed to return the time that matches the
  621. // samples so use ISeeking
  622. HRESULT hr = E_NOTIMPL;
  623. if (m_pMediaSeeking != NULL) {
  624. REFERENCE_TIME tStop;
  625. // This can return E_NOTIMPL for non-seekable graphs
  626. hr = m_pMediaSeeking->GetPositions((REFERENCE_TIME *)pCurrentTime,
  627. &tStop);
  628. }
  629. if (hr == E_NOTIMPL) {
  630. if (m_pMediaStreamFilter) {
  631. hr = m_pMediaStreamFilter->GetCurrentStreamTime(pCurrentTime);
  632. }
  633. }
  634. return hr;
  635. }
  636. STDMETHODIMP CMMStream::GetDuration(
  637. STREAM_TIME *pDuration
  638. )
  639. {
  640. TRACEINTERFACE(_T("IMultiMediaStream::GetDuration(0x%8.8X)\n"),
  641. pDuration);
  642. SetSeeking();
  643. if (m_pMediaSeeking == NULL) {
  644. return E_NOINTERFACE;
  645. }
  646. *pDuration = 0;
  647. if (m_StreamType != STREAMTYPE_READ) {
  648. return MS_E_INVALIDSTREAMTYPE;
  649. }
  650. return SUCCEEDED(m_pMediaSeeking->GetDuration(pDuration)) ?
  651. S_OK : S_FALSE;
  652. }
  653. STDMETHODIMP CMMStream::Seek(
  654. STREAM_TIME SeekTime
  655. )
  656. {
  657. TRACEINTERFACE(_T("IMultiMediaStream::Seek(%dms)\n"),
  658. (LONG)(SeekTime / 10000));
  659. SetSeeking();
  660. if (m_pMediaSeeking == NULL) {
  661. return E_NOINTERFACE;
  662. }
  663. return m_pMediaSeeking->SetPositions(
  664. &SeekTime,
  665. AM_SEEKING_AbsolutePositioning,
  666. NULL,
  667. AM_SEEKING_NoPositioning
  668. );
  669. }
  670. STDMETHODIMP CMMStream::GetEndOfStreamEventHandle(
  671. HANDLE *phEOS
  672. )
  673. {
  674. TRACEINTERFACE(_T("IAMMultiMediaStream::GetEndOfStreamEventHandle(0x%8.8X)\n"),
  675. phEOS);
  676. if (phEOS == NULL) {
  677. return E_POINTER;
  678. }
  679. HRESULT hr = CheckGraph();
  680. if (FAILED(hr)) {
  681. return hr;
  682. }
  683. _ASSERTE(m_hEOS != NULL);
  684. *phEOS = m_hEOS;
  685. return S_OK;
  686. }
  687. STDMETHODIMP CMMStream::SetClockDelta(
  688. REFERENCE_TIME rtAdjust
  689. )
  690. {
  691. // Get the clock and see if it supports it
  692. IMediaFilter *pGraphFilter;
  693. HRESULT hr = m_pGraphBuilder->QueryInterface(
  694. IID_IMediaFilter, (void **)&pGraphFilter);
  695. if (SUCCEEDED(hr)) {
  696. IReferenceClock *pClock;
  697. if (S_OK == pGraphFilter->GetSyncSource(&pClock)) {
  698. if (pClock) {
  699. IAMClockAdjust *pAdjust;
  700. hr = pClock->QueryInterface(IID_IAMClockAdjust, (void **)&pAdjust);
  701. if (SUCCEEDED(hr)) {
  702. hr = pAdjust->SetClockDelta(rtAdjust);
  703. pAdjust->Release();
  704. }
  705. pClock->Release();
  706. }
  707. }
  708. pGraphFilter->Release();
  709. }
  710. return hr;
  711. }
  712. HRESULT CMMStream::AddFilter(REFCLSID rclsidFilter, IBaseFilter **ppFilter)
  713. {
  714. TRACEFUNC(_T("CMMStream::AddFilter(%s, 0x%8.8X)\n"),
  715. rclsidFilter, ppFilter);
  716. IBaseFilter *pFilter;
  717. _ASSERTE(m_pGraphBuilder != NULL);
  718. HRESULT hr = CoCreateInstance(
  719. rclsidFilter,
  720. NULL,
  721. CLSCTX_INPROC_SERVER,
  722. IID_IBaseFilter,
  723. (void **)&pFilter
  724. );
  725. if (FAILED(hr)) {
  726. return hr;
  727. }
  728. hr = m_pGraphBuilder->AddFilter(pFilter, NULL);
  729. m_FilterList.Add(pFilter);
  730. if (ppFilter) {
  731. *ppFilter = pFilter;
  732. }
  733. pFilter->Release();
  734. return hr;
  735. }
  736. //
  737. // Potential future work -- this could look in the registry
  738. //
  739. HRESULT CMMStream::GetClsidFromPurposeid(REFMSPID PurposeId, bool bRenderer, CLSID * pclsid)
  740. {
  741. TRACEFUNC(_T("CMMStream::GetClsidFromPurposeid(%s, %d, 0x%8.8X)\n"),
  742. TextFromPurposeId(PurposeId), bRenderer, pclsid);
  743. if (bRenderer) {
  744. if (PurposeId == MSPID_PrimaryAudio) {
  745. *pclsid = CLSID_DSoundRender;
  746. } else {
  747. return MS_E_PURPOSEID;
  748. }
  749. } else {
  750. if (PurposeId == MSPID_PrimaryVideo) {
  751. *pclsid = CLSID_AMDirectDrawStream;
  752. } else {
  753. if (PurposeId == MSPID_PrimaryAudio) {
  754. *pclsid = CLSID_AMAudioStream;
  755. } else {
  756. return MS_E_PURPOSEID;
  757. }
  758. }
  759. }
  760. return NOERROR;
  761. }
  762. void CMMStream::CompleteAddGraph()
  763. {
  764. _ASSERTE(m_pMediaSeeking == NULL);
  765. m_pGraphBuilder->QueryInterface(IID_IMediaSeeking, (void **)&m_pMediaSeeking);
  766. m_pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl);
  767. /* Add our filter ! */
  768. m_pGraphBuilder->AddFilter(m_pBaseFilter, L"MediaStreamFilter");
  769. /* Get IMediaEvent to get the event handle */
  770. IMediaEventEx *pEvent;
  771. HRESULT hr = m_pGraphBuilder->QueryInterface(IID_IMediaEventEx, (void **)&pEvent);
  772. if (SUCCEEDED(hr)) {
  773. pEvent->GetEventHandle((OAEVENT *)&m_hEOS);
  774. pEvent->SetNotifyFlags(AM_MEDIAEVENT_NONOTIFY);
  775. pEvent->Release();
  776. }
  777. }
  778. // Call this after adding the source/sink to the graph
  779. HRESULT CMMStream::CompleteOpen(IBaseFilter *pSource, DWORD dwFlags)
  780. {
  781. HRESULT hrTotal = VFW_E_CANNOT_CONNECT;
  782. {
  783. // Find the output pin
  784. CComPtr<IEnumPins> pEnumPins;
  785. HRESULT hr = pSource->EnumPins(&pEnumPins);
  786. if (FAILED(hr)) {
  787. return hr;
  788. }
  789. for ( ; ; ) {
  790. CComPtr<IPin> pPin;
  791. ULONG nPins = 0;
  792. if (S_OK != pEnumPins->Next(1, &pPin, &nPins)) {
  793. break;
  794. }
  795. _ASSERTE(nPins == 1);
  796. switch (dwFlags & AMMSF_RENDERTYPEMASK) {
  797. case AMMSF_NORENDER:
  798. break;
  799. case AMMSF_RENDERTOEXISTING:
  800. case AMMSF_RENDERALLSTREAMS:
  801. // Do it the hard way
  802. hr = ConnectFilterPins(m_pGraphBuilder, m_pBaseFilter);
  803. if (SUCCEEDED(hr)) {
  804. for (int i = 0; i < m_FilterList.Size(); i++ ) {
  805. HRESULT hr =
  806. ConnectFilterPins(m_pGraphBuilder,
  807. m_FilterList.Element(i));
  808. }
  809. }
  810. // Performance is no good with this
  811. // We need to render to existing renderers which is only in
  812. // IFilterGraph2
  813. if (FAILED(hr)) {
  814. CComQIPtr<IFilterGraph2, &IID_IFilterGraph2> pGraph2(m_pGraphBuilder);
  815. hr = pGraph2->RenderEx(
  816. pPin,
  817. (dwFlags & AMMSF_RENDERTYPEMASK) ==
  818. AMMSF_RENDERALLSTREAMS ?
  819. 0 : AM_RENDEREX_RENDERTOEXISTINGRENDERERS,
  820. NULL);
  821. }
  822. if (SUCCEEDED(hr)) {
  823. hr = S_OK;
  824. }
  825. break;
  826. }
  827. if (S_OK == hr) {
  828. hrTotal = hr;
  829. }
  830. }
  831. }
  832. if (SUCCEEDED(hrTotal)) {
  833. m_StreamTypeSet = true;
  834. if (dwFlags & AMMSF_NOCLOCK) {
  835. CComPtr<IMediaFilter> pMediaFilter;
  836. EXECUTE_ASSERT(SUCCEEDED(m_pGraphBuilder->QueryInterface(
  837. IID_IMediaFilter, (void **)&pMediaFilter)));
  838. EXECUTE_ASSERT(SUCCEEDED(pMediaFilter->SetSyncSource(NULL)));
  839. } else {
  840. // Make it have a clock now or we'll get confused later
  841. m_pGraphBuilder->SetDefaultSyncSource();
  842. }
  843. // Make sure seeking is set up
  844. SetSeeking();
  845. if (dwFlags & AMMSF_RUN) {
  846. hrTotal = SetState(STREAMSTATE_RUN);
  847. }
  848. }
  849. return hrTotal;
  850. }
  851. HRESULT CMMStream::CheckGraph()
  852. {
  853. if (m_MMStreamState != STREAMSTATE_STOP) {
  854. return MS_E_BUSY;
  855. }
  856. if (m_pGraphBuilder == NULL) {
  857. // Make our own filter graph
  858. HRESULT hr = CoCreateInstance(
  859. m_dwInitializeFlags & AMMSF_NOGRAPHTHREAD ?
  860. CLSID_FilterGraphNoThread :
  861. CLSID_FilterGraph,
  862. NULL,
  863. CLSCTX_INPROC_SERVER,
  864. IID_IGraphBuilder,
  865. (void **)&m_pGraphBuilder
  866. );
  867. if (FAILED(hr)) {
  868. return hr;
  869. }
  870. CompleteAddGraph();
  871. }
  872. return S_OK;
  873. }
  874. void CMMStream::SetSeeking()
  875. {
  876. if (!m_bSeekingSet) {
  877. if (m_StreamType != STREAMTYPE_WRITE && m_pMediaStreamFilter != NULL) {
  878. m_pMediaStreamFilter->SupportSeeking(m_StreamType == STREAMTYPE_READ);
  879. }
  880. m_bSeekingSet = TRUE;
  881. }
  882. }
  883. STDMETHODIMP CMMStream::get_FileName(BSTR *pVal)
  884. {
  885. TRACEINTERFACE(_T("IDirectShowStream::get_FileName(0x%8.8X)\n"),
  886. pVal);
  887. *pVal = m_bstrFileName.Copy();
  888. return S_OK;
  889. }
  890. STDMETHODIMP CMMStream::put_FileName(BSTR newVal)
  891. {
  892. TRACEINTERFACE(_T("IDirectShowStream::put_FileName(%ls\n"),
  893. newVal);
  894. HRESULT hr = OpenFile(newVal, 0);
  895. if (SUCCEEDED(hr)) {
  896. m_bstrFileName = newVal;
  897. }
  898. return hr;
  899. }
  900. HRESULT CMMStream::SetStreamState(REFMSPID PurposeId, OUTPUT_STATE NewState, OUTPUT_STATE * pCurVal)
  901. {
  902. HRESULT hr = S_OK;
  903. if (*pCurVal != NewState) {
  904. switch (NewState) {
  905. case Disabled:
  906. hr = E_FAIL; // Currently no way to force this
  907. break;
  908. case ReadData:
  909. hr = AddMediaStream(NULL, &PurposeId, 0, NULL);
  910. break;
  911. case RenderData:
  912. hr = AddMediaStream(NULL, &PurposeId, AMMSF_ADDDEFAULTRENDERER, NULL);
  913. break;
  914. default:
  915. hr = E_INVALIDARG;
  916. }
  917. if (SUCCEEDED(hr)) {
  918. *pCurVal = NewState;
  919. }
  920. }
  921. return hr;
  922. }
  923. STDMETHODIMP CMMStream::get_Video(OUTPUT_STATE *pVal)
  924. {
  925. TRACEINTERFACE(_T("IDirectShowStream::get_Video(0x%8.8X\n"),
  926. pVal);
  927. *pVal = m_VideoState;
  928. return S_OK;
  929. }
  930. STDMETHODIMP CMMStream::put_Video(OUTPUT_STATE newVal)
  931. {
  932. TRACEINTERFACE(_T("IDirectShowStream::put_Video(%d\n"),
  933. newVal);
  934. return SetStreamState(MSPID_PrimaryVideo, newVal, &m_VideoState);
  935. }
  936. STDMETHODIMP CMMStream::get_Audio(OUTPUT_STATE *pVal)
  937. {
  938. TRACEINTERFACE(_T("IDirectShowStream::get_Audio(0x%8.8X\n"),
  939. pVal);
  940. *pVal = m_AudioState;
  941. return S_OK;
  942. }
  943. STDMETHODIMP CMMStream::put_Audio(OUTPUT_STATE newVal)
  944. {
  945. TRACEINTERFACE(_T("IDirectShowStream::put_Audio(%d\n"),
  946. newVal);
  947. return SetStreamState(MSPID_PrimaryAudio, newVal, &m_AudioState);
  948. }
  949. //
  950. // Property bag goop
  951. //
  952. STDMETHODIMP CMMStream::GetClassID(CLSID *pClsId)
  953. {
  954. *pClsId = CLSID_AMMultiMediaStream;
  955. return S_OK;
  956. }
  957. STDMETHODIMP CMMStream::InitNew(void)
  958. {
  959. return S_OK; // initialize new property bag goop
  960. }
  961. const WCHAR g_szVideo[] = L"Video";
  962. const WCHAR g_szAudio[] = L"Audio";
  963. const WCHAR g_szFileName[] = L"FileName";
  964. STDMETHODIMP CMMStream::Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog)
  965. {
  966. TRACEINTERFACE(_T("IPersistPropertyBag::Load"));
  967. CComVariant var;
  968. if (pPropBag->Read(g_szVideo, &var, pErrorLog) == S_OK) {
  969. var.ChangeType(VT_I4);
  970. put_Video((OUTPUT_STATE)var.lVal);
  971. var.Clear();
  972. }
  973. if (pPropBag->Read(g_szAudio, &var, pErrorLog) == S_OK) {
  974. var.ChangeType(VT_I4);
  975. put_Audio((OUTPUT_STATE)var.lVal);
  976. var.Clear();
  977. }
  978. if (pPropBag->Read(g_szFileName, &var, pErrorLog) == S_OK) {
  979. var.ChangeType(VT_BSTR);
  980. put_FileName(var.bstrVal);
  981. var.Clear();
  982. }
  983. return S_OK;
  984. }
  985. STDMETHODIMP CMMStream::Save(IPropertyBag* pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
  986. {
  987. TRACEINTERFACE(_T("IPersistPropertyBag::Save"));
  988. CComVariant var(m_VideoState);
  989. HRESULT hr = pPropBag->Write(g_szVideo, &var);
  990. if (SUCCEEDED(hr)) {
  991. var = m_AudioState;
  992. hr = pPropBag->Write(g_szAudio, &var);
  993. if (SUCCEEDED(hr)) {
  994. var.ChangeType(VT_BSTR | VT_BYREF);
  995. var.bstrVal = m_bstrFileName;
  996. hr = pPropBag->Write(g_szFileName, &var);
  997. }
  998. }
  999. return hr;
  1000. }
  1001. //
  1002. // IObjectSafety
  1003. //
  1004. STDMETHODIMP CMMStream::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
  1005. {
  1006. TRACEINTERFACE(_T("IObjectSafety::GetInterfaceSafetyOptions"));
  1007. if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL) {
  1008. return E_POINTER;
  1009. }
  1010. HRESULT hr = S_OK;
  1011. *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  1012. if (riid == IID_IDispatch) {
  1013. *pdwEnabledOptions = m_dwIDispSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  1014. } else {
  1015. if (riid == IID_IPersistPropertyBag) {
  1016. *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  1017. *pdwEnabledOptions = m_dwIPropBagSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
  1018. } else {
  1019. *pdwSupportedOptions = 0;
  1020. *pdwEnabledOptions = 0;
  1021. hr = E_NOINTERFACE;
  1022. }
  1023. }
  1024. return hr;
  1025. }
  1026. STDMETHODIMP CMMStream::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
  1027. {
  1028. TRACEINTERFACE(_T("IObjectSafety::SetInterfaceSafetyOptions"));
  1029. HRESULT hr = S_OK;
  1030. if (riid == IID_IDispatch)
  1031. {
  1032. m_dwIDispSafety = dwEnabledOptions & dwOptionSetMask;
  1033. } else {
  1034. if (riid == IID_IPersistPropertyBag) {
  1035. m_dwIPropBagSafety = dwEnabledOptions & dwOptionSetMask;
  1036. } else {
  1037. hr = E_NOINTERFACE;
  1038. }
  1039. }
  1040. return hr;
  1041. }