plugin_win_mf_producer_audio.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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 "plugin_win_mf_config.h"
  20. #include "internals/mf_utils.h"
  21. #include "internals/mf_sample_grabber.h"
  22. #include "internals/mf_devices.h"
  23. #include "tinydav/audio/tdav_producer_audio.h"
  24. #include "tsk_thread.h"
  25. #include "tsk_debug.h"
  26. static void* TSK_STDCALL RunSessionThread(void *pArg);
  27. typedef struct plugin_win_mf_producer_audio_s {
  28. TDAV_DECLARE_PRODUCER_AUDIO;
  29. bool bStarted;
  30. tsk_thread_handle_t* ppTread[1];
  31. DeviceListAudio* pDeviceList;
  32. IMFMediaSession *pSession;
  33. IMFMediaSource *pSource;
  34. SampleGrabberCB *pCallback;
  35. IMFActivate *pSinkActivate;
  36. IMFTopology *pTopology;
  37. IMFMediaType *pType;
  38. }
  39. plugin_win_mf_producer_audio_t;
  40. /* ============ Media Producer Interface ================= */
  41. static int plugin_win_mf_producer_audio_set(tmedia_producer_t* self, const tmedia_param_t* param)
  42. {
  43. plugin_win_mf_producer_audio_t* pSelf = (plugin_win_mf_producer_audio_t*)self;
  44. if(param->plugin_type == tmedia_ppt_producer) {
  45. }
  46. return tdav_producer_audio_set(TDAV_PRODUCER_AUDIO(pSelf), param);
  47. }
  48. static int plugin_win_mf_producer_audio_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec)
  49. {
  50. plugin_win_mf_producer_audio_t* pSelf = (plugin_win_mf_producer_audio_t*)self;
  51. if(!pSelf || !codec) {
  52. TSK_DEBUG_ERROR("Invalid parameter");
  53. return -1;
  54. }
  55. TMEDIA_PRODUCER(pSelf)->audio.channels = TMEDIA_CODEC_CHANNELS_AUDIO_ENCODING(codec);
  56. TMEDIA_PRODUCER(pSelf)->audio.rate = TMEDIA_CODEC_RATE_ENCODING(codec);
  57. TMEDIA_PRODUCER(pSelf)->audio.ptime = TMEDIA_CODEC_PTIME_AUDIO_ENCODING(codec);
  58. TSK_DEBUG_INFO("MF audio producer: channels=%d, rate=%d, ptime=%d",
  59. TMEDIA_PRODUCER(pSelf)->audio.channels,
  60. TMEDIA_PRODUCER(pSelf)->audio.rate,
  61. TMEDIA_PRODUCER(pSelf)->audio.ptime
  62. );
  63. HRESULT hr = S_OK;
  64. // create device list object
  65. if(!pSelf->pDeviceList && !(pSelf->pDeviceList = new DeviceListAudio())) {
  66. TSK_DEBUG_ERROR("Failed to create device list");
  67. hr = E_OUTOFMEMORY;
  68. goto bail;
  69. }
  70. // enumerate devices
  71. hr = pSelf->pDeviceList->EnumerateDevices();
  72. if(!SUCCEEDED(hr)) {
  73. goto bail;
  74. }
  75. // check if we have at least one MF video source connected to the PC
  76. if(pSelf->pDeviceList->Count() == 0) {
  77. TSK_DEBUG_WARN("No MF video source could be found...no video will be sent");
  78. // do not break the negotiation as one-way video connection is a valid use-case
  79. }
  80. else {
  81. IMFActivate* pActivate = NULL;
  82. // Get best MF audio source
  83. hr = pSelf->pDeviceList->GetDeviceBest(&pActivate);
  84. if(!SUCCEEDED(hr) || !pActivate) {
  85. TSK_DEBUG_ERROR("Failed to get best MF audio source");
  86. if(!pActivate) {
  87. hr = E_OUTOFMEMORY;
  88. }
  89. goto bail;
  90. }
  91. // Create the media source for the device.
  92. hr = pActivate->ActivateObject(
  93. __uuidof(IMFMediaSource),
  94. (void**)&pSelf->pSource
  95. );
  96. SafeRelease(&pActivate);
  97. if(!SUCCEEDED(hr)) {
  98. TSK_DEBUG_ERROR("ActivateObject(MF audio source) failed");
  99. goto bail;
  100. }
  101. // Create and configure the media type
  102. CHECK_HR(hr = MFCreateMediaType(&pSelf->pType));
  103. CHECK_HR(hr = pSelf->pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
  104. CHECK_HR(hr = pSelf->pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
  105. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, TMEDIA_PRODUCER(pSelf)->audio.channels));
  106. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, TMEDIA_PRODUCER(pSelf)->audio.rate));
  107. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, TMEDIA_PRODUCER(pSelf)->audio.bits_per_sample));
  108. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE)); // because uncompressed media type
  109. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE));
  110. UINT32 nBlockAlign = TMEDIA_PRODUCER(pSelf)->audio.channels * (TMEDIA_PRODUCER(pSelf)->audio.bits_per_sample >> 3);
  111. UINT32 nAvgBytesPerSec = (nBlockAlign * TMEDIA_PRODUCER(pSelf)->audio.rate);
  112. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, nBlockAlign));
  113. CHECK_HR(hr = pSelf->pType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, nAvgBytesPerSec));
  114. // Create the sample grabber sink.
  115. CHECK_HR(hr = SampleGrabberCB::CreateInstance(TMEDIA_PRODUCER(pSelf), &pSelf->pCallback));
  116. CHECK_HR(hr = MFCreateSampleGrabberSinkActivate(pSelf->pType, pSelf->pCallback, &pSelf->pSinkActivate));
  117. // To run as fast as possible, set this attribute (requires Windows 7):
  118. CHECK_HR(hr = pSelf->pSinkActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE));
  119. // Create the Media Session.
  120. CHECK_HR(hr = MFCreateMediaSession(NULL, &pSelf->pSession));
  121. // Create the topology.
  122. CHECK_HR(hr = MFUtils::CreateTopology(pSelf->pSource, NULL/*NO ENCODER*/, pSelf->pSinkActivate, NULL/*Preview*/, pSelf->pType, &pSelf->pTopology));
  123. }
  124. bail:
  125. return SUCCEEDED(hr) ? 0 : -1;
  126. }
  127. static int plugin_win_mf_producer_audio_start(tmedia_producer_t* self)
  128. {
  129. plugin_win_mf_producer_audio_t* pSelf = (plugin_win_mf_producer_audio_t*)self;
  130. if(!pSelf) {
  131. TSK_DEBUG_ERROR("Invalid parameter");
  132. return -1;
  133. }
  134. if(pSelf->bStarted) {
  135. TSK_DEBUG_INFO("MF audio producer already started");
  136. return 0;
  137. }
  138. HRESULT hr = S_OK;
  139. // Run the media session.
  140. CHECK_HR(hr = MFUtils::RunSession(pSelf->pSession, pSelf->pTopology));
  141. // Start asynchronous watcher thread
  142. pSelf->bStarted = true;
  143. int ret = tsk_thread_create(&pSelf->ppTread[0], RunSessionThread, pSelf);
  144. if(ret != 0) {
  145. TSK_DEBUG_ERROR("Failed to create thread");
  146. hr = E_FAIL;
  147. pSelf->bStarted = false;
  148. if(pSelf->ppTread[0]) {
  149. tsk_thread_join(&pSelf->ppTread[0]);
  150. }
  151. MFUtils::ShutdownSession(pSelf->pSession, pSelf->pSource);
  152. goto bail;
  153. }
  154. bail:
  155. return SUCCEEDED(hr) ? 0 : -1;
  156. }
  157. static int plugin_win_mf_producer_audio_pause(tmedia_producer_t* self)
  158. {
  159. plugin_win_mf_producer_audio_t* pSelf = (plugin_win_mf_producer_audio_t*)self;
  160. if(!pSelf) {
  161. TSK_DEBUG_ERROR("Invalid parameter");
  162. return -1;
  163. }
  164. HRESULT hr = MFUtils::PauseSession(pSelf->pSession);
  165. return SUCCEEDED(hr) ? 0 : -1;
  166. }
  167. static int plugin_win_mf_producer_audio_stop(tmedia_producer_t* self)
  168. {
  169. plugin_win_mf_producer_audio_t* pSelf = (plugin_win_mf_producer_audio_t*)self;
  170. if(!pSelf) {
  171. TSK_DEBUG_ERROR("Invalid parameter");
  172. return -1;
  173. }
  174. HRESULT hr = S_OK;
  175. // for the thread
  176. pSelf->bStarted = false;
  177. hr = MFUtils::ShutdownSession(pSelf->pSession, NULL); // stop session to wakeup the asynchronous thread
  178. if(pSelf->ppTread[0]) {
  179. tsk_thread_join(&pSelf->ppTread[0]);
  180. }
  181. hr = MFUtils::ShutdownSession(NULL, pSelf->pSource); // stop source to release the camera
  182. return 0;
  183. }
  184. //
  185. // WaveAPI producer object definition
  186. //
  187. /* constructor */
  188. static tsk_object_t* plugin_win_mf_producer_audio_ctor(tsk_object_t * self, va_list * app)
  189. {
  190. MFUtils::Startup();
  191. plugin_win_mf_producer_audio_t *pSelf = (plugin_win_mf_producer_audio_t*)self;
  192. if(pSelf) {
  193. /* init base */
  194. tdav_producer_audio_init(TDAV_PRODUCER_AUDIO(pSelf));
  195. /* init self */
  196. }
  197. return self;
  198. }
  199. /* destructor */
  200. static tsk_object_t* plugin_win_mf_producer_audio_dtor(tsk_object_t * self)
  201. {
  202. plugin_win_mf_producer_audio_t *pSelf = (plugin_win_mf_producer_audio_t *)self;
  203. if(pSelf) {
  204. /* stop */
  205. if(pSelf->bStarted) {
  206. plugin_win_mf_producer_audio_stop(TMEDIA_PRODUCER(pSelf));
  207. }
  208. /* deinit base */
  209. tdav_producer_audio_deinit(TDAV_PRODUCER_AUDIO(pSelf));
  210. /* deinit self */
  211. if(pSelf->pDeviceList) {
  212. delete pSelf->pDeviceList, pSelf->pDeviceList = NULL;
  213. }
  214. if(pSelf->pSource) {
  215. pSelf->pSource->Shutdown();
  216. }
  217. if(pSelf->pSession) {
  218. pSelf->pSession->Shutdown();
  219. }
  220. SafeRelease(&pSelf->pSession);
  221. SafeRelease(&pSelf->pSource);
  222. SafeRelease(&pSelf->pCallback);
  223. SafeRelease(&pSelf->pSinkActivate);
  224. SafeRelease(&pSelf->pTopology);
  225. SafeRelease(&pSelf->pType);
  226. }
  227. return self;
  228. }
  229. /* object definition */
  230. static const tsk_object_def_t plugin_win_mf_producer_audio_def_s = {
  231. sizeof(plugin_win_mf_producer_audio_t),
  232. plugin_win_mf_producer_audio_ctor,
  233. plugin_win_mf_producer_audio_dtor,
  234. tdav_producer_audio_cmp,
  235. };
  236. /* plugin definition*/
  237. static const tmedia_producer_plugin_def_t plugin_win_mf_producer_audio_plugin_def_s = {
  238. &plugin_win_mf_producer_audio_def_s,
  239. tmedia_audio,
  240. "Media Foundation audio producer",
  241. plugin_win_mf_producer_audio_set,
  242. plugin_win_mf_producer_audio_prepare,
  243. plugin_win_mf_producer_audio_start,
  244. plugin_win_mf_producer_audio_pause,
  245. plugin_win_mf_producer_audio_stop
  246. };
  247. const tmedia_producer_plugin_def_t *plugin_win_mf_producer_audio_plugin_def_t = &plugin_win_mf_producer_audio_plugin_def_s;
  248. // Run session async thread
  249. static void* TSK_STDCALL RunSessionThread(void *pArg)
  250. {
  251. plugin_win_mf_producer_audio_t *pSelf = (plugin_win_mf_producer_audio_t *)pArg;
  252. HRESULT hrStatus = S_OK;
  253. HRESULT hr = S_OK;
  254. IMFMediaEvent *pEvent = NULL;
  255. MediaEventType met;
  256. TSK_DEBUG_INFO("RunSessionThread (audio) - ENTER");
  257. while(pSelf->bStarted) {
  258. CHECK_HR(hr = pSelf->pSession->GetEvent(0, &pEvent));
  259. CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
  260. CHECK_HR(hr = pEvent->GetType(&met));
  261. if (FAILED(hrStatus)) {
  262. TSK_DEBUG_ERROR("Session error: 0x%x (event id: %d)\n", hrStatus, met);
  263. hr = hrStatus;
  264. goto bail;
  265. }
  266. if (met == MESessionEnded) {
  267. break;
  268. }
  269. SafeRelease(&pEvent);
  270. }
  271. bail:
  272. TSK_DEBUG_INFO("RunSessionThread (audio) - EXIT");
  273. return NULL;
  274. }