plugin_win_mf_producer_video.cxx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /* Copyright (C) 2013-2015 Mamadou DIOP
  2. * Copyright (C) 2013-2015 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 "internals/mf_display_watcher.h"
  24. #include "internals/mf_custom_src.h"
  25. #include "internals/mf_codec.h"
  26. #include "tinymedia/tmedia_defaults.h"
  27. #include "tinymedia/tmedia_producer.h"
  28. #include "tsk_string.h"
  29. #include "tsk_thread.h"
  30. #include "tsk_debug.h"
  31. #include <KS.h>
  32. #include <Codecapi.h>
  33. #include <assert.h>
  34. #include <stdlib.h> /* mbstowcs, wchar_t(C) */
  35. #include <initguid.h>
  36. // 0: {{[Source] -> (VideoProcessor) -> SampleGrabber}} , {{[Encoder]}} -> RTP
  37. // 1: {{[Source] -> (VideoProcessor) -> [Encoder] -> SampleGrabber}} -> RTP
  38. // (VideoProcessor) is optional
  39. // "{{" and "}}" defines where the graph starts and ends respectively. For "0", [Decoder] is a stand-alone IMFTransform.
  40. #if !defined(PLUGIN_MF_PV_BUNDLE_CODEC)
  41. # define PLUGIN_MF_PV_BUNDLE_CODEC 1 /* MUST be "1" when the encoder is an Async Transform (e.g. Intel Quick Sync). Use "1" to be sure is will always work. */
  42. #endif /* PLUGIN_MF_PV_BUNDLE_CODEC */
  43. #if !defined(PLUGIN_MF_GOP_SIZE_IN_SECONDS)
  44. #define PLUGIN_MF_GOP_SIZE_IN_SECONDS 60
  45. #endif /* PLUGIN_MF_GOP_SIZE_IN_SECONDS */
  46. DEFINE_GUID(PLUGIN_MF_LOW_LATENCY,
  47. 0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee);
  48. extern const tmedia_codec_plugin_def_t *mf_codec_h264_main_plugin_def_t;
  49. extern const tmedia_codec_plugin_def_t *mf_codec_h264_base_plugin_def_t;
  50. static void* TSK_STDCALL RunSessionThread(void *pArg);
  51. static int _plugin_win_mf_producer_video_unprepare(struct plugin_win_mf_producer_video_s* pSelf);
  52. //
  53. // plugin_win_mf_producer_video_t
  54. //
  55. typedef struct plugin_win_mf_producer_video_s {
  56. TMEDIA_DECLARE_PRODUCER;
  57. bool bStarted, bPrepared, bMuted;
  58. tsk_thread_handle_t* ppTread[1];
  59. HWND hWndPreview;
  60. int32_t bitrate_bps; // used when encoder bundled only
  61. DeviceListVideo* pDeviceList;
  62. MFCodecVideo *pEncoder;
  63. IMFMediaSession *pSession;
  64. IMFMediaSource *pSource;
  65. SampleGrabberCB *pCallback;
  66. IMFActivate *pSinkGrabber;
  67. IMFActivate *pSinkActivatePreview;
  68. DisplayWatcher* pWatcherPreview;
  69. IMFTopology *pTopology;
  70. IMFMediaType *pGrabberInputType;
  71. }
  72. plugin_win_mf_producer_video_t;
  73. /* ============ Video MF Producer Interface ================= */
  74. static int plugin_win_mf_producer_video_set(tmedia_producer_t *self, const tmedia_param_t* param)
  75. {
  76. int ret = 0;
  77. HRESULT hr = S_OK;
  78. plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
  79. if (!pSelf || !param) {
  80. TSK_DEBUG_ERROR("Invalid parameter");
  81. return -1;
  82. }
  83. if (tsk_striequals(param->key, "action")) {
  84. tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
  85. HRESULT hr = S_OK;
  86. switch (action) {
  87. case tmedia_codec_action_encode_idr: {
  88. if (pSelf->pEncoder) {
  89. CHECK_HR(hr = pSelf->pEncoder->RequestKeyFrame());
  90. }
  91. break;
  92. }
  93. case tmedia_codec_action_bw_down: {
  94. pSelf->bitrate_bps = TSK_CLAMP(0, (int32_t)((pSelf->bitrate_bps << 1) / 3), TMEDIA_CODEC(pSelf)->bandwidth_max_upload);
  95. TSK_DEBUG_INFO("New target bitrate = %d kbps", pSelf->bitrate_bps);
  96. if (pSelf->pEncoder) {
  97. CHECK_HR(hr = pSelf->pEncoder->SetBitRate(pSelf->bitrate_bps));
  98. }
  99. break;
  100. }
  101. case tmedia_codec_action_bw_up: {
  102. pSelf->bitrate_bps = TSK_CLAMP(0, (int32_t)((pSelf->bitrate_bps * 3) >> 1), TMEDIA_CODEC(pSelf)->bandwidth_max_upload);
  103. TSK_DEBUG_INFO("New target bitrate = %d kbps", pSelf->bitrate_bps);
  104. if (pSelf->pEncoder) {
  105. CHECK_HR(hr = pSelf->pEncoder->SetBitRate(pSelf->bitrate_bps));
  106. }
  107. break;
  108. }
  109. }
  110. }
  111. else if (param->value_type == tmedia_pvt_int64) {
  112. if (tsk_striequals(param->key, "local-hwnd")) {
  113. HWND hWnd = reinterpret_cast<HWND>((INT64)*((int64_t*)param->value));
  114. if (hWnd != pSelf->hWndPreview) {
  115. pSelf->hWndPreview = hWnd;
  116. if (pSelf->pWatcherPreview) {
  117. CHECK_HR(hr = pSelf->pWatcherPreview->SetHwnd(hWnd));
  118. }
  119. }
  120. }
  121. }
  122. else if (param->value_type == tmedia_pvt_int32) {
  123. if (tsk_striequals(param->key, "mute")) {
  124. pSelf->bMuted = (TSK_TO_INT32((uint8_t*)param->value) != 0);
  125. if (pSelf->pCallback) {
  126. pSelf->pCallback->SetMute(pSelf->bMuted);
  127. }
  128. #if 0
  129. if (pSelf->bStarted && pSelf->pSession) {
  130. if (pSelf->bMuted) {
  131. pSelf->pSession->Pause();
  132. }
  133. else {
  134. CHECK_HR(hr = MFUtils::RunSession(pSelf->pSession, pSelf->pTopology));
  135. }
  136. }
  137. #endif
  138. }
  139. else if (tsk_striequals(param->key, "create-on-current-thead")) {
  140. //producer->create_on_ui_thread = *((int32_t*)param->value) ? tsk_false : tsk_true;
  141. }
  142. else if (tsk_striequals(param->key, "plugin-firefox")) {
  143. //producer->plugin_firefox = (*((int32_t*)param->value) != 0);
  144. //if(producer->grabber){
  145. // producer->grabber->setPluginFirefox((producer->plugin_firefox == tsk_true));
  146. //}
  147. }
  148. }
  149. bail:
  150. return SUCCEEDED(hr) ? 0 : -1;
  151. }
  152. static int plugin_win_mf_producer_video_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec)
  153. {
  154. plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
  155. if (!pSelf || !codec && codec->plugin) {
  156. TSK_DEBUG_ERROR("Invalid parameter");
  157. return -1;
  158. }
  159. if (pSelf->bPrepared) {
  160. TSK_DEBUG_WARN("MF video producer already prepared");
  161. return -1;
  162. }
  163. // FIXME: DirectShow requires flipping but not MF
  164. // The Core library always tries to flip when OSType==Win32. Must be changed
  165. TMEDIA_CODEC_VIDEO(codec)->out.flip = tsk_false;
  166. TMEDIA_PRODUCER(pSelf)->video.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps;
  167. TMEDIA_PRODUCER(pSelf)->video.width = TMEDIA_CODEC_VIDEO(codec)->out.width;
  168. TMEDIA_PRODUCER(pSelf)->video.height = TMEDIA_CODEC_VIDEO(codec)->out.height;
  169. TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input
  170. TSK_DEBUG_INFO("MF video producer: fps=%d, width=%d, height=%d",
  171. TMEDIA_PRODUCER(pSelf)->video.fps,
  172. TMEDIA_PRODUCER(pSelf)->video.width,
  173. TMEDIA_PRODUCER(pSelf)->video.height);
  174. HRESULT hr = S_OK;
  175. IMFAttributes* pSessionAttributes = NULL;
  176. IMFTopology *pTopology = NULL;
  177. IMFMediaSink* pEvr = NULL;
  178. IMFMediaType* pEncoderInputType = NULL;
  179. IMFTopologyNode *pNodeGrabber = NULL;
  180. IMFMediaType* pGrabberNegotiatedInputMedia = NULL;
  181. BOOL bVideoProcessorIsSupported = FALSE;
  182. const VideoSubTypeGuidPair *pcPreferredSubTypeGuidPair = NULL;
  183. // create device list object
  184. if (!pSelf->pDeviceList && !(pSelf->pDeviceList = new DeviceListVideo())) {
  185. TSK_DEBUG_ERROR("Failed to create device list");
  186. hr = E_OUTOFMEMORY;
  187. goto bail;
  188. }
  189. // enumerate devices
  190. hr = pSelf->pDeviceList->EnumerateDevices();
  191. if (!SUCCEEDED(hr)) {
  192. goto bail;
  193. }
  194. // check if we have at least one MF video source connected to the PC
  195. if (pSelf->pDeviceList->Count() == 0) {
  196. TSK_DEBUG_WARN("No MF video source could be found...no video will be sent");
  197. // do not break the negotiation as one-way video connection is a valid use-case
  198. }
  199. else {
  200. // Get best MF video source
  201. IMFActivate* pActivate = NULL;
  202. const char* pczSrcFriendlyName = tmedia_producer_get_friendly_name(tmedia_video);
  203. if (!tsk_strnullORempty(pczSrcFriendlyName)) {
  204. TSK_DEBUG_INFO("MF pref. video source = %s", pczSrcFriendlyName);
  205. wchar_t pczwSrcFriendlyName[MAX_PATH] = { 0 };
  206. mbstowcs(pczwSrcFriendlyName, pczSrcFriendlyName, sizeof(pczwSrcFriendlyName) / sizeof(pczwSrcFriendlyName[0]));
  207. hr = pSelf->pDeviceList->GetDeviceBest(&pActivate, pczwSrcFriendlyName);
  208. }
  209. else {
  210. hr = pSelf->pDeviceList->GetDeviceBest(&pActivate);
  211. }
  212. if (!SUCCEEDED(hr) || !pActivate) {
  213. TSK_DEBUG_ERROR("Failed to get best MF video source");
  214. if (!pActivate) {
  215. hr = E_OUTOFMEMORY;
  216. }
  217. goto bail;
  218. }
  219. // Create the media source for the device.
  220. hr = pActivate->ActivateObject(
  221. __uuidof(IMFMediaSource),
  222. (void**)&pSelf->pSource
  223. );
  224. SafeRelease(&pActivate);
  225. if (!SUCCEEDED(hr)) {
  226. TSK_DEBUG_ERROR("ActivateObject(MF video source) failed");
  227. goto bail;
  228. }
  229. // Check whether video processor (http://msdn.microsoft.com/en-us/library/windows/desktop/hh162913(v=vs.85).aspx) is supported
  230. CHECK_HR(hr = MFUtils::IsVideoProcessorSupported(&bVideoProcessorIsSupported));
  231. // Must not be set because not supported by Frame Rate Converter DSP (http://msdn.microsoft.com/en-us/library/windows/desktop/ff819100(v=vs.85).aspx).aspx) because of color (neither I420 nor NV12)
  232. // Video Processor (http://msdn.microsoft.com/en-us/library/windows/desktop/hh162913(v=vs.85).aspx) supports both NV12 and I420
  233. if (!bVideoProcessorIsSupported) {
  234. UINT32 nWidth, nHeight, nFps;
  235. hr = MFUtils::GetBestFormat(
  236. pSelf->pSource,
  237. &MFVideoFormat_I420,
  238. (UINT32)TMEDIA_PRODUCER(pSelf)->video.width,
  239. (UINT32)TMEDIA_PRODUCER(pSelf)->video.height,
  240. (UINT32)TMEDIA_PRODUCER(pSelf)->video.fps,
  241. &nWidth,
  242. &nHeight,
  243. &nFps,
  244. &pcPreferredSubTypeGuidPair
  245. );
  246. if (SUCCEEDED(hr)) {
  247. TSK_DEBUG_INFO("Video processor not supported...using source fps=%u, width=%u, height=%u", nFps, nWidth, nHeight);
  248. TMEDIA_PRODUCER(pSelf)->video.width = nWidth;
  249. TMEDIA_PRODUCER(pSelf)->video.height = nHeight;
  250. TMEDIA_PRODUCER(pSelf)->video.fps = nFps;
  251. }
  252. }
  253. // If H.264 is negotiated for this session then, try to find hardware encoder
  254. // If no HW encoder is found will fallback to SW implementation from x264
  255. #if PLUGIN_MF_PV_BUNDLE_CODEC
  256. // Before embedding a H.264 encoder we have to be sure that:
  257. // - Low latency is supported
  258. // - The user decided to use MF encoder (Microsoft, Intel Quick Sync or any other)
  259. if ((codec->id == tmedia_codec_id_h264_bp || codec->id == tmedia_codec_id_h264_mp) && MFUtils::IsLowLatencyH264Supported()) {
  260. BOOL bMFEncoderIsRegistered =
  261. (codec->id == tmedia_codec_id_h264_mp && codec->plugin == mf_codec_h264_main_plugin_def_t)
  262. || (codec->id == tmedia_codec_id_h264_bp && codec->plugin == mf_codec_h264_base_plugin_def_t);
  263. if (bMFEncoderIsRegistered) {
  264. // both Microsoft and Intel encoders support NV12 only as input
  265. // static const BOOL kIsEncoder = TRUE;
  266. // hr = MFUtils::GetBestCodec(kIsEncoder, MFMediaType_Video, MFVideoFormat_NV12, MFVideoFormat_H264, &pSelf->pEncoder);
  267. pSelf->pEncoder = (codec->id == tmedia_codec_id_h264_bp) ? MFCodecVideoH264::CreateCodecH264Base(MFCodecType_Encoder) : MFCodecVideoH264::CreateCodecH264Main(MFCodecType_Encoder);
  268. if (pSelf->pEncoder) {
  269. pSelf->pEncoder->setBundled(TRUE);
  270. int32_t avg_bitrate_kbps = tmedia_get_video_bandwidth_kbps_2((unsigned int)TMEDIA_PRODUCER(pSelf)->video.width, (unsigned int)TMEDIA_PRODUCER(pSelf)->video.height, TMEDIA_PRODUCER(pSelf)->video.fps);
  271. TSK_DEBUG_INFO("MF_MT_AVG_BITRATE defined with value = %d kbps", avg_bitrate_kbps);
  272. pSelf->bitrate_bps = (avg_bitrate_kbps * 1024);
  273. hr = pSelf->pEncoder->Initialize(
  274. (UINT32)TMEDIA_PRODUCER(pSelf)->video.fps,
  275. (UINT32)TMEDIA_PRODUCER(pSelf)->video.width,
  276. (UINT32)TMEDIA_PRODUCER(pSelf)->video.height,
  277. (UINT32)pSelf->bitrate_bps);
  278. if (SUCCEEDED(hr)) {
  279. /*hr =*/ pSelf->pEncoder->SetGOPSize((PLUGIN_MF_GOP_SIZE_IN_SECONDS * TMEDIA_PRODUCER(pSelf)->video.fps));
  280. }
  281. if (FAILED(hr)) {
  282. SafeRelease(&pSelf->pEncoder);
  283. hr = S_OK;
  284. }
  285. }
  286. if (SUCCEEDED(hr) && pSelf->pEncoder) {
  287. TMEDIA_PRODUCER(pSelf)->encoder.codec_id = codec->id; // means encoded frames as input
  288. }
  289. else {
  290. SafeRelease(&pSelf->pEncoder);
  291. TSK_DEBUG_WARN("Failed to find H.264 HW encoder...fallback to SW implementation");
  292. }
  293. }
  294. else { /* if(!bMFEncoderIsRegistered) */
  295. TSK_DEBUG_INFO("Not bundling MF H.264 encoder even if low latency is supported because another implementation is registered: %s", codec->plugin->desc);
  296. }
  297. }
  298. #endif
  299. // Set session attributes
  300. CHECK_HR(hr = MFCreateAttributes(&pSessionAttributes, 1));
  301. CHECK_HR(hr = pSessionAttributes->SetUINT32(PLUGIN_MF_LOW_LATENCY, 1));
  302. // Configure the media type that the Sample Grabber will receive.
  303. // Setting the major and subtype is usually enough for the topology loader
  304. // to resolve the topology.
  305. CHECK_HR(hr = MFCreateMediaType(&pSelf->pGrabberInputType));
  306. CHECK_HR(hr = pSelf->pGrabberInputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
  307. CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
  308. CHECK_HR(hr = MFSetAttributeSize(pSelf->pGrabberInputType, MF_MT_FRAME_SIZE, (UINT32)TMEDIA_PRODUCER(pSelf)->video.width, (UINT32)TMEDIA_PRODUCER(pSelf)->video.height));
  309. CHECK_HR(hr = MFSetAttributeRatio(pSelf->pGrabberInputType, MF_MT_FRAME_RATE, TMEDIA_PRODUCER(pSelf)->video.fps, 1));
  310. CHECK_HR(hr = MFSetAttributeRatio(pSelf->pGrabberInputType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
  311. CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, pSelf->pEncoder ? FALSE : TRUE));
  312. CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, pSelf->pEncoder ? FALSE : TRUE));
  313. if (pSelf->pEncoder) {
  314. switch (codec->id) {
  315. case tmedia_codec_id_h264_bp:
  316. case tmedia_codec_id_h264_mp: {
  317. CHECK_HR(hr = pSelf->pGrabberInputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
  318. CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_MPEG2_PROFILE, (codec->id == tmedia_codec_id_h264_bp) ? eAVEncH264VProfile_Base : eAVEncH264VProfile_Main));
  319. CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_AVG_BITRATE, pSelf->bitrate_bps));
  320. break;
  321. }
  322. default: {
  323. TSK_DEBUG_ERROR("HW encoder with id = %d not expected", codec->id);
  324. assert(false);
  325. }
  326. }
  327. TMEDIA_PRODUCER(pSelf)->video.chroma = tmedia_chroma_nv12;
  328. TSK_DEBUG_INFO("MF video producer chroma = NV12 (because of HW encoder)");
  329. }
  330. else {
  331. // Video Processors will be inserted in the topology if the source cannot produce I420 frames
  332. // IMPORTANT: Must not be NV12 because not supported by Video Resizer DSP (http://msdn.microsoft.com/en-us/library/windows/desktop/ff819491(v=vs.85).aspx)
  333. CHECK_HR(hr = pSelf->pGrabberInputType->SetGUID(MF_MT_SUBTYPE, pcPreferredSubTypeGuidPair ? pcPreferredSubTypeGuidPair->fourcc : MFVideoFormat_I420));
  334. TMEDIA_PRODUCER(pSelf)->video.chroma = pcPreferredSubTypeGuidPair ? pcPreferredSubTypeGuidPair->chroma : tmedia_chroma_yuv420p;
  335. TSK_DEBUG_INFO("MF video producer chroma = %d", TMEDIA_PRODUCER(pSelf)->video.chroma);
  336. }
  337. if (pSelf->pEncoder) {
  338. // Unlock the encoder
  339. //BOOL bIsAsyncMFT = FALSE;
  340. //CHECK_HR(hr = MFUtils::IsAsyncMFT(pSelf->pEncoder->GetMFT(), &bIsAsyncMFT));
  341. //if(bIsAsyncMFT)
  342. //{
  343. // CHECK_HR(hr = MFUtils::UnlockAsyncMFT(pSelf->pEncoderpSelf->pEncoder->GetMFT()));
  344. //}
  345. // Apply Encoder output type (must be called before SetInputType)
  346. //CHECK_HR(hr = pSelf->pEncoder->GetMFT()->SetOutputType(0, pSelf->pGrabberInputType, 0/*MFT_SET_TYPE_TEST_ONLY*/));
  347. // HW encoders support only NV12
  348. //CHECK_HR(hr = MFUtils::ConvertVideoTypeToUncompressedType(pSelf->pGrabberInputType, MFVideoFormat_NV12, &pEncoderInputType));
  349. //CHECK_HR(hr = pSelf->pEncoder->GetMFT()->SetInputType(0, pEncoderInputType, 0/*MFT_SET_TYPE_TEST_ONLY*/));
  350. }
  351. // Create the sample grabber sink.
  352. CHECK_HR(hr = SampleGrabberCB::CreateInstance(TMEDIA_PRODUCER(pSelf), &pSelf->pCallback));
  353. CHECK_HR(hr = MFCreateSampleGrabberSinkActivate(pSelf->pGrabberInputType, pSelf->pCallback, &pSelf->pSinkGrabber));
  354. // To run as fast as possible, set this attribute (requires Windows 7):
  355. CHECK_HR(hr = pSelf->pSinkGrabber->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE));
  356. // Create the Media Session.
  357. CHECK_HR(hr = MFCreateMediaSession(pSessionAttributes, &pSelf->pSession));
  358. // Create the EVR activation object for the preview.
  359. CHECK_HR(hr = MFCreateVideoRendererActivate(pSelf->hWndPreview, &pSelf->pSinkActivatePreview));
  360. // Create the topology.
  361. CHECK_HR(hr = MFUtils::CreateTopology(
  362. pSelf->pSource,
  363. pSelf->pEncoder ? pSelf->pEncoder->GetMFT() : NULL,
  364. pSelf->pSinkGrabber,
  365. pSelf->pSinkActivatePreview,
  366. pSelf->pGrabberInputType,
  367. &pTopology));
  368. // Resolve topology (adds video processors if needed).
  369. CHECK_HR(hr = MFUtils::ResolveTopology(pTopology, &pSelf->pTopology));
  370. // Find EVR for the preview.
  371. CHECK_HR(hr = MFUtils::FindNodeObject(pSelf->pTopology, MFUtils::g_ullTopoIdSinkPreview, (void**)&pEvr));
  372. // Find negotiated media and update producer
  373. UINT32 nNegWidth = (UINT32)TMEDIA_PRODUCER(pSelf)->video.width, nNegHeight = (UINT32)TMEDIA_PRODUCER(pSelf)->video.height, nNegNumeratorFps = (UINT32)TMEDIA_PRODUCER(pSelf)->video.fps, nNegDenominatorFps = 1;
  374. CHECK_HR(hr = pSelf->pTopology->GetNodeByID(MFUtils::g_ullTopoIdSinkMain, &pNodeGrabber));
  375. CHECK_HR(hr = pNodeGrabber->GetInputPrefType(0, &pGrabberNegotiatedInputMedia));
  376. hr = MFGetAttributeSize(pGrabberNegotiatedInputMedia, MF_MT_FRAME_SIZE, &nNegWidth, &nNegHeight);
  377. if (SUCCEEDED(hr)) {
  378. TSK_DEBUG_INFO("MF video producer topology vs sdp parameters: width(%u/%u), height(%u/%u)",
  379. TMEDIA_PRODUCER(pSelf)->video.width, nNegWidth,
  380. TMEDIA_PRODUCER(pSelf)->video.height, nNegHeight
  381. );
  382. TMEDIA_PRODUCER(pSelf)->video.width = nNegWidth;
  383. TMEDIA_PRODUCER(pSelf)->video.height = nNegHeight;
  384. }
  385. hr = MFGetAttributeRatio(pGrabberNegotiatedInputMedia, MF_MT_FRAME_RATE, &nNegNumeratorFps, &nNegDenominatorFps);
  386. if (SUCCEEDED(hr)) {
  387. TSK_DEBUG_INFO("MF video producer topology vs sdp parameters: fps(%u/%u)",
  388. TMEDIA_PRODUCER(pSelf)->video.fps, (nNegNumeratorFps / nNegDenominatorFps)
  389. );
  390. TMEDIA_PRODUCER(pSelf)->video.fps = (nNegNumeratorFps / nNegDenominatorFps);
  391. }
  392. // Create EVR watcher for the preview.
  393. pSelf->pWatcherPreview = new DisplayWatcher(pSelf->hWndPreview, pEvr, hr);
  394. CHECK_HR(hr);
  395. }
  396. bail:
  397. SafeRelease(&pSessionAttributes);
  398. SafeRelease(&pTopology);
  399. SafeRelease(&pEvr);
  400. SafeRelease(&pEncoderInputType);
  401. SafeRelease(&pNodeGrabber);
  402. SafeRelease(&pGrabberNegotiatedInputMedia);
  403. pSelf->bPrepared = SUCCEEDED(hr);
  404. return pSelf->bPrepared ? 0 : -1;
  405. }
  406. static int plugin_win_mf_producer_video_start(tmedia_producer_t* self)
  407. {
  408. plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
  409. if (!pSelf) {
  410. TSK_DEBUG_ERROR("Invalid parameter");
  411. return -1;
  412. }
  413. if (pSelf->bStarted) {
  414. TSK_DEBUG_INFO("MF video producer already started");
  415. return 0;
  416. }
  417. if (!pSelf->bPrepared) {
  418. TSK_DEBUG_ERROR("MF video producer not prepared");
  419. return -1;
  420. }
  421. HRESULT hr = S_OK;
  422. // Run preview watcher
  423. if (pSelf->pWatcherPreview) {
  424. CHECK_HR(hr = pSelf->pWatcherPreview->Start());
  425. }
  426. // Run the media session.
  427. CHECK_HR(hr = MFUtils::RunSession(pSelf->pSession, pSelf->pTopology));
  428. // Start asynchronous watcher thread
  429. pSelf->bStarted = true;
  430. int ret = tsk_thread_create(&pSelf->ppTread[0], RunSessionThread, pSelf);
  431. if (ret != 0) {
  432. TSK_DEBUG_ERROR("Failed to create thread");
  433. hr = E_FAIL;
  434. pSelf->bStarted = false;
  435. if (pSelf->ppTread[0]) {
  436. tsk_thread_join(&pSelf->ppTread[0]);
  437. }
  438. MFUtils::ShutdownSession(pSelf->pSession, pSelf->pSource);
  439. goto bail;
  440. }
  441. bail:
  442. return SUCCEEDED(hr) ? 0 : -1;
  443. }
  444. static int plugin_win_mf_producer_video_pause(tmedia_producer_t* self)
  445. {
  446. plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
  447. if (!pSelf) {
  448. TSK_DEBUG_ERROR("Invalid parameter");
  449. return -1;
  450. }
  451. if (!pSelf->bStarted) {
  452. TSK_DEBUG_INFO("MF video producer not started");
  453. return 0;
  454. }
  455. HRESULT hr = MFUtils::PauseSession(pSelf->pSession);
  456. return SUCCEEDED(hr) ? 0 : -1;
  457. }
  458. static int plugin_win_mf_producer_video_stop(tmedia_producer_t* self)
  459. {
  460. plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
  461. if (!pSelf) {
  462. TSK_DEBUG_ERROR("Invalid parameter");
  463. return -1;
  464. }
  465. HRESULT hr = S_OK;
  466. if (pSelf->pWatcherPreview) {
  467. hr = pSelf->pWatcherPreview->Stop();
  468. }
  469. // for the thread
  470. pSelf->bStarted = false;
  471. hr = MFUtils::ShutdownSession(pSelf->pSession, NULL); // stop session to wakeup the asynchronous thread
  472. if (pSelf->ppTread[0]) {
  473. tsk_thread_join(&pSelf->ppTread[0]);
  474. }
  475. hr = MFUtils::ShutdownSession(NULL, pSelf->pSource); // stop source to release the camera
  476. // next start() will be called after prepare()
  477. return _plugin_win_mf_producer_video_unprepare(pSelf);
  478. }
  479. static int _plugin_win_mf_producer_video_unprepare(plugin_win_mf_producer_video_t* pSelf)
  480. {
  481. if (!pSelf) {
  482. TSK_DEBUG_ERROR("Invalid parameter");
  483. return -1;
  484. }
  485. if (pSelf->bStarted) {
  486. // plugin_win_mf_producer_video_stop(TMEDIA_PRODUCER(pSelf));
  487. TSK_DEBUG_ERROR("Producer must be stopped before calling unprepare");
  488. }
  489. if (pSelf->pDeviceList) {
  490. delete pSelf->pDeviceList, pSelf->pDeviceList = NULL;
  491. }
  492. if (pSelf->pWatcherPreview) {
  493. pSelf->pWatcherPreview->Stop();
  494. }
  495. if (pSelf->pSource) {
  496. pSelf->pSource->Shutdown();
  497. }
  498. if (pSelf->pSession) {
  499. pSelf->pSession->Shutdown();
  500. }
  501. SafeRelease(&pSelf->pEncoder);
  502. SafeRelease(&pSelf->pSession);
  503. SafeRelease(&pSelf->pSource);
  504. SafeRelease(&pSelf->pSinkActivatePreview);
  505. SafeRelease(&pSelf->pCallback);
  506. SafeRelease(&pSelf->pSinkGrabber);
  507. SafeRelease(&pSelf->pTopology);
  508. SafeRelease(&pSelf->pGrabberInputType);
  509. if (pSelf->pWatcherPreview) {
  510. delete pSelf->pWatcherPreview;
  511. pSelf->pWatcherPreview = NULL;
  512. }
  513. pSelf->bPrepared = false;
  514. return 0;
  515. }
  516. //
  517. // Windows Media Foundation video producer object definition
  518. //
  519. /* constructor */
  520. static tsk_object_t* plugin_win_mf_producer_video_ctor(tsk_object_t * self, va_list * app)
  521. {
  522. MFUtils::Startup();
  523. plugin_win_mf_producer_video_t *pSelf = (plugin_win_mf_producer_video_t *)self;
  524. if (pSelf) {
  525. /* init base */
  526. tmedia_producer_init(TMEDIA_PRODUCER(pSelf));
  527. /* init self with default values*/
  528. TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input
  529. TMEDIA_PRODUCER(pSelf)->video.chroma = tmedia_chroma_nv12;
  530. TMEDIA_PRODUCER(pSelf)->video.fps = 15;
  531. TMEDIA_PRODUCER(pSelf)->video.width = 352;
  532. TMEDIA_PRODUCER(pSelf)->video.height = 288;
  533. TSK_DEBUG_INFO("Create WinMF video producer");
  534. }
  535. return self;
  536. }
  537. /* destructor */
  538. static tsk_object_t* plugin_win_mf_producer_video_dtor(tsk_object_t * self)
  539. {
  540. plugin_win_mf_producer_video_t *pSelf = (plugin_win_mf_producer_video_t *)self;
  541. if (pSelf) {
  542. /* stop */
  543. if (pSelf->bStarted) {
  544. plugin_win_mf_producer_video_stop(TMEDIA_PRODUCER(pSelf));
  545. }
  546. /* deinit base */
  547. tmedia_producer_deinit(TMEDIA_PRODUCER(pSelf));
  548. /* deinit self */
  549. _plugin_win_mf_producer_video_unprepare(pSelf);
  550. }
  551. return self;
  552. }
  553. /* object definition */
  554. static const tsk_object_def_t plugin_win_mf_producer_video_def_s = {
  555. sizeof(plugin_win_mf_producer_video_t),
  556. plugin_win_mf_producer_video_ctor,
  557. plugin_win_mf_producer_video_dtor,
  558. tsk_null,
  559. };
  560. /* plugin definition*/
  561. static const tmedia_producer_plugin_def_t plugin_win_mf_producer_video_plugin_def_s = {
  562. &plugin_win_mf_producer_video_def_s,
  563. tmedia_video,
  564. "Microsoft Windows Media Foundation producer (Video)",
  565. plugin_win_mf_producer_video_set,
  566. plugin_win_mf_producer_video_prepare,
  567. plugin_win_mf_producer_video_start,
  568. plugin_win_mf_producer_video_pause,
  569. plugin_win_mf_producer_video_stop
  570. };
  571. const tmedia_producer_plugin_def_t *plugin_win_mf_producer_video_plugin_def_t = &plugin_win_mf_producer_video_plugin_def_s;
  572. // Run session async thread
  573. static void* TSK_STDCALL RunSessionThread(void *pArg)
  574. {
  575. plugin_win_mf_producer_video_t *pSelf = (plugin_win_mf_producer_video_t *)pArg;
  576. HRESULT hrStatus = S_OK;
  577. HRESULT hr = S_OK;
  578. IMFMediaEvent *pEvent = NULL;
  579. MediaEventType met;
  580. TSK_DEBUG_INFO("RunSessionThread (MF video producer) - ENTER");
  581. while (pSelf->bStarted) {
  582. hr = pSelf->pSession->GetEvent(0, &pEvent);
  583. if (hr == MF_E_SHUTDOWN) {
  584. if (pSelf->bStarted) {
  585. CHECK_HR(hr); // Shutdown called but "bStarted" not equal to false
  586. }
  587. break; // Shutdown called and "bStarted" is equal to false => break the loop
  588. }
  589. CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
  590. CHECK_HR(hr = pEvent->GetType(&met));
  591. if (FAILED(hrStatus) /*&& hrStatus != MF_E_NO_SAMPLE_TIMESTAMP*/) {
  592. TSK_DEBUG_ERROR("Session error: 0x%x (event id: %d)\n", hrStatus, met);
  593. hr = hrStatus;
  594. goto bail;
  595. }
  596. if (met == MESessionEnded) {
  597. break;
  598. }
  599. SafeRelease(&pEvent);
  600. }
  601. bail:
  602. TSK_DEBUG_INFO("RunSessionThread (MF video producer) - EXIT");
  603. return NULL;
  604. }