mf_codec_topology.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /* Copyright (C) 2013 Mamadou DIOP
  2. * Copyright (C) 2013 Doubango Telecom <http://www.doubango.org>
  3. *
  4. * This file is part of Open Source Doubango Framework.
  5. *
  6. * DOUBANGO is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * DOUBANGO is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with DOUBANGO.
  18. */
  19. #include "mf_codec_topology.h"
  20. #include "mf_utils.h"
  21. #include "tsk_debug.h"
  22. //
  23. // MFCodecTopologySampleGrabberCB
  24. //
  25. class MFCodecTopologySampleGrabberCB : public IMFSampleGrabberSinkCallback
  26. {
  27. long m_cRef;
  28. MFCodecTopology *m_pCodecTopology;
  29. MFCodecTopologySampleGrabberCB(MFCodecTopology *pCodecTopology)
  30. : m_cRef(1) {
  31. m_pCodecTopology = pCodecTopology;
  32. m_pCodecTopology->AddRef();
  33. }
  34. virtual ~MFCodecTopologySampleGrabberCB() {
  35. SafeRelease(&m_pCodecTopology);
  36. }
  37. public:
  38. // Create a new instance of the object.
  39. static HRESULT MFCodecTopologySampleGrabberCB::CreateInstance(MFCodecTopology *pCodecTopology, MFCodecTopologySampleGrabberCB **ppCB) {
  40. *ppCB = new (std::nothrow) MFCodecTopologySampleGrabberCB(pCodecTopology);
  41. if (ppCB == NULL) {
  42. return E_OUTOFMEMORY;
  43. }
  44. return S_OK;
  45. }
  46. STDMETHODIMP MFCodecTopologySampleGrabberCB::QueryInterface(REFIID riid, void** ppv) {
  47. static const QITAB qit[] = {
  48. QITABENT(MFCodecTopologySampleGrabberCB, IMFSampleGrabberSinkCallback),
  49. QITABENT(MFCodecTopologySampleGrabberCB, IMFClockStateSink),
  50. { 0 }
  51. };
  52. return QISearch(this, qit, riid, ppv);
  53. }
  54. STDMETHODIMP_(ULONG) MFCodecTopologySampleGrabberCB::AddRef() {
  55. return InterlockedIncrement(&m_cRef);
  56. }
  57. STDMETHODIMP_(ULONG) MFCodecTopologySampleGrabberCB::Release() {
  58. ULONG cRef = InterlockedDecrement(&m_cRef);
  59. if (cRef == 0) {
  60. delete this;
  61. }
  62. return cRef;
  63. }
  64. // IMFClockStateSink methods
  65. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) {
  66. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnClockStart(%lld, %lld)", hnsSystemTime, llClockStartOffset);
  67. return S_OK;
  68. }
  69. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnClockStop(MFTIME hnsSystemTime) {
  70. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnClockStop(%lld)", hnsSystemTime);
  71. return S_OK;
  72. }
  73. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnClockPause(MFTIME hnsSystemTime) {
  74. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnClockPause(%lld)", hnsSystemTime);
  75. return S_OK;
  76. }
  77. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnClockRestart(MFTIME hnsSystemTime) {
  78. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnClockRestart(%lld)", hnsSystemTime);
  79. return S_OK;
  80. }
  81. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnClockSetRate(MFTIME hnsSystemTime, float flRate) {
  82. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnClockSetRate(%lld, %f)", hnsSystemTime, flRate);
  83. return S_OK;
  84. }
  85. // IMFSampleGrabberSink methods.
  86. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnSetPresentationClock(IMFPresentationClock* pClock) {
  87. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnSetPresentationClock");
  88. return S_OK;
  89. }
  90. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnProcessSample(
  91. REFGUID guidMajorMediaType, DWORD dwSampleFlags,
  92. LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
  93. DWORD dwSampleSize) {
  94. HRESULT hr = S_OK;
  95. IMFSample *pSample = NULL;
  96. IMFMediaBuffer* pMediaBuffer = NULL;
  97. BYTE* _pcBufferPtr = NULL;
  98. CHECK_HR(hr = MFUtils::CreateMediaSample(dwSampleSize, &pSample));
  99. CHECK_HR(hr = pSample->SetSampleTime(llSampleTime));
  100. CHECK_HR(hr = pSample->SetSampleDuration(llSampleDuration));
  101. CHECK_HR(hr = pSample->GetBufferByIndex(0, &pMediaBuffer));
  102. CHECK_HR(hr = pMediaBuffer->Lock(&_pcBufferPtr, NULL, NULL));
  103. memcpy(_pcBufferPtr, pSampleBuffer, dwSampleSize);
  104. CHECK_HR(hr = pMediaBuffer->SetCurrentLength(dwSampleSize));
  105. CHECK_HR(hr = pMediaBuffer->Unlock());
  106. m_pCodecTopology->m_SampleQueue.Queue(pSample); // thread-safe
  107. bail:
  108. SafeRelease(&pSample);
  109. SafeRelease(&pMediaBuffer);
  110. return hr;
  111. }
  112. STDMETHODIMP MFCodecTopologySampleGrabberCB::OnShutdown() {
  113. TSK_DEBUG_INFO("MFCodecTopologySampleGrabberCB::OnShutdown");
  114. return S_OK;
  115. }
  116. };
  117. //
  118. // MFCodecTopology
  119. //
  120. MFCodecTopology::MFCodecTopology(MFCodec* pCodec, HRESULT &hr)
  121. : m_nRefCount(1)
  122. , m_bInitialized(FALSE)
  123. , m_bStarted(FALSE)
  124. , m_pCodec(NULL)
  125. , m_pSource(NULL)
  126. , m_pSession(NULL)
  127. , m_pTopologyFull(NULL)
  128. , m_pTopologyPartial(NULL)
  129. , m_pOutputType(NULL)
  130. , m_pInputType(NULL)
  131. , m_pGrabberCallback(NULL)
  132. , m_pGrabberActivate(NULL)
  133. , m_pTread(NULL)
  134. {
  135. hr = S_OK;
  136. if(!pCodec) {
  137. CHECK_HR(hr = E_POINTER);
  138. }
  139. m_pCodec = pCodec;
  140. m_pCodec->AddRef();
  141. bail:
  142. ;
  143. }
  144. MFCodecTopology::~MFCodecTopology()
  145. {
  146. DeInitialize();
  147. }
  148. ULONG MFCodecTopology::AddRef()
  149. {
  150. return InterlockedIncrement(&m_nRefCount);
  151. }
  152. ULONG MFCodecTopology::Release()
  153. {
  154. ULONG uCount = InterlockedDecrement(&m_nRefCount);
  155. if (uCount == 0) {
  156. delete this;
  157. }
  158. // For thread safety, return a temporary variable.
  159. return uCount;
  160. }
  161. HRESULT MFCodecTopology::QueryInterface(REFIID iid, void** ppv)
  162. {
  163. return E_NOTIMPL;
  164. }
  165. HRESULT MFCodecTopology::Start()
  166. {
  167. HRESULT hr = S_OK;
  168. if(m_bStarted) {
  169. return S_OK;
  170. }
  171. if(!m_bInitialized) {
  172. CHECK_HR(hr = E_FAIL);
  173. }
  174. CHECK_HR(hr = MFUtils::RunSession(m_pSession, m_pTopologyFull));
  175. // Start asynchronous watcher thread
  176. m_bStarted = TRUE;
  177. int ret = tsk_thread_create(&m_pTread, MFCodecTopology::RunSessionThread, this);
  178. if(ret != 0) {
  179. TSK_DEBUG_ERROR("Failed to create thread");
  180. m_bStarted = FALSE;
  181. if(m_pTread) {
  182. tsk_thread_join(&m_pTread);
  183. }
  184. MFUtils::ShutdownSession(m_pSession, m_pSource);
  185. CHECK_HR(hr = E_FAIL);
  186. }
  187. // FIXME
  188. Sleep(2000);
  189. bail:
  190. return hr;
  191. }
  192. HRESULT MFCodecTopology::Stop()
  193. {
  194. HRESULT hr = S_OK;
  195. if(!m_bStarted) {
  196. return S_OK;
  197. }
  198. m_bStarted = FALSE;
  199. hr = MFUtils::ShutdownSession(m_pSession, NULL); // stop session to wakeup the asynchronous thread
  200. if(m_pTread) {
  201. tsk_thread_join(&m_pTread);
  202. }
  203. hr = MFUtils::ShutdownSession(NULL, m_pSource);
  204. return hr;
  205. }
  206. HRESULT MFCodecTopology::Initialize()
  207. {
  208. HRESULT hr = S_OK;
  209. IMFAttributes* pSessionAttributes = NULL;
  210. if(m_bInitialized) {
  211. CHECK_HR(hr = E_FAIL);
  212. }
  213. // Set session attributes
  214. CHECK_HR(hr = MFCreateAttributes(&pSessionAttributes, 1));
  215. CHECK_HR(hr = pSessionAttributes->SetUINT32(PLUGIN_MF_LOW_LATENCY, 1));
  216. // Get input and output type
  217. CHECK_HR(hr = m_pCodec->GetInputType(&m_pInputType));
  218. CHECK_HR(hr = m_pCodec->GetOutputType(&m_pOutputType));
  219. // Create custom source
  220. CHECK_HR(hr = CMFSource::CreateInstanceEx(IID_IMFMediaSource, (void**)&m_pSource, m_pInputType));
  221. // Create the sample grabber sink.
  222. CHECK_HR(hr = MFCodecTopologySampleGrabberCB::CreateInstance(this, &m_pGrabberCallback));
  223. CHECK_HR(hr = MFCreateSampleGrabberSinkActivate(m_pOutputType, m_pGrabberCallback, &m_pGrabberActivate));
  224. // To run as fast as possible, set this attribute (requires Windows 7 or later):
  225. CHECK_HR(hr = m_pGrabberActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE));
  226. // Create the Media Session.
  227. CHECK_HR(hr = MFCreateMediaSession(pSessionAttributes, &m_pSession));
  228. // Create the topology.
  229. CHECK_HR(hr = MFUtils::CreateTopology(
  230. m_pSource,
  231. m_pCodec->GetMFT(),
  232. m_pGrabberActivate,
  233. NULL, // no preview
  234. m_pOutputType,
  235. &m_pTopologyPartial));
  236. // Resolve topology (adds video processors if needed).
  237. CHECK_HR(hr = MFUtils::ResolveTopology(m_pTopologyPartial, &m_pTopologyFull));
  238. m_bInitialized = TRUE;
  239. bail:
  240. SafeRelease(&pSessionAttributes);
  241. if(FAILED(hr)) {
  242. DeInitialize();
  243. }
  244. return hr;
  245. }
  246. void* TSK_STDCALL MFCodecTopology::RunSessionThread(void *pArg)
  247. {
  248. MFCodecTopology *pSelf = (MFCodecTopology *)pArg;
  249. HRESULT hrStatus = S_OK;
  250. HRESULT hr = S_OK;
  251. IMFMediaEvent *pEvent = NULL;
  252. MediaEventType met;
  253. TSK_DEBUG_INFO("RunSessionThread (MFCodecTopology) - ENTER");
  254. while(pSelf->isStarted()) {
  255. CHECK_HR(hr = pSelf->m_pSession->GetEvent(0, &pEvent));
  256. CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
  257. CHECK_HR(hr = pEvent->GetType(&met));
  258. if (FAILED(hrStatus) /*&& hrStatus != MF_E_NO_SAMPLE_TIMESTAMP*/) {
  259. TSK_DEBUG_ERROR("Session error: 0x%x (event id: %d)\n", hrStatus, met);
  260. hr = hrStatus;
  261. goto bail;
  262. }
  263. if (met == MESessionEnded) {
  264. break;
  265. }
  266. SafeRelease(&pEvent);
  267. }
  268. bail:
  269. TSK_DEBUG_INFO("RunSessionThread (MFCodecTopology) - EXIT");
  270. return NULL;
  271. }
  272. HRESULT MFCodecTopology::DeInitialize()
  273. {
  274. Stop();
  275. SafeRelease(&m_pCodec);
  276. SafeRelease(&m_pSource);
  277. SafeRelease(&m_pCodec);
  278. SafeRelease(&m_pSession);
  279. SafeRelease(&m_pTopologyFull);
  280. SafeRelease(&m_pTopologyPartial);
  281. SafeRelease(&m_pOutputType);
  282. SafeRelease(&m_pInputType);
  283. SafeRelease(&m_pGrabberCallback);
  284. SafeRelease(&m_pGrabberActivate);
  285. if(m_pTread) {
  286. tsk_thread_join(&m_pTread);
  287. }
  288. m_SampleQueue.Clear();
  289. m_bInitialized = FALSE;
  290. return S_OK;
  291. }
  292. HRESULT MFCodecTopology::ProcessInput(IMFSample* pSample)
  293. {
  294. HRESULT hr = S_OK;
  295. IMFMediaBuffer* pMediaBuffer = NULL;
  296. BYTE* _pcBufferPtr = NULL;
  297. if(!pSample) {
  298. CHECK_HR(hr = E_POINTER);
  299. }
  300. if(m_pCodec->GetMediaType() != MFCodecMediaType_Video) {
  301. CHECK_HR(hr = E_NOTIMPL);
  302. }
  303. if(!m_bStarted) {
  304. CHECK_HR(hr = Start());
  305. }
  306. CHECK_HR(hr = pSample->GetBufferByIndex(0, &pMediaBuffer));
  307. DWORD dwDataLength = 0;
  308. BOOL bLocked = FALSE;
  309. CHECK_HR(hr = pMediaBuffer->GetCurrentLength(&dwDataLength));
  310. bLocked = TRUE;
  311. if(dwDataLength > 0) {
  312. CHECK_HR(hr = pMediaBuffer->Lock(&_pcBufferPtr, NULL, NULL));
  313. CHECK_HR(hr = m_pSource->CopyVideoBuffer(
  314. dynamic_cast<MFCodecVideo*>(m_pCodec)->GetWidth(),
  315. dynamic_cast<MFCodecVideo*>(m_pCodec)->GetHeight(),
  316. _pcBufferPtr, dwDataLength));
  317. }
  318. bail:
  319. if(bLocked) {
  320. pMediaBuffer->Unlock();
  321. }
  322. SafeRelease(&pMediaBuffer);
  323. return hr;
  324. }
  325. HRESULT MFCodecTopology::ProcessOutput(IMFSample **ppSample)
  326. {
  327. HRESULT hr = S_OK;
  328. if(!ppSample) {
  329. CHECK_HR(hr = E_POINTER);
  330. }
  331. if(!m_SampleQueue.IsEmpty()) {
  332. CHECK_HR(hr = m_SampleQueue.Dequeue(ppSample)); // thread-safe
  333. }
  334. bail:
  335. return hr;
  336. }
  337. //
  338. // MFCodecVideoTopology
  339. //
  340. MFCodecVideoTopology::MFCodecVideoTopology(MFCodec* pCodec, HRESULT &hr)
  341. : MFCodecTopology(pCodec, hr)
  342. , m_nWidth(0)
  343. , m_nHeight(0)
  344. {
  345. assert(pCodec->GetMediaType() == MFCodecMediaType_Video);
  346. }
  347. MFCodecVideoTopology::~MFCodecVideoTopology()
  348. {
  349. }