123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- /* Copyright (C) 2013-2015 Mamadou DIOP
- * Copyright (C) 2013-2015 Doubango Telecom <http://www.doubango.org>
- *
- * This file is part of Open Source Doubango Framework.
- *
- * DOUBANGO is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * DOUBANGO is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with DOUBANGO.
- */
- #include "plugin_win_mf_config.h"
- #include "internals/mf_utils.h"
- #include "internals/mf_sample_grabber.h"
- #include "internals/mf_devices.h"
- #include "internals/mf_display_watcher.h"
- #include "internals/mf_custom_src.h"
- #include "internals/mf_codec.h"
- #include "tinymedia/tmedia_defaults.h"
- #include "tinymedia/tmedia_producer.h"
- #include "tsk_string.h"
- #include "tsk_thread.h"
- #include "tsk_debug.h"
- #include <KS.h>
- #include <Codecapi.h>
- #include <assert.h>
- #include <stdlib.h> /* mbstowcs, wchar_t(C) */
- #include <initguid.h>
- // 0: {{[Source] -> (VideoProcessor) -> SampleGrabber}} , {{[Encoder]}} -> RTP
- // 1: {{[Source] -> (VideoProcessor) -> [Encoder] -> SampleGrabber}} -> RTP
- // (VideoProcessor) is optional
- // "{{" and "}}" defines where the graph starts and ends respectively. For "0", [Decoder] is a stand-alone IMFTransform.
- #if !defined(PLUGIN_MF_PV_BUNDLE_CODEC)
- # 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. */
- #endif /* PLUGIN_MF_PV_BUNDLE_CODEC */
- #if !defined(PLUGIN_MF_GOP_SIZE_IN_SECONDS)
- #define PLUGIN_MF_GOP_SIZE_IN_SECONDS 60
- #endif /* PLUGIN_MF_GOP_SIZE_IN_SECONDS */
- DEFINE_GUID(PLUGIN_MF_LOW_LATENCY,
- 0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee);
- extern const tmedia_codec_plugin_def_t *mf_codec_h264_main_plugin_def_t;
- extern const tmedia_codec_plugin_def_t *mf_codec_h264_base_plugin_def_t;
- static void* TSK_STDCALL RunSessionThread(void *pArg);
- static int _plugin_win_mf_producer_video_unprepare(struct plugin_win_mf_producer_video_s* pSelf);
- //
- // plugin_win_mf_producer_video_t
- //
- typedef struct plugin_win_mf_producer_video_s {
- TMEDIA_DECLARE_PRODUCER;
- bool bStarted, bPrepared, bMuted;
- tsk_thread_handle_t* ppTread[1];
- HWND hWndPreview;
- int32_t bitrate_bps; // used when encoder bundled only
- DeviceListVideo* pDeviceList;
- MFCodecVideo *pEncoder;
- IMFMediaSession *pSession;
- IMFMediaSource *pSource;
- SampleGrabberCB *pCallback;
- IMFActivate *pSinkGrabber;
- IMFActivate *pSinkActivatePreview;
- DisplayWatcher* pWatcherPreview;
- IMFTopology *pTopology;
- IMFMediaType *pGrabberInputType;
- }
- plugin_win_mf_producer_video_t;
- /* ============ Video MF Producer Interface ================= */
- static int plugin_win_mf_producer_video_set(tmedia_producer_t *self, const tmedia_param_t* param)
- {
- int ret = 0;
- HRESULT hr = S_OK;
- plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
- if (!pSelf || !param) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- if (tsk_striequals(param->key, "action")) {
- tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value);
- HRESULT hr = S_OK;
- switch (action) {
- case tmedia_codec_action_encode_idr: {
- if (pSelf->pEncoder) {
- CHECK_HR(hr = pSelf->pEncoder->RequestKeyFrame());
- }
- break;
- }
- case tmedia_codec_action_bw_down: {
- pSelf->bitrate_bps = TSK_CLAMP(0, (int32_t)((pSelf->bitrate_bps << 1) / 3), TMEDIA_CODEC(pSelf)->bandwidth_max_upload);
- TSK_DEBUG_INFO("New target bitrate = %d kbps", pSelf->bitrate_bps);
- if (pSelf->pEncoder) {
- CHECK_HR(hr = pSelf->pEncoder->SetBitRate(pSelf->bitrate_bps));
- }
- break;
- }
- case tmedia_codec_action_bw_up: {
- pSelf->bitrate_bps = TSK_CLAMP(0, (int32_t)((pSelf->bitrate_bps * 3) >> 1), TMEDIA_CODEC(pSelf)->bandwidth_max_upload);
- TSK_DEBUG_INFO("New target bitrate = %d kbps", pSelf->bitrate_bps);
- if (pSelf->pEncoder) {
- CHECK_HR(hr = pSelf->pEncoder->SetBitRate(pSelf->bitrate_bps));
- }
- break;
- }
- }
- }
- else if (param->value_type == tmedia_pvt_int64) {
- if (tsk_striequals(param->key, "local-hwnd")) {
- HWND hWnd = reinterpret_cast<HWND>((INT64)*((int64_t*)param->value));
- if (hWnd != pSelf->hWndPreview) {
- pSelf->hWndPreview = hWnd;
- if (pSelf->pWatcherPreview) {
- CHECK_HR(hr = pSelf->pWatcherPreview->SetHwnd(hWnd));
- }
- }
- }
- }
- else if (param->value_type == tmedia_pvt_int32) {
- if (tsk_striequals(param->key, "mute")) {
- pSelf->bMuted = (TSK_TO_INT32((uint8_t*)param->value) != 0);
- if (pSelf->pCallback) {
- pSelf->pCallback->SetMute(pSelf->bMuted);
- }
- #if 0
- if (pSelf->bStarted && pSelf->pSession) {
- if (pSelf->bMuted) {
- pSelf->pSession->Pause();
- }
- else {
- CHECK_HR(hr = MFUtils::RunSession(pSelf->pSession, pSelf->pTopology));
- }
- }
- #endif
- }
- else if (tsk_striequals(param->key, "create-on-current-thead")) {
- //producer->create_on_ui_thread = *((int32_t*)param->value) ? tsk_false : tsk_true;
- }
- else if (tsk_striequals(param->key, "plugin-firefox")) {
- //producer->plugin_firefox = (*((int32_t*)param->value) != 0);
- //if(producer->grabber){
- // producer->grabber->setPluginFirefox((producer->plugin_firefox == tsk_true));
- //}
- }
- }
- bail:
- return SUCCEEDED(hr) ? 0 : -1;
- }
- static int plugin_win_mf_producer_video_prepare(tmedia_producer_t* self, const tmedia_codec_t* codec)
- {
- plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
- if (!pSelf || !codec && codec->plugin) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- if (pSelf->bPrepared) {
- TSK_DEBUG_WARN("MF video producer already prepared");
- return -1;
- }
- // FIXME: DirectShow requires flipping but not MF
- // The Core library always tries to flip when OSType==Win32. Must be changed
- TMEDIA_CODEC_VIDEO(codec)->out.flip = tsk_false;
- TMEDIA_PRODUCER(pSelf)->video.fps = TMEDIA_CODEC_VIDEO(codec)->out.fps;
- TMEDIA_PRODUCER(pSelf)->video.width = TMEDIA_CODEC_VIDEO(codec)->out.width;
- TMEDIA_PRODUCER(pSelf)->video.height = TMEDIA_CODEC_VIDEO(codec)->out.height;
- TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input
- TSK_DEBUG_INFO("MF video producer: fps=%d, width=%d, height=%d",
- TMEDIA_PRODUCER(pSelf)->video.fps,
- TMEDIA_PRODUCER(pSelf)->video.width,
- TMEDIA_PRODUCER(pSelf)->video.height);
- HRESULT hr = S_OK;
- IMFAttributes* pSessionAttributes = NULL;
- IMFTopology *pTopology = NULL;
- IMFMediaSink* pEvr = NULL;
- IMFMediaType* pEncoderInputType = NULL;
- IMFTopologyNode *pNodeGrabber = NULL;
- IMFMediaType* pGrabberNegotiatedInputMedia = NULL;
- BOOL bVideoProcessorIsSupported = FALSE;
- const VideoSubTypeGuidPair *pcPreferredSubTypeGuidPair = NULL;
- // create device list object
- if (!pSelf->pDeviceList && !(pSelf->pDeviceList = new DeviceListVideo())) {
- TSK_DEBUG_ERROR("Failed to create device list");
- hr = E_OUTOFMEMORY;
- goto bail;
- }
- // enumerate devices
- hr = pSelf->pDeviceList->EnumerateDevices();
- if (!SUCCEEDED(hr)) {
- goto bail;
- }
- // check if we have at least one MF video source connected to the PC
- if (pSelf->pDeviceList->Count() == 0) {
- TSK_DEBUG_WARN("No MF video source could be found...no video will be sent");
- // do not break the negotiation as one-way video connection is a valid use-case
- }
- else {
- // Get best MF video source
- IMFActivate* pActivate = NULL;
- const char* pczSrcFriendlyName = tmedia_producer_get_friendly_name(tmedia_video);
- if (!tsk_strnullORempty(pczSrcFriendlyName)) {
- TSK_DEBUG_INFO("MF pref. video source = %s", pczSrcFriendlyName);
- wchar_t pczwSrcFriendlyName[MAX_PATH] = { 0 };
- mbstowcs(pczwSrcFriendlyName, pczSrcFriendlyName, sizeof(pczwSrcFriendlyName) / sizeof(pczwSrcFriendlyName[0]));
- hr = pSelf->pDeviceList->GetDeviceBest(&pActivate, pczwSrcFriendlyName);
- }
- else {
- hr = pSelf->pDeviceList->GetDeviceBest(&pActivate);
- }
- if (!SUCCEEDED(hr) || !pActivate) {
- TSK_DEBUG_ERROR("Failed to get best MF video source");
- if (!pActivate) {
- hr = E_OUTOFMEMORY;
- }
- goto bail;
- }
- // Create the media source for the device.
- hr = pActivate->ActivateObject(
- __uuidof(IMFMediaSource),
- (void**)&pSelf->pSource
- );
- SafeRelease(&pActivate);
- if (!SUCCEEDED(hr)) {
- TSK_DEBUG_ERROR("ActivateObject(MF video source) failed");
- goto bail;
- }
- // Check whether video processor (http://msdn.microsoft.com/en-us/library/windows/desktop/hh162913(v=vs.85).aspx) is supported
- CHECK_HR(hr = MFUtils::IsVideoProcessorSupported(&bVideoProcessorIsSupported));
- // 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)
- // Video Processor (http://msdn.microsoft.com/en-us/library/windows/desktop/hh162913(v=vs.85).aspx) supports both NV12 and I420
- if (!bVideoProcessorIsSupported) {
- UINT32 nWidth, nHeight, nFps;
- hr = MFUtils::GetBestFormat(
- pSelf->pSource,
- &MFVideoFormat_I420,
- (UINT32)TMEDIA_PRODUCER(pSelf)->video.width,
- (UINT32)TMEDIA_PRODUCER(pSelf)->video.height,
- (UINT32)TMEDIA_PRODUCER(pSelf)->video.fps,
- &nWidth,
- &nHeight,
- &nFps,
- &pcPreferredSubTypeGuidPair
- );
- if (SUCCEEDED(hr)) {
- TSK_DEBUG_INFO("Video processor not supported...using source fps=%u, width=%u, height=%u", nFps, nWidth, nHeight);
- TMEDIA_PRODUCER(pSelf)->video.width = nWidth;
- TMEDIA_PRODUCER(pSelf)->video.height = nHeight;
- TMEDIA_PRODUCER(pSelf)->video.fps = nFps;
- }
- }
- // If H.264 is negotiated for this session then, try to find hardware encoder
- // If no HW encoder is found will fallback to SW implementation from x264
- #if PLUGIN_MF_PV_BUNDLE_CODEC
- // Before embedding a H.264 encoder we have to be sure that:
- // - Low latency is supported
- // - The user decided to use MF encoder (Microsoft, Intel Quick Sync or any other)
- if ((codec->id == tmedia_codec_id_h264_bp || codec->id == tmedia_codec_id_h264_mp) && MFUtils::IsLowLatencyH264Supported()) {
- BOOL bMFEncoderIsRegistered =
- (codec->id == tmedia_codec_id_h264_mp && codec->plugin == mf_codec_h264_main_plugin_def_t)
- || (codec->id == tmedia_codec_id_h264_bp && codec->plugin == mf_codec_h264_base_plugin_def_t);
- if (bMFEncoderIsRegistered) {
- // both Microsoft and Intel encoders support NV12 only as input
- // static const BOOL kIsEncoder = TRUE;
- // hr = MFUtils::GetBestCodec(kIsEncoder, MFMediaType_Video, MFVideoFormat_NV12, MFVideoFormat_H264, &pSelf->pEncoder);
- pSelf->pEncoder = (codec->id == tmedia_codec_id_h264_bp) ? MFCodecVideoH264::CreateCodecH264Base(MFCodecType_Encoder) : MFCodecVideoH264::CreateCodecH264Main(MFCodecType_Encoder);
- if (pSelf->pEncoder) {
- pSelf->pEncoder->setBundled(TRUE);
- 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);
- TSK_DEBUG_INFO("MF_MT_AVG_BITRATE defined with value = %d kbps", avg_bitrate_kbps);
- pSelf->bitrate_bps = (avg_bitrate_kbps * 1024);
- hr = pSelf->pEncoder->Initialize(
- (UINT32)TMEDIA_PRODUCER(pSelf)->video.fps,
- (UINT32)TMEDIA_PRODUCER(pSelf)->video.width,
- (UINT32)TMEDIA_PRODUCER(pSelf)->video.height,
- (UINT32)pSelf->bitrate_bps);
- if (SUCCEEDED(hr)) {
- /*hr =*/ pSelf->pEncoder->SetGOPSize((PLUGIN_MF_GOP_SIZE_IN_SECONDS * TMEDIA_PRODUCER(pSelf)->video.fps));
- }
- if (FAILED(hr)) {
- SafeRelease(&pSelf->pEncoder);
- hr = S_OK;
- }
- }
- if (SUCCEEDED(hr) && pSelf->pEncoder) {
- TMEDIA_PRODUCER(pSelf)->encoder.codec_id = codec->id; // means encoded frames as input
- }
- else {
- SafeRelease(&pSelf->pEncoder);
- TSK_DEBUG_WARN("Failed to find H.264 HW encoder...fallback to SW implementation");
- }
- }
- else { /* if(!bMFEncoderIsRegistered) */
- TSK_DEBUG_INFO("Not bundling MF H.264 encoder even if low latency is supported because another implementation is registered: %s", codec->plugin->desc);
- }
- }
- #endif
- // Set session attributes
- CHECK_HR(hr = MFCreateAttributes(&pSessionAttributes, 1));
- CHECK_HR(hr = pSessionAttributes->SetUINT32(PLUGIN_MF_LOW_LATENCY, 1));
- // Configure the media type that the Sample Grabber will receive.
- // Setting the major and subtype is usually enough for the topology loader
- // to resolve the topology.
- CHECK_HR(hr = MFCreateMediaType(&pSelf->pGrabberInputType));
- CHECK_HR(hr = pSelf->pGrabberInputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
- CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
- CHECK_HR(hr = MFSetAttributeSize(pSelf->pGrabberInputType, MF_MT_FRAME_SIZE, (UINT32)TMEDIA_PRODUCER(pSelf)->video.width, (UINT32)TMEDIA_PRODUCER(pSelf)->video.height));
- CHECK_HR(hr = MFSetAttributeRatio(pSelf->pGrabberInputType, MF_MT_FRAME_RATE, TMEDIA_PRODUCER(pSelf)->video.fps, 1));
- CHECK_HR(hr = MFSetAttributeRatio(pSelf->pGrabberInputType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
- CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, pSelf->pEncoder ? FALSE : TRUE));
- CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, pSelf->pEncoder ? FALSE : TRUE));
- if (pSelf->pEncoder) {
- switch (codec->id) {
- case tmedia_codec_id_h264_bp:
- case tmedia_codec_id_h264_mp: {
- CHECK_HR(hr = pSelf->pGrabberInputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
- CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_MPEG2_PROFILE, (codec->id == tmedia_codec_id_h264_bp) ? eAVEncH264VProfile_Base : eAVEncH264VProfile_Main));
- CHECK_HR(hr = pSelf->pGrabberInputType->SetUINT32(MF_MT_AVG_BITRATE, pSelf->bitrate_bps));
- break;
- }
- default: {
- TSK_DEBUG_ERROR("HW encoder with id = %d not expected", codec->id);
- assert(false);
- }
- }
- TMEDIA_PRODUCER(pSelf)->video.chroma = tmedia_chroma_nv12;
- TSK_DEBUG_INFO("MF video producer chroma = NV12 (because of HW encoder)");
- }
- else {
- // Video Processors will be inserted in the topology if the source cannot produce I420 frames
- // 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)
- CHECK_HR(hr = pSelf->pGrabberInputType->SetGUID(MF_MT_SUBTYPE, pcPreferredSubTypeGuidPair ? pcPreferredSubTypeGuidPair->fourcc : MFVideoFormat_I420));
- TMEDIA_PRODUCER(pSelf)->video.chroma = pcPreferredSubTypeGuidPair ? pcPreferredSubTypeGuidPair->chroma : tmedia_chroma_yuv420p;
- TSK_DEBUG_INFO("MF video producer chroma = %d", TMEDIA_PRODUCER(pSelf)->video.chroma);
- }
- if (pSelf->pEncoder) {
- // Unlock the encoder
- //BOOL bIsAsyncMFT = FALSE;
- //CHECK_HR(hr = MFUtils::IsAsyncMFT(pSelf->pEncoder->GetMFT(), &bIsAsyncMFT));
- //if(bIsAsyncMFT)
- //{
- // CHECK_HR(hr = MFUtils::UnlockAsyncMFT(pSelf->pEncoderpSelf->pEncoder->GetMFT()));
- //}
- // Apply Encoder output type (must be called before SetInputType)
- //CHECK_HR(hr = pSelf->pEncoder->GetMFT()->SetOutputType(0, pSelf->pGrabberInputType, 0/*MFT_SET_TYPE_TEST_ONLY*/));
- // HW encoders support only NV12
- //CHECK_HR(hr = MFUtils::ConvertVideoTypeToUncompressedType(pSelf->pGrabberInputType, MFVideoFormat_NV12, &pEncoderInputType));
- //CHECK_HR(hr = pSelf->pEncoder->GetMFT()->SetInputType(0, pEncoderInputType, 0/*MFT_SET_TYPE_TEST_ONLY*/));
- }
- // Create the sample grabber sink.
- CHECK_HR(hr = SampleGrabberCB::CreateInstance(TMEDIA_PRODUCER(pSelf), &pSelf->pCallback));
- CHECK_HR(hr = MFCreateSampleGrabberSinkActivate(pSelf->pGrabberInputType, pSelf->pCallback, &pSelf->pSinkGrabber));
- // To run as fast as possible, set this attribute (requires Windows 7):
- CHECK_HR(hr = pSelf->pSinkGrabber->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, TRUE));
- // Create the Media Session.
- CHECK_HR(hr = MFCreateMediaSession(pSessionAttributes, &pSelf->pSession));
- // Create the EVR activation object for the preview.
- CHECK_HR(hr = MFCreateVideoRendererActivate(pSelf->hWndPreview, &pSelf->pSinkActivatePreview));
- // Create the topology.
- CHECK_HR(hr = MFUtils::CreateTopology(
- pSelf->pSource,
- pSelf->pEncoder ? pSelf->pEncoder->GetMFT() : NULL,
- pSelf->pSinkGrabber,
- pSelf->pSinkActivatePreview,
- pSelf->pGrabberInputType,
- &pTopology));
- // Resolve topology (adds video processors if needed).
- CHECK_HR(hr = MFUtils::ResolveTopology(pTopology, &pSelf->pTopology));
- // Find EVR for the preview.
- CHECK_HR(hr = MFUtils::FindNodeObject(pSelf->pTopology, MFUtils::g_ullTopoIdSinkPreview, (void**)&pEvr));
- // Find negotiated media and update producer
- UINT32 nNegWidth = (UINT32)TMEDIA_PRODUCER(pSelf)->video.width, nNegHeight = (UINT32)TMEDIA_PRODUCER(pSelf)->video.height, nNegNumeratorFps = (UINT32)TMEDIA_PRODUCER(pSelf)->video.fps, nNegDenominatorFps = 1;
- CHECK_HR(hr = pSelf->pTopology->GetNodeByID(MFUtils::g_ullTopoIdSinkMain, &pNodeGrabber));
- CHECK_HR(hr = pNodeGrabber->GetInputPrefType(0, &pGrabberNegotiatedInputMedia));
- hr = MFGetAttributeSize(pGrabberNegotiatedInputMedia, MF_MT_FRAME_SIZE, &nNegWidth, &nNegHeight);
- if (SUCCEEDED(hr)) {
- TSK_DEBUG_INFO("MF video producer topology vs sdp parameters: width(%u/%u), height(%u/%u)",
- TMEDIA_PRODUCER(pSelf)->video.width, nNegWidth,
- TMEDIA_PRODUCER(pSelf)->video.height, nNegHeight
- );
- TMEDIA_PRODUCER(pSelf)->video.width = nNegWidth;
- TMEDIA_PRODUCER(pSelf)->video.height = nNegHeight;
- }
- hr = MFGetAttributeRatio(pGrabberNegotiatedInputMedia, MF_MT_FRAME_RATE, &nNegNumeratorFps, &nNegDenominatorFps);
- if (SUCCEEDED(hr)) {
- TSK_DEBUG_INFO("MF video producer topology vs sdp parameters: fps(%u/%u)",
- TMEDIA_PRODUCER(pSelf)->video.fps, (nNegNumeratorFps / nNegDenominatorFps)
- );
- TMEDIA_PRODUCER(pSelf)->video.fps = (nNegNumeratorFps / nNegDenominatorFps);
- }
- // Create EVR watcher for the preview.
- pSelf->pWatcherPreview = new DisplayWatcher(pSelf->hWndPreview, pEvr, hr);
- CHECK_HR(hr);
- }
- bail:
- SafeRelease(&pSessionAttributes);
- SafeRelease(&pTopology);
- SafeRelease(&pEvr);
- SafeRelease(&pEncoderInputType);
- SafeRelease(&pNodeGrabber);
- SafeRelease(&pGrabberNegotiatedInputMedia);
- pSelf->bPrepared = SUCCEEDED(hr);
- return pSelf->bPrepared ? 0 : -1;
- }
- static int plugin_win_mf_producer_video_start(tmedia_producer_t* self)
- {
- plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
- if (!pSelf) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- if (pSelf->bStarted) {
- TSK_DEBUG_INFO("MF video producer already started");
- return 0;
- }
- if (!pSelf->bPrepared) {
- TSK_DEBUG_ERROR("MF video producer not prepared");
- return -1;
- }
- HRESULT hr = S_OK;
- // Run preview watcher
- if (pSelf->pWatcherPreview) {
- CHECK_HR(hr = pSelf->pWatcherPreview->Start());
- }
- // Run the media session.
- CHECK_HR(hr = MFUtils::RunSession(pSelf->pSession, pSelf->pTopology));
- // Start asynchronous watcher thread
- pSelf->bStarted = true;
- int ret = tsk_thread_create(&pSelf->ppTread[0], RunSessionThread, pSelf);
- if (ret != 0) {
- TSK_DEBUG_ERROR("Failed to create thread");
- hr = E_FAIL;
- pSelf->bStarted = false;
- if (pSelf->ppTread[0]) {
- tsk_thread_join(&pSelf->ppTread[0]);
- }
- MFUtils::ShutdownSession(pSelf->pSession, pSelf->pSource);
- goto bail;
- }
- bail:
- return SUCCEEDED(hr) ? 0 : -1;
- }
- static int plugin_win_mf_producer_video_pause(tmedia_producer_t* self)
- {
- plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
- if (!pSelf) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- if (!pSelf->bStarted) {
- TSK_DEBUG_INFO("MF video producer not started");
- return 0;
- }
- HRESULT hr = MFUtils::PauseSession(pSelf->pSession);
- return SUCCEEDED(hr) ? 0 : -1;
- }
- static int plugin_win_mf_producer_video_stop(tmedia_producer_t* self)
- {
- plugin_win_mf_producer_video_t* pSelf = (plugin_win_mf_producer_video_t*)self;
- if (!pSelf) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- HRESULT hr = S_OK;
- if (pSelf->pWatcherPreview) {
- hr = pSelf->pWatcherPreview->Stop();
- }
- // for the thread
- pSelf->bStarted = false;
- hr = MFUtils::ShutdownSession(pSelf->pSession, NULL); // stop session to wakeup the asynchronous thread
- if (pSelf->ppTread[0]) {
- tsk_thread_join(&pSelf->ppTread[0]);
- }
- hr = MFUtils::ShutdownSession(NULL, pSelf->pSource); // stop source to release the camera
- // next start() will be called after prepare()
- return _plugin_win_mf_producer_video_unprepare(pSelf);
- }
- static int _plugin_win_mf_producer_video_unprepare(plugin_win_mf_producer_video_t* pSelf)
- {
- if (!pSelf) {
- TSK_DEBUG_ERROR("Invalid parameter");
- return -1;
- }
- if (pSelf->bStarted) {
- // plugin_win_mf_producer_video_stop(TMEDIA_PRODUCER(pSelf));
- TSK_DEBUG_ERROR("Producer must be stopped before calling unprepare");
- }
- if (pSelf->pDeviceList) {
- delete pSelf->pDeviceList, pSelf->pDeviceList = NULL;
- }
- if (pSelf->pWatcherPreview) {
- pSelf->pWatcherPreview->Stop();
- }
- if (pSelf->pSource) {
- pSelf->pSource->Shutdown();
- }
- if (pSelf->pSession) {
- pSelf->pSession->Shutdown();
- }
- SafeRelease(&pSelf->pEncoder);
- SafeRelease(&pSelf->pSession);
- SafeRelease(&pSelf->pSource);
- SafeRelease(&pSelf->pSinkActivatePreview);
- SafeRelease(&pSelf->pCallback);
- SafeRelease(&pSelf->pSinkGrabber);
- SafeRelease(&pSelf->pTopology);
- SafeRelease(&pSelf->pGrabberInputType);
- if (pSelf->pWatcherPreview) {
- delete pSelf->pWatcherPreview;
- pSelf->pWatcherPreview = NULL;
- }
- pSelf->bPrepared = false;
- return 0;
- }
- //
- // Windows Media Foundation video producer object definition
- //
- /* constructor */
- static tsk_object_t* plugin_win_mf_producer_video_ctor(tsk_object_t * self, va_list * app)
- {
- MFUtils::Startup();
- plugin_win_mf_producer_video_t *pSelf = (plugin_win_mf_producer_video_t *)self;
- if (pSelf) {
- /* init base */
- tmedia_producer_init(TMEDIA_PRODUCER(pSelf));
- /* init self with default values*/
- TMEDIA_PRODUCER(pSelf)->encoder.codec_id = tmedia_codec_id_none; // means RAW frames as input
- TMEDIA_PRODUCER(pSelf)->video.chroma = tmedia_chroma_nv12;
- TMEDIA_PRODUCER(pSelf)->video.fps = 15;
- TMEDIA_PRODUCER(pSelf)->video.width = 352;
- TMEDIA_PRODUCER(pSelf)->video.height = 288;
- TSK_DEBUG_INFO("Create WinMF video producer");
- }
- return self;
- }
- /* destructor */
- static tsk_object_t* plugin_win_mf_producer_video_dtor(tsk_object_t * self)
- {
- plugin_win_mf_producer_video_t *pSelf = (plugin_win_mf_producer_video_t *)self;
- if (pSelf) {
- /* stop */
- if (pSelf->bStarted) {
- plugin_win_mf_producer_video_stop(TMEDIA_PRODUCER(pSelf));
- }
- /* deinit base */
- tmedia_producer_deinit(TMEDIA_PRODUCER(pSelf));
- /* deinit self */
- _plugin_win_mf_producer_video_unprepare(pSelf);
- }
- return self;
- }
- /* object definition */
- static const tsk_object_def_t plugin_win_mf_producer_video_def_s = {
- sizeof(plugin_win_mf_producer_video_t),
- plugin_win_mf_producer_video_ctor,
- plugin_win_mf_producer_video_dtor,
- tsk_null,
- };
- /* plugin definition*/
- static const tmedia_producer_plugin_def_t plugin_win_mf_producer_video_plugin_def_s = {
- &plugin_win_mf_producer_video_def_s,
- tmedia_video,
- "Microsoft Windows Media Foundation producer (Video)",
- plugin_win_mf_producer_video_set,
- plugin_win_mf_producer_video_prepare,
- plugin_win_mf_producer_video_start,
- plugin_win_mf_producer_video_pause,
- plugin_win_mf_producer_video_stop
- };
- const tmedia_producer_plugin_def_t *plugin_win_mf_producer_video_plugin_def_t = &plugin_win_mf_producer_video_plugin_def_s;
- // Run session async thread
- static void* TSK_STDCALL RunSessionThread(void *pArg)
- {
- plugin_win_mf_producer_video_t *pSelf = (plugin_win_mf_producer_video_t *)pArg;
- HRESULT hrStatus = S_OK;
- HRESULT hr = S_OK;
- IMFMediaEvent *pEvent = NULL;
- MediaEventType met;
- TSK_DEBUG_INFO("RunSessionThread (MF video producer) - ENTER");
- while (pSelf->bStarted) {
- hr = pSelf->pSession->GetEvent(0, &pEvent);
- if (hr == MF_E_SHUTDOWN) {
- if (pSelf->bStarted) {
- CHECK_HR(hr); // Shutdown called but "bStarted" not equal to false
- }
- break; // Shutdown called and "bStarted" is equal to false => break the loop
- }
- CHECK_HR(hr = pEvent->GetStatus(&hrStatus));
- CHECK_HR(hr = pEvent->GetType(&met));
- if (FAILED(hrStatus) /*&& hrStatus != MF_E_NO_SAMPLE_TIMESTAMP*/) {
- TSK_DEBUG_ERROR("Session error: 0x%x (event id: %d)\n", hrStatus, met);
- hr = hrStatus;
- goto bail;
- }
- if (met == MESessionEnded) {
- break;
- }
- SafeRelease(&pEvent);
- }
- bail:
- TSK_DEBUG_INFO("RunSessionThread (MF video producer) - EXIT");
- return NULL;
- }
|