2013-07-03 10:13:37 +00:00
/* Copyright (C) 2013 Mamadou DIOP
* Copyright ( C ) 2013 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_custom_src.h"
2013-07-10 02:21:17 +00:00
# include "internals/mf_display_watcher.h"
2013-07-03 10:13:37 +00:00
# include "tinymedia/tmedia_consumer.h"
# include "tsk_string.h"
# include "tsk_thread.h"
# include "tsk_debug.h"
# include <Codecapi.h>
# include <assert.h>
// 0: {{[Source] -> (VideoProcessor) -> SampleGrabber}} , {{[Decoder]}} -> RTP
// 1: {{[Source] -> (VideoProcessor) -> [Decoder] -> 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_CV_BUNDLE_CODEC)
# define PLUGIN_MF_CV_BUNDLE_CODEC 0
# endif
// Uncompressed video frame will come from Doubango core and it's up to the converter to match the requested chroma.
// Supported values: NV12, I420, RGB32 and RGB24. (RGB formats are not recommended because of performance issues)
// To avoid chroma conversion (performance issues) we use NV12 when the codec is bundled as MediaFoundation codecs most likely only support this format.
// NV12 is the native format for media foundation codecs (e.g. Intel Quick Sync) and the GPU.
// I420 is the native format for FFmpeg, libvpx and libtheora.
const GUID kDefaultUncompressedType
# if PLUGIN_MF_CV_BUNDLE_CODEC
= MFVideoFormat_NV12 ;
# else
= MFVideoFormat_I420 ;
# endif
static void * TSK_STDCALL RunSessionThread ( void * pArg ) ;
static int _plugin_win_mf_consumer_video_unprepare ( struct plugin_win_mf_consumer_video_s * pSelf ) ;
typedef struct plugin_win_mf_consumer_video_s
{
TMEDIA_DECLARE_CONSUMER ;
bool bStarted , bPrepared ;
HWND hWindow ;
tsk_thread_handle_t * ppTread [ 1 ] ;
2013-07-10 21:57:19 +00:00
UINT32 nNegWidth ;
UINT32 nNegHeight ;
UINT32 nNegFps ;
2013-07-03 10:13:37 +00:00
IMFTransform * pDecoder ;
IMFMediaSession * pSession ;
CMFSource * pSource ;
IMFActivate * pSinkActivate ;
2013-07-10 02:21:17 +00:00
DisplayWatcher * pDisplayWatcher ;
2013-07-10 21:57:19 +00:00
IMFTopology * pTopologyFull ;
IMFTopology * pTopologyPartial ;
2013-07-03 10:13:37 +00:00
IMFMediaType * pOutType ;
}
plugin_win_mf_consumer_video_t ;
/* ============ Media Consumer Interface ================= */
static int plugin_win_mf_consumer_video_set ( tmedia_consumer_t * self , const tmedia_param_t * param )
{
int ret = 0 ;
2013-07-10 02:21:17 +00:00
HRESULT hr = S_OK ;
2013-07-03 10:13:37 +00:00
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
if ( ! self | | ! param ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( param - > value_type = = tmedia_pvt_int64 ) {
if ( tsk_striequals ( param - > key , " remote-hwnd " ) ) {
2013-07-10 02:21:17 +00:00
HWND hWnd = reinterpret_cast < HWND > ( ( INT64 ) * ( ( int64_t * ) param - > value ) ) ;
if ( hWnd ! = pSelf - > hWindow )
{
pSelf - > hWindow = hWnd ;
if ( pSelf - > pDisplayWatcher )
{
CHECK_HR ( hr = pSelf - > pDisplayWatcher - > SetHwnd ( hWnd ) ) ;
2013-07-03 10:13:37 +00:00
}
2013-07-10 02:21:17 +00:00
}
2013-07-03 10:13:37 +00:00
}
}
else if ( param - > value_type = = tmedia_pvt_int32 ) {
if ( tsk_striequals ( param - > key , " fullscreen " ) ) {
2013-07-10 02:21:17 +00:00
if ( pSelf - > pDisplayWatcher )
{
CHECK_HR ( hr = pSelf - > pDisplayWatcher - > SetFullscreen ( ! ! * ( ( int32_t * ) param - > value ) ) ) ;
}
2013-07-03 10:13:37 +00:00
}
else if ( tsk_striequals ( param - > key , " create-on-current-thead " ) ) {
// DSCONSUMER(self)->create_on_ui_thread = *((int32_t*)param->value) ? tsk_false : tsk_true;
}
else if ( tsk_striequals ( param - > key , " plugin-firefox " ) ) {
/*DSCONSUMER(self)->plugin_firefox = (*((int32_t*)param->value) != 0);
if ( DSCONSUMER ( self ) - > display ) {
DSCONSUMER ( self ) - > display - > setPluginFirefox ( ( DSCONSUMER ( self ) - > plugin_firefox = = tsk_true ) ) ;
} */
}
}
2013-07-10 02:21:17 +00:00
bail :
return SUCCEEDED ( hr ) ? 0 : - 1 ;
2013-07-03 10:13:37 +00:00
}
static int plugin_win_mf_consumer_video_prepare ( tmedia_consumer_t * self , const tmedia_codec_t * codec )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
if ( ! pSelf | | ! codec & & codec - > plugin ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( pSelf - > bPrepared ) {
TSK_DEBUG_WARN ( " MF video consumer already prepared " ) ;
return - 1 ;
}
HRESULT hr = S_OK ;
TMEDIA_CONSUMER ( pSelf ) - > video . fps = TMEDIA_CODEC_VIDEO ( codec ) - > in . fps ;
TMEDIA_CONSUMER ( pSelf ) - > video . in . width = TMEDIA_CODEC_VIDEO ( codec ) - > in . width ;
TMEDIA_CONSUMER ( pSelf ) - > video . in . height = TMEDIA_CODEC_VIDEO ( codec ) - > in . height ;
if ( ! TMEDIA_CONSUMER ( pSelf ) - > video . display . width ) {
TMEDIA_CONSUMER ( pSelf ) - > video . display . width = TMEDIA_CONSUMER ( pSelf ) - > video . in . width ;
}
if ( ! TMEDIA_CONSUMER ( pSelf ) - > video . display . height ) {
TMEDIA_CONSUMER ( pSelf ) - > video . display . height = TMEDIA_CONSUMER ( pSelf ) - > video . in . height ;
}
2013-07-10 21:57:19 +00:00
pSelf - > nNegFps = TMEDIA_CONSUMER ( pSelf ) - > video . fps ;
pSelf - > nNegWidth = TMEDIA_CONSUMER ( pSelf ) - > video . display . width ;
pSelf - > nNegHeight = TMEDIA_CONSUMER ( pSelf ) - > video . display . height ;
2013-07-03 10:13:37 +00:00
TSK_DEBUG_INFO ( " MF video consumer: fps=%d, width=%d, height=%d " ,
2013-07-10 21:57:19 +00:00
pSelf - > nNegFps ,
pSelf - > nNegWidth ,
pSelf - > nNegHeight ) ;
2013-07-03 10:13:37 +00:00
if ( kDefaultUncompressedType = = MFVideoFormat_NV12 ) {
TMEDIA_CONSUMER ( pSelf ) - > video . display . chroma = tmedia_chroma_nv12 ;
}
else if ( kDefaultUncompressedType = = MFVideoFormat_I420 ) {
TMEDIA_CONSUMER ( pSelf ) - > video . display . chroma = tmedia_chroma_yuv420p ;
}
else if ( kDefaultUncompressedType = = MFVideoFormat_RGB32 ) {
TMEDIA_CONSUMER ( pSelf ) - > video . display . chroma = tmedia_chroma_rgb32 ;
}
else if ( kDefaultUncompressedType = = MFVideoFormat_RGB24 ) {
TMEDIA_CONSUMER ( pSelf ) - > video . display . chroma = tmedia_chroma_rgb24 ;
}
else {
CHECK_HR ( hr = E_NOTIMPL ) ;
}
2013-07-10 02:21:17 +00:00
IMFMediaSink * pEvr = NULL ;
2013-07-03 10:13:37 +00:00
CHECK_HR ( hr = MFCreateMediaType ( & pSelf - > pOutType ) ) ;
CHECK_HR ( hr = pSelf - > pOutType - > SetGUID ( MF_MT_MAJOR_TYPE , MFMediaType_Video ) ) ;
# if PLUGIN_MF_CV_BUNDLE_CODEC
if ( ( codec - > id = = tmedia_codec_id_h264_bp | | codec - > id = = tmedia_codec_id_h264_mp ) ) {
// both Microsoft and Intel encoders support NV12 only as input
static const bool kIsEncoder = false ;
hr = MFUtils : : GetBestCodec ( kIsEncoder , MFMediaType_Video , MFVideoFormat_NV12 , MFVideoFormat_H264 , & pSelf - > pDecoder ) ;
if ( SUCCEEDED ( hr ) & & pSelf - > pDecoder ) {
TMEDIA_CONSUMER ( pSelf ) - > decoder . codec_id = codec - > id ;
CHECK_HR ( hr = pSelf - > pOutType - > SetGUID ( MF_MT_SUBTYPE , MFVideoFormat_H264 ) ) ;
}
else {
SafeRelease ( & pSelf - > pDecoder ) ;
TSK_DEBUG_WARN ( " Failed to find H.264 HW encoder...fallback to SW implementation " ) ;
TMEDIA_PRODUCER ( pSelf ) - > decoder . codec_id = tmedia_codec_id_none ; // means RAW
}
}
# endif
2013-07-10 21:57:19 +00:00
2013-07-03 10:13:37 +00:00
if ( ! pSelf - > pDecoder ) {
CHECK_HR ( hr = pSelf - > pOutType - > SetGUID ( MF_MT_SUBTYPE , kDefaultUncompressedType ) ) ;
}
CHECK_HR ( hr = pSelf - > pOutType - > SetUINT32 ( MF_MT_INTERLACE_MODE , MFVideoInterlace_Progressive ) ) ;
CHECK_HR ( hr = pSelf - > pOutType - > SetUINT32 ( MF_MT_ALL_SAMPLES_INDEPENDENT , TRUE ) ) ;
2013-07-10 21:57:19 +00:00
CHECK_HR ( hr = MFSetAttributeSize ( pSelf - > pOutType , MF_MT_FRAME_SIZE , pSelf - > nNegWidth , pSelf - > nNegHeight ) ) ;
CHECK_HR ( hr = MFSetAttributeRatio ( pSelf - > pOutType , MF_MT_FRAME_RATE , pSelf - > nNegFps , 1 ) ) ;
2013-07-03 10:13:37 +00:00
CHECK_HR ( hr = MFSetAttributeRatio ( pSelf - > pOutType , MF_MT_PIXEL_ASPECT_RATIO , 1 , 1 ) ) ;
CHECK_HR ( hr = CMFSource : : CreateInstanceEx ( IID_IMFMediaSource , ( void * * ) & pSelf - > pSource , pSelf - > pOutType ) ) ;
// Apply Encoder output type (must be called before SetInputType)
if ( pSelf - > pDecoder ) {
CHECK_HR ( hr = pSelf - > pDecoder - > SetOutputType ( 0 , pSelf - > pOutType , 0 /*MFT_SET_TYPE_TEST_ONLY*/ ) ) ;
}
// Create the Media Session.
CHECK_HR ( hr = MFCreateMediaSession ( NULL , & pSelf - > pSession ) ) ;
// Create the EVR activation object.
CHECK_HR ( hr = MFCreateVideoRendererActivate ( pSelf - > hWindow , & pSelf - > pSinkActivate ) ) ;
// Create the topology.
2013-07-10 21:57:19 +00:00
CHECK_HR ( hr = MFUtils : : CreateTopology ( pSelf - > pSource , pSelf - > pDecoder , pSelf - > pSinkActivate , NULL /*Preview*/ , MFMediaType_Video , & pSelf - > pTopologyPartial ) ) ;
2013-07-03 10:13:37 +00:00
// Resolve topology (adds video processors if needed).
2013-07-10 21:57:19 +00:00
CHECK_HR ( hr = MFUtils : : ResolveTopology ( pSelf - > pTopologyPartial , & pSelf - > pTopologyFull ) ) ;
2013-07-03 10:13:37 +00:00
2013-07-10 02:21:17 +00:00
// Find EVR
2013-07-10 21:57:19 +00:00
CHECK_HR ( hr = MFUtils : : FindNodeObject ( pSelf - > pTopologyFull , MFUtils : : g_ullTopoIdSinkMain , ( void * * ) & pEvr ) ) ;
2013-07-10 02:21:17 +00:00
// Create EVR watcher
pSelf - > pDisplayWatcher = new DisplayWatcher ( pSelf - > hWindow , pEvr , hr ) ;
CHECK_HR ( hr ) ;
2013-07-03 10:13:37 +00:00
bail :
2013-07-10 02:21:17 +00:00
SafeRelease ( & pEvr ) ;
2013-07-03 10:13:37 +00:00
pSelf - > bPrepared = SUCCEEDED ( hr ) ;
return pSelf - > bPrepared ? 0 : - 1 ;
}
static int plugin_win_mf_consumer_video_start ( tmedia_consumer_t * self )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
if ( ! pSelf ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
if ( pSelf - > bStarted ) {
TSK_DEBUG_INFO ( " MF video consumer already started " ) ;
return 0 ;
}
if ( ! pSelf - > bPrepared ) {
TSK_DEBUG_ERROR ( " MF video consumer not prepared " ) ;
return - 1 ;
}
HRESULT hr = S_OK ;
2013-07-10 02:21:17 +00:00
// Run EVR watcher
if ( pSelf - > pDisplayWatcher ) {
CHECK_HR ( hr = pSelf - > pDisplayWatcher - > Start ( ) ) ;
}
2013-07-03 10:13:37 +00:00
// Run the media session.
2013-07-10 21:57:19 +00:00
CHECK_HR ( hr = MFUtils : : RunSession ( pSelf - > pSession , pSelf - > pTopologyFull ) ) ;
2013-07-03 10:13:37 +00:00
// 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 ) ;
CHECK_HR ( hr = E_FAIL ) ;
}
bail :
return SUCCEEDED ( hr ) ? 0 : - 1 ;
}
static int plugin_win_mf_consumer_video_consume ( tmedia_consumer_t * self , const void * buffer , tsk_size_t size , const tsk_object_t * proto_hdr )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
HRESULT hr = S_OK ;
if ( ! pSelf | | ! buffer | | ! size ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
CHECK_HR ( hr = E_INVALIDARG ) ;
}
if ( ! pSelf - > bStarted ) {
TSK_DEBUG_INFO ( " MF video consumer not started " ) ;
CHECK_HR ( hr = E_FAIL ) ;
}
if ( ! pSelf - > pSource ) {
TSK_DEBUG_ERROR ( " No video custom source " ) ;
CHECK_HR ( hr = E_FAIL ) ;
}
2013-07-10 21:57:19 +00:00
if ( pSelf - > nNegWidth ! = TMEDIA_CONSUMER ( pSelf ) - > video . in . width | | pSelf - > nNegHeight ! = TMEDIA_CONSUMER ( pSelf ) - > video . in . height ) {
TSK_DEBUG_INFO ( " Negotiated and input video sizes are different:%d#%d or %d#%d " ,
pSelf - > nNegWidth , TMEDIA_CONSUMER ( pSelf ) - > video . in . width ,
pSelf - > nNegHeight , TMEDIA_CONSUMER ( pSelf ) - > video . in . height ) ;
// Update media type
CHECK_HR ( hr = MFSetAttributeSize ( pSelf - > pOutType , MF_MT_FRAME_SIZE , TMEDIA_CONSUMER ( pSelf ) - > video . in . width , TMEDIA_CONSUMER ( pSelf ) - > video . in . height ) ) ;
CHECK_HR ( hr = MFSetAttributeRatio ( pSelf - > pOutType , MF_MT_FRAME_RATE , TMEDIA_CONSUMER ( pSelf ) - > video . fps , 1 ) ) ;
2013-07-10 23:12:13 +00:00
CHECK_HR ( hr = pSelf - > pSession - > ClearTopologies ( ) ) ;
//
// FIXME: Using same EVR when the size is just swapped (e.g. [640, 480] -> [480, 640]) doesn't work while other changes does (e.g. [352, 288] -> [640, 480])
// /!\This look like a bug in Media Foundation
//
if ( pSelf - > nNegWidth = = TMEDIA_CONSUMER ( pSelf ) - > video . in . height & & pSelf - > nNegHeight = = TMEDIA_CONSUMER ( pSelf ) - > video . in . width ) // swapped?
{
TSK_DEBUG_INFO ( " /! \\ Size swapped " ) ;
IMFActivate * pSinkActivate = NULL ;
IMFTopology * pTopologyPartial = NULL ;
hr = MFCreateVideoRendererActivate ( pSelf - > hWindow , & pSinkActivate ) ;
if ( FAILED ( hr ) ) goto end_of_swapping ;
hr = MFUtils : : CreateTopology ( pSelf - > pSource , pSelf - > pDecoder , pSinkActivate , NULL /*Preview*/ , MFMediaType_Video , & pTopologyPartial ) ;
if ( FAILED ( hr ) ) goto end_of_swapping ;
if ( SUCCEEDED ( hr ) ) {
SafeRelease ( & pSelf - > pSinkActivate ) ;
SafeRelease ( & pSelf - > pTopologyPartial ) ;
pSelf - > pSinkActivate = pSinkActivate ; pSinkActivate = NULL ;
pSelf - > pTopologyPartial = pTopologyPartial ; pTopologyPartial = NULL ;
}
end_of_swapping :
SafeRelease ( & pSinkActivate ) ;
SafeRelease ( & pTopologyPartial ) ;
CHECK_HR ( hr ) ;
}
2013-07-10 21:57:19 +00:00
// Set media type again (not required but who know)
CHECK_HR ( hr = MFUtils : : SetMediaType ( pSelf - > pSource , pSelf - > pOutType ) ) ;
// Rebuild topology using the partial one
2013-07-10 23:12:13 +00:00
IMFTopology * pTopologyFull = NULL ;
hr = MFUtils : : ResolveTopology ( pSelf - > pTopologyPartial , & pTopologyFull ) ;
if ( SUCCEEDED ( hr ) ) {
SafeRelease ( & pSelf - > pTopologyFull ) ;
pSelf - > pTopologyFull = pTopologyFull ; pTopologyFull = NULL ;
}
SafeRelease ( & pTopologyFull ) ;
CHECK_HR ( hr ) ;
// Find Main Sink
IMFMediaSink * pMediaSink = NULL ;
hr = MFUtils : : FindNodeObject ( pSelf - > pTopologyFull , MFUtils : : g_ullTopoIdSinkMain , ( void * * ) & pMediaSink ) ;
if ( SUCCEEDED ( hr ) ) {
if ( pSelf - > pDisplayWatcher ) {
delete pSelf - > pDisplayWatcher , pSelf - > pDisplayWatcher = NULL ;
}
pSelf - > pDisplayWatcher = new DisplayWatcher ( pSelf - > hWindow , pMediaSink , hr ) ;
if ( SUCCEEDED ( hr ) & & pSelf - > bStarted ) {
hr = pSelf - > pDisplayWatcher - > Start ( ) ;
}
}
SafeRelease ( & pMediaSink ) ;
CHECK_HR ( hr ) ;
2013-07-10 21:57:19 +00:00
// Update the topology associated to the media session
CHECK_HR ( hr = pSelf - > pSession - > SetTopology ( MFSESSION_SETTOPOLOGY_IMMEDIATE , pSelf - > pTopologyFull ) ) ;
// Update negotiated width and height
pSelf - > nNegWidth = TMEDIA_CONSUMER ( pSelf ) - > video . in . width ;
pSelf - > nNegHeight = TMEDIA_CONSUMER ( pSelf ) - > video . in . height ;
}
2013-07-03 10:13:37 +00:00
2013-07-10 21:57:19 +00:00
// Deliver buffer
CHECK_HR ( hr = pSelf - > pSource - > CopyVideoBuffer ( pSelf - > nNegWidth , pSelf - > nNegHeight , buffer , size ) ) ;
2013-07-03 10:13:37 +00:00
bail :
return SUCCEEDED ( hr ) ? 0 : - 1 ;
}
static int plugin_win_mf_consumer_video_pause ( tmedia_consumer_t * self )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_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_consumer_video_stop ( tmedia_consumer_t * self )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
if ( ! pSelf ) {
TSK_DEBUG_ERROR ( " Invalid parameter " ) ;
return - 1 ;
}
HRESULT hr = S_OK ;
2013-07-10 02:21:17 +00:00
// stop EVR watcher
if ( pSelf - > pDisplayWatcher ) {
hr = pSelf - > pDisplayWatcher - > Stop ( ) ;
}
2013-07-03 10:13:37 +00:00
// 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_consumer_video_unprepare ( pSelf ) ;
}
static int _plugin_win_mf_consumer_video_unprepare ( plugin_win_mf_consumer_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 ( " Consumer must be stopped before calling unprepare " ) ;
}
2013-07-10 02:21:17 +00:00
if ( pSelf - > pDisplayWatcher ) {
pSelf - > pDisplayWatcher - > Stop ( ) ;
}
2013-07-03 10:13:37 +00:00
if ( pSelf - > pSource ) {
pSelf - > pSource - > Shutdown ( ) ;
pSelf - > pSource = NULL ;
}
if ( pSelf - > pSession ) {
pSelf - > pSession - > Shutdown ( ) ;
pSelf - > pSession = NULL ;
}
SafeRelease ( & pSelf - > pDecoder ) ;
SafeRelease ( & pSelf - > pSession ) ;
SafeRelease ( & pSelf - > pSource ) ;
SafeRelease ( & pSelf - > pSinkActivate ) ;
2013-07-10 21:57:19 +00:00
SafeRelease ( & pSelf - > pTopologyFull ) ;
SafeRelease ( & pSelf - > pTopologyPartial ) ;
2013-07-03 10:13:37 +00:00
SafeRelease ( & pSelf - > pOutType ) ;
2013-07-10 02:21:17 +00:00
if ( pSelf - > pDisplayWatcher ) {
delete pSelf - > pDisplayWatcher ;
pSelf - > pDisplayWatcher = NULL ;
}
2013-07-03 10:13:37 +00:00
pSelf - > bPrepared = false ;
return 0 ;
}
//
// Media Foundation video consumer object definition
//
/* constructor */
static tsk_object_t * plugin_win_mf_consumer_video_ctor ( tsk_object_t * self , va_list * app )
{
MFUtils : : Startup ( ) ;
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
if ( pSelf ) {
/* init base */
tmedia_consumer_init ( TMEDIA_CONSUMER ( pSelf ) ) ;
TMEDIA_CONSUMER ( pSelf ) - > video . display . chroma = tmedia_chroma_yuv420p ;
/* init self */
// consumer->create_on_ui_thread = tsk_true;
TMEDIA_CONSUMER ( pSelf ) - > video . fps = 15 ;
TMEDIA_CONSUMER ( pSelf ) - > video . display . width = 0 ; // use codec value
TMEDIA_CONSUMER ( pSelf ) - > video . display . height = 0 ; // use codec value
TMEDIA_CONSUMER ( pSelf ) - > video . display . auto_resize = tsk_true ;
}
return self ;
}
/* destructor */
static tsk_object_t * plugin_win_mf_consumer_video_dtor ( tsk_object_t * self )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) self ;
if ( pSelf ) {
/* stop */
if ( pSelf - > bStarted ) {
plugin_win_mf_consumer_video_stop ( TMEDIA_CONSUMER ( pSelf ) ) ;
}
/* deinit base */
tmedia_consumer_deinit ( TMEDIA_CONSUMER ( pSelf ) ) ;
/* deinit self */
_plugin_win_mf_consumer_video_unprepare ( pSelf ) ;
}
return self ;
}
/* object definition */
static const tsk_object_def_t plugin_win_mf_consumer_video_def_s =
{
sizeof ( plugin_win_mf_consumer_video_t ) ,
plugin_win_mf_consumer_video_ctor ,
plugin_win_mf_consumer_video_dtor ,
tsk_null ,
} ;
/* plugin definition*/
static const tmedia_consumer_plugin_def_t plugin_win_mf_consumer_video_plugin_def_s =
{
& plugin_win_mf_consumer_video_def_s ,
tmedia_video ,
" Media Foundation video consumer " ,
plugin_win_mf_consumer_video_set ,
plugin_win_mf_consumer_video_prepare ,
plugin_win_mf_consumer_video_start ,
plugin_win_mf_consumer_video_consume ,
plugin_win_mf_consumer_video_pause ,
plugin_win_mf_consumer_video_stop
} ;
const tmedia_consumer_plugin_def_t * plugin_win_mf_consumer_video_plugin_def_t = & plugin_win_mf_consumer_video_plugin_def_s ;
// Run session async thread
static void * TSK_STDCALL RunSessionThread ( void * pArg )
{
plugin_win_mf_consumer_video_t * pSelf = ( plugin_win_mf_consumer_video_t * ) pArg ;
HRESULT hrStatus = S_OK ;
HRESULT hr = S_OK ;
IMFMediaEvent * pEvent = NULL ;
MediaEventType met ;
TSK_DEBUG_INFO ( " RunSessionThread (MF video consumer) - ENTER " ) ;
while ( pSelf - > bStarted ) {
CHECK_HR ( hr = pSelf - > pSession - > GetEvent ( 0 , & pEvent ) ) ;
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 consumer) - EXIT " ) ;
return NULL ;
}