X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fdshow%2Fdshow.cpp;h=6b0d67ce43dd3184ab256739457b1397e4dd3d2b;hb=ab3a276e8240fb586c54fb84b904a378914f5d1d;hp=7b2e96a321ef571d622f643ae38a399031067d48;hpb=c7d9a5cdacd1afb15ca73a42dbb48f7407bfb8e9;p=vlc diff --git a/modules/access/dshow/dshow.cpp b/modules/access/dshow/dshow.cpp index 7b2e96a321..6b0d67ce43 100644 --- a/modules/access/dshow/dshow.cpp +++ b/modules/access/dshow/dshow.cpp @@ -1,7 +1,7 @@ /***************************************************************************** * dshow.cpp : DirectShow access module for vlc ***************************************************************************** - * Copyright (C) 2002, 2003 the VideoLAN team + * Copyright (C) 2002-2004, 2006, 2008, 2010 the VideoLAN team * $Id$ * * Author: Gildas Bazin @@ -30,6 +30,7 @@ # include "config.h" #endif +#define __STDC_CONSTANT_MACROS 1 #define __STDC_FORMAT_MACROS 1 #include @@ -38,8 +39,8 @@ #include #include #include -#include -#include +#include +#include #include "common.h" #include "filter.h" @@ -114,6 +115,8 @@ static const char *const ppsz_amtuner_mode_text[] = { N_("Default"), "Size of the video that will be displayed by the " \ "DirectShow plugin. If you don't specify anything the default size for " \ "your device will be used. You can specify a standard size (cif, d1, ...) or x.") +#define ASPECT_TEXT N_("Picture aspect-ratio n:m") +#define ASPECT_LONGTEXT N_("Define input picture aspect-ratio to use. Default is 4:3" ) #define CHROMA_TEXT N_("Video input chroma format") #define CHROMA_LONGTEXT N_( \ "Force the DirectShow video input to use a specific chroma format " \ @@ -133,6 +136,10 @@ static const char *const ppsz_amtuner_mode_text[] = { N_("Default"), #define CHANNEL_LONGTEXT N_( \ "Set the TV channel the tuner will set to " \ "(0 means default)." ) +#define TVFREQ_TEXT N_("Tuner Frequency") +#define TVFREQ_LONGTEXT N_( "This overrides the channel. Measured in Hz." ) +#define STANDARD_TEXT N_( "Standard" ) +#define STANDARD_LONGTEXT N_( "Video standard (Default, SECAM_D, PAL_B, NTSC_M, etc...)." ) #define COUNTRY_TEXT N_("Tuner country code") #define COUNTRY_LONGTEXT N_( \ "Set the tuner country code that establishes the current " \ @@ -173,6 +180,31 @@ static const char *const ppsz_amtuner_mode_text[] = { N_("Default"), #define AUDIO_BITSPERSAMPLE_LONGTEXT N_( \ "Select audio input format with the given bits/sample (if non 0)" ) +static const int i_standards_list[] = + { + KS_AnalogVideo_None, + KS_AnalogVideo_NTSC_M, KS_AnalogVideo_NTSC_M_J, KS_AnalogVideo_NTSC_433, + KS_AnalogVideo_PAL_B, KS_AnalogVideo_PAL_D, KS_AnalogVideo_PAL_G, + KS_AnalogVideo_PAL_H, KS_AnalogVideo_PAL_I, KS_AnalogVideo_PAL_M, + KS_AnalogVideo_PAL_N, KS_AnalogVideo_PAL_60, + KS_AnalogVideo_SECAM_B, KS_AnalogVideo_SECAM_D, KS_AnalogVideo_SECAM_G, + KS_AnalogVideo_SECAM_H, KS_AnalogVideo_SECAM_K, KS_AnalogVideo_SECAM_K1, + KS_AnalogVideo_SECAM_L, KS_AnalogVideo_SECAM_L1, + KS_AnalogVideo_PAL_N_COMBO + }; +static const char *const ppsz_standards_list_text[] = + { + N_("Default"), + "NTSC_M", "NTSC_M_J", "NTSC_443", + "PAL_B", "PAL_D", "PAL_G", + "PAL_H", "PAL_I", "PAL_M", + "PAL_N", "PAL_60", + "SECAM_B", "SECAM_D", "SECAM_G", + "SECAM_H", "SECAM_K", "SECAM_K1", + "SECAM_L", "SECAM_L1", + "PAL_N_COMBO" + }; + static int CommonOpen ( vlc_object_t *, access_sys_t *, bool ); static void CommonClose( vlc_object_t *, access_sys_t * ); @@ -182,81 +214,88 @@ static void AccessClose( vlc_object_t * ); static int DemuxOpen ( vlc_object_t * ); static void DemuxClose ( vlc_object_t * ); -vlc_module_begin(); - set_shortname( N_("DirectShow") ); - set_description( N_("DirectShow input") ); - set_category( CAT_INPUT ); - set_subcategory( SUBCAT_INPUT_ACCESS ); +vlc_module_begin () + set_shortname( N_("DirectShow") ) + set_description( N_("DirectShow input") ) + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_ACCESS ) add_integer( "dshow-caching", (mtime_t)(0.2*CLOCK_FREQ) / 1000, NULL, - CACHING_TEXT, CACHING_LONGTEXT, true ); + CACHING_TEXT, CACHING_LONGTEXT, true ) + + add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, false) + change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback ) + change_action_add( FindDevicesCallback, N_("Refresh list") ) + change_action_add( ConfigDevicesCallback, N_("Configure") ) + + add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, false) + change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback ) + change_action_add( FindDevicesCallback, N_("Refresh list") ) + change_action_add( ConfigDevicesCallback, N_("Configure") ) + + add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, false) - add_string( "dshow-vdev", NULL, NULL, VDEV_TEXT, VDEV_LONGTEXT, false); - change_string_list( ppsz_vdev, ppsz_vdev_text, FindDevicesCallback ); - change_action_add( FindDevicesCallback, N_("Refresh list") ); - change_action_add( ConfigDevicesCallback, N_("Configure") ); + add_string( "dshow-aspect-ratio", "4:3", NULL, ASPECT_TEXT, ASPECT_LONGTEXT, false) - add_string( "dshow-adev", NULL, NULL, ADEV_TEXT, ADEV_LONGTEXT, false); - change_string_list( ppsz_adev, ppsz_adev_text, FindDevicesCallback ); - change_action_add( FindDevicesCallback, N_("Refresh list") ); - change_action_add( ConfigDevicesCallback, N_("Configure") ); + add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true ) - add_string( "dshow-size", NULL, NULL, SIZE_TEXT, SIZE_LONGTEXT, false); + add_float( "dshow-fps", 0.0f, NULL, FPS_TEXT, FPS_LONGTEXT, true ) - add_string( "dshow-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, - true ); + add_bool( "dshow-config", false, NULL, CONFIG_TEXT, CONFIG_LONGTEXT, true ) - add_float( "dshow-fps", 0.0f, NULL, FPS_TEXT, FPS_LONGTEXT, - true ); + add_bool( "dshow-tuner", false, NULL, TUNER_TEXT, TUNER_LONGTEXT, true ) - add_bool( "dshow-config", false, NULL, CONFIG_TEXT, CONFIG_LONGTEXT, - true ); + add_integer( "dshow-tuner-channel", 0, NULL, CHANNEL_TEXT, CHANNEL_LONGTEXT, + true ) - add_bool( "dshow-tuner", false, NULL, TUNER_TEXT, TUNER_LONGTEXT, - true ); + add_integer( "dshow-tuner-frequency", 0, NULL, TVFREQ_TEXT, TVFREQ_LONGTEXT, + true ) - add_integer( "dshow-tuner-channel", 0, NULL, CHANNEL_TEXT, - CHANNEL_LONGTEXT, true ); + add_integer( "dshow-tuner-country", 0, NULL, COUNTRY_TEXT, COUNTRY_LONGTEXT, + true ) - add_integer( "dshow-tuner-country", 0, NULL, COUNTRY_TEXT, - COUNTRY_LONGTEXT, true ); + add_integer( "dshow-tuner-standard", 0, NULL, STANDARD_TEXT, STANDARD_LONGTEXT, + false ) + change_integer_list( i_standards_list, ppsz_standards_list_text, NULL ) add_integer( "dshow-tuner-input", 0, NULL, TUNER_INPUT_TEXT, - TUNER_INPUT_LONGTEXT, true ); - change_integer_list( pi_tuner_input, ppsz_tuner_input_text, NULL ); + TUNER_INPUT_LONGTEXT, true ) + change_integer_list( pi_tuner_input, ppsz_tuner_input_text, NULL ) add_integer( "dshow-video-input", -1, NULL, VIDEO_IN_TEXT, - VIDEO_IN_LONGTEXT, true ); - - add_integer( "dshow-audio-input", -1, NULL, AUDIO_IN_TEXT, - AUDIO_IN_LONGTEXT, true ); + VIDEO_IN_LONGTEXT, true ) add_integer( "dshow-video-output", -1, NULL, VIDEO_OUT_TEXT, - VIDEO_OUT_LONGTEXT, true ); + VIDEO_OUT_LONGTEXT, true ) + + add_integer( "dshow-audio-input", -1, NULL, AUDIO_IN_TEXT, + AUDIO_IN_LONGTEXT, true ) add_integer( "dshow-audio-output", -1, NULL, AUDIO_OUT_TEXT, - AUDIO_OUT_LONGTEXT, true ); + AUDIO_OUT_LONGTEXT, true ) add_integer( "dshow-amtuner-mode", AMTUNER_MODE_TV, NULL, - AMTUNER_MODE_TEXT, AMTUNER_MODE_LONGTEXT, false); - change_integer_list( pi_amtuner_mode, ppsz_amtuner_mode_text, NULL ); + AMTUNER_MODE_TEXT, AMTUNER_MODE_LONGTEXT, false) + change_integer_list( pi_amtuner_mode, ppsz_amtuner_mode_text, NULL ) add_integer( "dshow-audio-channels", 0, NULL, AUDIO_CHANNELS_TEXT, - AUDIO_CHANNELS_LONGTEXT, true ); + AUDIO_CHANNELS_LONGTEXT, true ) add_integer( "dshow-audio-samplerate", 0, NULL, AUDIO_SAMPLERATE_TEXT, - AUDIO_SAMPLERATE_LONGTEXT, true ); + AUDIO_SAMPLERATE_LONGTEXT, true ) add_integer( "dshow-audio-bitspersample", 0, NULL, AUDIO_BITSPERSAMPLE_TEXT, - AUDIO_BITSPERSAMPLE_LONGTEXT, true ); + AUDIO_BITSPERSAMPLE_LONGTEXT, true ) - add_shortcut( "dshow" ); - set_capability( "access_demux", 0 ); - set_callbacks( DemuxOpen, DemuxClose ); + add_shortcut( "dshow" ) + set_capability( "access_demux", 0 ) + set_callbacks( DemuxOpen, DemuxClose ) - add_submodule(); - set_description( N_("DirectShow input") ); - set_capability( "access", 0 ); - set_callbacks( AccessOpen, AccessClose ); + add_submodule () + set_description( N_("DirectShow input") ) + add_shortcut( "dshow" ) + set_capability( "access", 0 ) + set_callbacks( AccessOpen, AccessClose ) + +vlc_module_end () -vlc_module_end(); /***************************************************************************** * DirectShow elementary stream descriptor @@ -346,39 +385,57 @@ static void DeleteDirectShowGraph( access_sys_t *p_sys ) static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, bool b_access_demux ) { - vlc_value_t val; int i; + char *psz_val; /* Get/parse options and open device(s) */ string vdevname, adevname; - int i_width = 0, i_height = 0, i_chroma = 0; - bool b_audio = true; + int i_width = 0, i_height = 0; + vlc_fourcc_t i_chroma = 0; + bool b_use_audio = true; + bool b_use_video = true; + + /* Initialize OLE/COM */ + CoInitialize( 0 ); var_Create( p_this, "dshow-config", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); var_Create( p_this, "dshow-tuner", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Create( p_this, "dshow-vdev", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_this, "dshow-vdev", &val ); - if( val.psz_string ) vdevname = string( val.psz_string ); - free( val.psz_string ); + psz_val = var_CreateGetString( p_this, "dshow-vdev" ); + if( psz_val ) + { + msg_Dbg( p_this, "dshow-vdev: %s", psz_val ) ; + /* skip none device */ + if ( strncasecmp( psz_val, "none", 4 ) != 0 ) + vdevname = string( psz_val ); + else + b_use_video = false ; + } + free( psz_val ); - var_Create( p_this, "dshow-adev", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_this, "dshow-adev", &val ); - if( val.psz_string ) adevname = string( val.psz_string ); - free( val.psz_string ); + psz_val = var_CreateGetString( p_this, "dshow-adev" ); + if( psz_val ) + { + msg_Dbg( p_this, "dshow-adev: %s", psz_val ) ; + /* skip none device */ + if ( strncasecmp( psz_val, "none", 4 ) != 0 ) + adevname = string( psz_val ); + else + b_use_audio = false ; + } + free( psz_val ); - static struct {char *psz_size; int i_width; int i_height;} size_table[] = + static struct {const char *psz_size; int i_width; int i_height;} size_table[] = { { "subqcif", 128, 96 }, { "qsif", 160, 120 }, { "qcif", 176, 144 }, { "sif", 320, 240 }, { "cif", 352, 288 }, { "d1", 640, 480 }, { 0, 0, 0 }, }; - var_Create( p_this, "dshow-size", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_this, "dshow-size", &val ); - if( val.psz_string && *val.psz_string ) + psz_val = var_CreateGetString( p_this, "dshow-size" ); + if( !EMPTY_STR(psz_val) ) { for( i = 0; size_table[i].psz_size; i++ ) { - if( !strcmp( val.psz_string, size_table[i].psz_size ) ) + if( !strcmp( psz_val, size_table[i].psz_size ) ) { i_width = size_table[i].i_width; i_height = size_table[i].i_height; @@ -388,7 +445,7 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, if( !size_table[i].psz_size ) /* Try to parse "WidthxHeight" */ { char *psz_parser; - i_width = strtol( val.psz_string, &psz_parser, 0 ); + i_width = strtol( psz_val, &psz_parser, 0 ); if( *psz_parser == 'x' || *psz_parser == 'X') { i_height = strtol( psz_parser + 1, &psz_parser, 0 ); @@ -396,22 +453,20 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, msg_Dbg( p_this, "width x height %dx%d", i_width, i_height ); } } - free( val.psz_string ); + free( psz_val ); - p_sys->b_chroma = false; - var_Create( p_this, "dshow-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_this, "dshow-chroma", &val ); - if( val.psz_string && strlen( val.psz_string ) >= 4 ) - { - i_chroma = VLC_FOURCC( val.psz_string[0], val.psz_string[1], - val.psz_string[2], val.psz_string[3] ); - p_sys->b_chroma = true; - } - free( val.psz_string ); + psz_val = var_CreateGetString( p_this, "dshow-chroma" ); + i_chroma = vlc_fourcc_GetCodecFromString( UNKNOWN_ES, psz_val ); + p_sys->b_chroma = i_chroma != 0; + free( psz_val ); var_Create( p_this, "dshow-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); var_Create( p_this, "dshow-tuner-channel", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_this, "dshow-tuner-frequency", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_this, "dshow-tuner-standard", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_this, "dshow-tuner-country", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_this, "dshow-tuner-input", @@ -427,12 +482,10 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, var_Create( p_this, "dshow-video-output", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); var_Create( p_this, "dshow-audio-output", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - /* Initialize OLE/COM */ - CoInitialize( 0 ); /* Initialize some data */ p_sys->i_streams = 0; - p_sys->pp_streams = 0; + p_sys->pp_streams = NULL; p_sys->i_width = i_width; p_sys->i_height = i_height; p_sys->i_chroma = i_chroma; @@ -441,17 +494,30 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, p_sys->p_capture_graph_builder2 = NULL; p_sys->p_control = NULL; + /* Build directshow graph */ + CreateDirectShowGraph( p_sys ); + vlc_mutex_init( &p_sys->lock ); vlc_cond_init( &p_sys->wait ); - /* Build directshow graph */ - CreateDirectShowGraph( p_sys ); + if( !b_use_video && !b_use_audio ) + { + dialog_Fatal( p_this, _("Capture failed"), + _("No video or audio device selected.") ); + return VLC_EGENERIC ; + } - if( OpenDevice( p_this, p_sys, vdevname, 0 ) != VLC_SUCCESS ) + if( !b_use_video ) + msg_Dbg( p_this, "skipping video device" ) ; + bool b_err_video = false ; + + if( b_use_video && OpenDevice( p_this, p_sys, vdevname, 0 ) != VLC_SUCCESS ) { - msg_Err( p_this, "can't open video"); + msg_Err( p_this, "can't open video device"); + b_err_video = true ; } - else + + if ( b_use_video && !b_err_video ) { /* Check if we can handle the demuxing ourselves or need to spawn * a demuxer module */ @@ -460,13 +526,11 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, if( p_stream->mt.majortype == MEDIATYPE_Video ) { if( /* Raw DV stream */ - p_stream->i_fourcc == VLC_FOURCC('d','v','s','l') || - p_stream->i_fourcc == VLC_FOURCC('d','v','s','d') || - p_stream->i_fourcc == VLC_FOURCC('d','v','h','d') || + p_stream->i_fourcc == VLC_CODEC_DV || /* Raw MPEG video stream */ - p_stream->i_fourcc == VLC_FOURCC('m','p','2','v') ) + p_stream->i_fourcc == VLC_CODEC_MPGV ) { - b_audio = false; + b_use_audio = false; if( b_access_demux ) { @@ -478,7 +542,7 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, if( p_stream->mt.majortype == MEDIATYPE_Stream ) { - b_audio = false; + b_use_audio = false; if( b_access_demux ) { @@ -486,8 +550,7 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, return VLC_EGENERIC; } - var_Get( p_this, "dshow-tuner", &val ); - if( val.b_bool ) + if( var_GetBool( p_this, "dshow-tuner" ) ) { /* FIXME: we do MEDIATYPE_Stream here so we don't do * it twice. */ @@ -497,25 +560,42 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, } } - if( b_audio && OpenDevice( p_this, p_sys, adevname, 1 ) != VLC_SUCCESS ) + if( !b_use_audio ) + msg_Dbg( p_this, "skipping audio device") ; + + bool b_err_audio = false ; + + if( b_use_audio && OpenDevice( p_this, p_sys, adevname, 1 ) != VLC_SUCCESS ) { - msg_Err( p_this, "can't open audio"); + msg_Err( p_this, "can't open audio device"); + b_err_audio = true ; + } + + if( ( b_use_video && b_err_video && b_use_audio && b_err_audio ) || + ( !b_use_video && b_use_audio && b_err_audio ) || + ( b_use_video && !b_use_audio && b_err_video ) ) + { + msg_Err( p_this, "FATAL: could not open ANY device" ) ; + dialog_Fatal( p_this, _("Capture failed"), + _("VLC cannot open ANY capture device." + "Check the error log for details.") ); + return VLC_EGENERIC ; } for( i = p_sys->i_crossbar_route_depth-1; i >= 0 ; --i ) { - var_Get( p_this, "dshow-video-input", &val ); - if( val.i_int >= 0 ) - p_sys->crossbar_routes[i].VideoInputIndex=val.i_int; - var_Get( p_this, "dshow-video-output", &val ); - if( val.i_int >= 0 ) - p_sys->crossbar_routes[i].VideoOutputIndex=val.i_int; - var_Get( p_this, "dshow-audio-input", &val ); - if( val.i_int >= 0 ) - p_sys->crossbar_routes[i].AudioInputIndex=val.i_int; - var_Get( p_this, "dshow-audio-output", &val ); - if( val.i_int >= 0 ) - p_sys->crossbar_routes[i].AudioOutputIndex=val.i_int; + int i_val = var_GetInteger( p_this, "dshow-video-input" ); + if( i_val >= 0 ) + p_sys->crossbar_routes[i].VideoInputIndex = i_val; + i_val = var_GetInteger( p_this, "dshow-video-output" ); + if( i_val >= 0 ) + p_sys->crossbar_routes[i].VideoOutputIndex = i_val; + i_val = var_GetInteger( p_this, "dshow-audio-input" ); + if( i_val >= 0 ) + p_sys->crossbar_routes[i].AudioInputIndex = i_val; + i_val = var_GetInteger( p_this, "dshow-audio-output" ); + if( i_val >= 0 ) + p_sys->crossbar_routes[i].AudioOutputIndex = i_val; IAMCrossbar *pXbar = p_sys->crossbar_routes[i].pXbar; LONG VideoInputIndex = p_sys->crossbar_routes[i].VideoInputIndex; @@ -540,13 +620,15 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys, } } } + else + msg_Err( p_this, "crossbar at depth %d could not route video " + "output %ld to input %ld", i, VideoOutputIndex, VideoInputIndex ); } /* ** Show properties pages from other filters in graph */ - var_Get( p_this, "dshow-config", &val ); - if( val.b_bool ) + if( var_GetBool( p_this, "dshow-config" ) ) { for( i = p_sys->i_crossbar_route_depth-1; i >= 0 ; --i ) { @@ -579,10 +661,9 @@ static int DemuxOpen( vlc_object_t *p_this ) access_sys_t *p_sys; int i; - p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) ); + p_sys = (access_sys_t*)calloc( 1, sizeof( access_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; - memset( p_sys, 0, sizeof( access_sys_t ) ); p_demux->p_sys = (demux_sys_t *)p_sys; if( CommonOpen( p_this, p_sys, true ) != VLC_SUCCESS ) @@ -608,11 +689,25 @@ static int DemuxOpen( vlc_object_t *p_this ) if( p_stream->mt.majortype == MEDIATYPE_Video ) { + char *psz_aspect = var_CreateGetString( p_this, "dshow-aspect-ratio" ); + char *psz_delim = !EMPTY_STR( psz_aspect ) ? strchr( psz_aspect, ':' ) : NULL; + es_format_Init( &fmt, VIDEO_ES, p_stream->i_fourcc ); fmt.video.i_width = p_stream->header.video.bmiHeader.biWidth; fmt.video.i_height = p_stream->header.video.bmiHeader.biHeight; - fmt.video.i_aspect = 4 * VOUT_ASPECT_FACTOR / 3; + + if( psz_delim ) + { + fmt.video.i_sar_num = atoi( psz_aspect ) * fmt.video.i_height; + fmt.video.i_sar_den = atoi( psz_delim + 1 ) * fmt.video.i_width; + } + else + { + fmt.video.i_sar_num = 4 * fmt.video.i_height; + fmt.video.i_sar_den = 3 * fmt.video.i_width; + } + free( psz_aspect ); if( !p_stream->header.video.bmiHeader.biCompression ) { @@ -621,12 +716,14 @@ static int DemuxOpen( vlc_object_t *p_this ) } /* Setup rgb mask for RGB formats */ - if( p_stream->i_fourcc == VLC_FOURCC('R','V','2','4') ) + if( p_stream->i_fourcc == VLC_CODEC_RGB24 ) { - /* This is in BGR format */ - fmt.video.i_bmask = 0x00ff0000; + /* This is in RGB format + http://msdn.microsoft.com/en-us/library/dd407253%28VS.85%29.aspx?ppud=4 + */ + fmt.video.i_rmask = 0x00ff0000; fmt.video.i_gmask = 0x0000ff00; - fmt.video.i_rmask = 0x000000ff; + fmt.video.i_bmask = 0x000000ff; } if( p_stream->header.video.AvgTimePerFrame ) @@ -663,10 +760,9 @@ static int AccessOpen( vlc_object_t *p_this ) access_t *p_access = (access_t*)p_this; access_sys_t *p_sys; - p_access->p_sys = p_sys = (access_sys_t *)malloc( sizeof( access_sys_t ) ); + p_access->p_sys = p_sys = (access_sys_t*)calloc( 1, sizeof( access_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; - memset( p_sys, 0, sizeof( access_sys_t ) ); if( CommonOpen( p_this, p_sys, false ) != VLC_SUCCESS ) { @@ -679,14 +775,12 @@ static int AccessOpen( vlc_object_t *p_this ) /* Check if we need to force demuxers */ if( !p_access->psz_demux || !*p_access->psz_demux ) { - if( p_stream->i_fourcc == VLC_FOURCC('d','v','s','l') || - p_stream->i_fourcc == VLC_FOURCC('d','v','s','d') || - p_stream->i_fourcc == VLC_FOURCC('d','v','h','d') ) + if( p_stream->i_fourcc == VLC_CODEC_DV ) { free( p_access->psz_demux ); p_access->psz_demux = strdup( "rawdv" ); } - else if( p_stream->i_fourcc == VLC_FOURCC('m','p','2','v') ) + else if( p_stream->i_fourcc == VLC_CODEC_MPGV ) { free( p_access->psz_demux ); p_access->psz_demux = strdup( "mpgv" ); @@ -784,16 +878,16 @@ static bool ConnectFilters( vlc_object_t *p_this, access_sys_t *p_sys, // Sort out all the possible video inputs // The class needs to be given the capture filters ANALOGVIDEO input pin - IEnumPins *pins = 0; + IEnumPins *pins = NULL; if( ( mediaType.majortype == MEDIATYPE_Video || mediaType.majortype == MEDIATYPE_Stream ) && SUCCEEDED(p_filter->EnumPins(&pins)) ) { - IPin *pP = 0; + IPin *pP = NULL; ULONG n; PIN_INFO pinInfo; BOOL Found = FALSE; - IKsPropertySet *pKs=0; + IKsPropertySet *pKs = NULL; GUID guid; DWORD dw; @@ -825,6 +919,9 @@ static bool ConnectFilters( vlc_object_t *p_this, access_sys_t *p_sys, pP->Release(); } pins->Release(); + msg_Dbg( p_this, "ConnectFilters: graph_builder2 available.") ; + if ( !Found ) + msg_Warn( p_this, "ConnectFilters: No crossBar routes found (incobatible pin types)" ) ; } return true; } @@ -863,17 +960,17 @@ static int GetFourCCPriority( int i_fourcc ) { switch( i_fourcc ) { - case VLC_FOURCC('I','4','2','0'): - case VLC_FOURCC('f','l','3','2'): + case VLC_CODEC_I420: + case VLC_CODEC_FL32: return 9; - case VLC_FOURCC('Y','V','1','2'): + case VLC_CODEC_YV12: case VLC_FOURCC('a','r','a','w'): return 8; - case VLC_FOURCC('R','V','2','4'): + case VLC_CODEC_RGB24: return 7; - case VLC_FOURCC('Y','U','Y','2'): - case VLC_FOURCC('R','V','3','2'): - case VLC_FOURCC('R','G','B','A'): + case VLC_CODEC_YUYV: + case VLC_CODEC_RGB32: + case VLC_CODEC_RGBA: return 6; } @@ -900,7 +997,6 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, /* Enumerate devices and display their names */ FindCaptureDevice( p_this, NULL, &list_devices, b_audio ); - if( !list_devices.size() ) return VLC_EGENERIC; @@ -911,22 +1007,26 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, /* If no device name was specified, pick the 1st one */ if( devicename.size() == 0 ) { + /* When none selected */ devicename = *list_devices.begin(); + msg_Dbg( p_this, "asking for default device: %s", devicename.c_str() ) ; } - + else + msg_Dbg( p_this, "asking for device: %s", devicename.c_str() ) ; // Use the system device enumerator and class enumerator to find // a capture/preview device, such as a desktop USB video camera. IBaseFilter *p_device_filter = - FindCaptureDevice( p_this, &devicename, 0, b_audio ); + FindCaptureDevice( p_this, &devicename, NULL, b_audio ); + if( p_device_filter ) msg_Dbg( p_this, "using device: %s", devicename.c_str() ); else { msg_Err( p_this, "can't use device: %s, unsupported device type", devicename.c_str() ); - intf_UserFatal( p_this, false, _("Capturing failed"), + dialog_Fatal( p_this, _("Capture failed"), _("VLC cannot use the device \"%s\", because its " - "type is not supported.") ); + "type is not supported."), devicename.c_str() ); return VLC_EGENERIC; } @@ -977,7 +1077,7 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, else { /* capture device */ msg_Err( p_this, "capture device '%s' does not support required parameters !", devicename.c_str() ); - intf_UserFatal( p_this, false, _("Capturing failed"), + dialog_Fatal( p_this, _("Capture failed"), _("The capture device \"%s\" does not support the " "required parameters."), devicename.c_str() ); p_device_filter->Release(); @@ -1008,9 +1108,7 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, /* Show Device properties. Done here so the VLC stream is setup with * the proper parameters. */ - vlc_value_t val; - var_Get( p_this, "dshow-config", &val ); - if( val.b_bool ) + if( var_GetBool( p_this, "dshow-config" ) ) { ShowDeviceProperties( p_this, p_sys->p_capture_graph_builder2, p_device_filter, b_audio ); @@ -1019,8 +1117,8 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, ConfigTuner( p_this, p_sys->p_capture_graph_builder2, p_device_filter ); - var_Get( p_this, "dshow-tuner", &val ); - if( val.b_bool && dshow_stream.mt.majortype != MEDIATYPE_Stream ) + if( var_GetBool( p_this, "dshow-tuner" ) && + dshow_stream.mt.majortype != MEDIATYPE_Stream ) { /* FIXME: we do MEDIATYPE_Stream later so we don't do it twice. */ ShowTunerProperties( p_this, p_sys->p_capture_graph_builder2, @@ -1065,8 +1163,8 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, dshow_stream.p_device_filter = p_device_filter; dshow_stream.p_capture_filter = p_capture_filter; - p_sys->pp_streams = (dshow_stream_t **)realloc( p_sys->pp_streams, - sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) ); + p_sys->pp_streams = (dshow_stream_t **)xrealloc( p_sys->pp_streams, + sizeof(dshow_stream_t *) * (p_sys->i_streams + 1) ); p_sys->pp_streams[p_sys->i_streams] = new dshow_stream_t; *p_sys->pp_streams[p_sys->i_streams++] = dshow_stream; @@ -1086,6 +1184,11 @@ static int OpenDevice( vlc_object_t *p_this, access_sys_t *p_sys, return VLC_EGENERIC; } +/* FindCaptureDevices:: This Function had two purposes : + Returns the list of capture devices when p_listdevices != NULL + Creates an IBaseFilter when p_devicename corresponds to an existing devname + These actions *may* be requested whith a single call. +*/ static IBaseFilter * FindCaptureDevice( vlc_object_t *p_this, string *p_devicename, list *p_listdevices, bool b_audio ) @@ -1126,7 +1229,7 @@ FindCaptureDevice( vlc_object_t *p_this, string *p_devicename, * CreateClassEnumerator will succeed, but p_class_enum will be NULL */ if( p_class_enum == NULL ) { - msg_Err( p_this, "no capture device was detected" ); + msg_Err( p_this, "no %s capture device was detected", ( b_audio ? "audio" : "video" ) ); return NULL; } @@ -1150,40 +1253,35 @@ FindCaptureDevice( vlc_object_t *p_this, string *p_devicename, p_bag->Release(); if( SUCCEEDED(hr) ) { - int i_convert = WideCharToMultiByte(CP_ACP, 0, var.bstrVal, - SysStringLen(var.bstrVal), NULL, 0, NULL, NULL); - char *p_buf = (char *)alloca( i_convert+1 ); p_buf[0] = 0; - WideCharToMultiByte( CP_ACP, 0, var.bstrVal, - SysStringLen(var.bstrVal), p_buf, i_convert, NULL, NULL ); - SysFreeString(var.bstrVal); - p_buf[i_convert] = '\0'; - - string devname = string(p_buf); - - int dup = 0; - /* find out if this name is already used by a previously found device */ - list::const_iterator iter = devicelist.begin(); - list::const_iterator end = devicelist.end(); - while ( iter != end ) - { - if( 0 == (*iter).compare(0, devname.size(), devname) ) - ++dup; - ++iter; - } - if( dup ) - { - /* we have a duplicate device name, append a sequence number to name - to provive a unique list back to the user */ - char seq[16]; - sprintf(seq, " #%d", dup); - devname.append(seq); - } - devicelist.push_back( devname ); + char *p_buf = FromWide( var.bstrVal ); + string devname = string(p_buf); + free( p_buf) ; + + int dup = 0; + /* find out if this name is already used by a previously found device */ + list::const_iterator iter = devicelist.begin(); + list::const_iterator end = devicelist.end(); + string ordevname = devname ; + while ( iter != end ) + { + if( 0 == (*iter).compare( devname ) ) + { /* devname is on the list. Try another name with sequence + number apended and then rescan until a unique entry is found*/ + char seq[16]; + snprintf(seq, 16, " #%d", ++dup); + devname = ordevname + seq; + iter = devicelist.begin(); + } + else + ++iter; + } + devicelist.push_back( devname ); if( p_devicename && *p_devicename == devname ) { - /* Bind Moniker to a filter object */ - hr = p_moniker->BindToObject( 0, 0, IID_IBaseFilter, + msg_Dbg( p_this, "asked for %s, binding to %s", p_devicename->c_str() , devname.c_str() ) ; + /* NULL possibly means we don't need BindMoniker BindCtx ?? */ + hr = p_moniker->BindToObject( NULL, 0, IID_IBaseFilter, (void **)&p_base_filter ); if( FAILED(hr) ) { @@ -1426,7 +1524,7 @@ static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter, val = i_bitspersample; if( ! val ) { - if( VLC_FOURCC('f', 'l', '3', '2') == i_current_fourcc ) + if( VLC_CODEC_FL32 == i_current_fourcc ) val = 32; else val = 16; @@ -1573,7 +1671,7 @@ static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter, } else { - char *psz_type = "unknown"; + const char * psz_type = "unknown"; if( p_mt->majortype == MEDIATYPE_Video ) psz_type = "video"; if( p_mt->majortype == MEDIATYPE_Audio ) psz_type = "audio"; if( p_mt->majortype == MEDIATYPE_Stream ) psz_type = "stream"; @@ -1603,7 +1701,7 @@ static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter, { // output format for 'Hauppauge WinTV PVR PCI II Capture' // try I420 as an input format - i_current_fourcc = VLC_FOURCC('I','4','2','0'); + i_current_fourcc = VLC_CODEC_I420; if( !i_fourcc || i_fourcc == i_current_fourcc ) { // return alternative media type @@ -1628,7 +1726,7 @@ static size_t EnumDeviceCaps( vlc_object_t *p_this, IBaseFilter *p_filter, ((VIDEOINFOHEADER *)p_mt->pbFormat)->bmiHeader.biHeight; vh.bmiHeader.biPlanes = 3; vh.bmiHeader.biBitCount = 12; - vh.bmiHeader.biCompression = VLC_FOURCC('I','4','2','0'); + vh.bmiHeader.biCompression = VLC_CODEC_I420; vh.bmiHeader.biSizeImage = vh.bmiHeader.biWidth * 12 * vh.bmiHeader.biHeight / 8; mtr.lSampleSize = vh.bmiHeader.biSizeImage; @@ -1671,7 +1769,7 @@ static block_t *ReadCompressed( access_t *p_access ) while( 1 ) { - if( !vlc_object_alive (p_access) || p_access->b_error ) return 0; + if( !vlc_object_alive (p_access) ) return NULL; /* Get new sample/frame from the elementary stream (blocking). */ vlc_mutex_lock( &p_sys->lock ); @@ -1708,7 +1806,7 @@ static block_t *ReadCompressed( access_t *p_access ) return p_block; } - return 0; /* never reached */ + return NULL; /* never reached */ } /**************************************************************************** @@ -1810,9 +1908,8 @@ static int Demux( demux_t *p_demux ) *****************************************************************************/ static int AccessControl( access_t *p_access, int i_query, va_list args ) { - bool *pb_bool; - int *pi_int; - int64_t *pi_64; + bool *pb_bool; + int64_t *pi_64; switch( i_query ) { @@ -1826,14 +1923,9 @@ static int AccessControl( access_t *p_access, int i_query, va_list args ) break; /* */ - case ACCESS_GET_MTU: - pi_int = (int*)va_arg( args, int * ); - *pi_int = 0; - break; - case ACCESS_GET_PTS_DELAY: pi_64 = (int64_t*)va_arg( args, int64_t * ); - *pi_64 = (int64_t)var_GetInteger( p_access, "dshow-caching" ) * 1000; + *pi_64 = var_GetInteger( p_access, "dshow-caching" ) * 1000; break; /* */ @@ -1857,8 +1949,8 @@ static int AccessControl( access_t *p_access, int i_query, va_list args ) ****************************************************************************/ static int DemuxControl( demux_t *p_demux, int i_query, va_list args ) { - bool *pb; - int64_t *pi64; + bool *pb; + int64_t *pi64; switch( i_query ) { @@ -1873,7 +1965,7 @@ static int DemuxControl( demux_t *p_demux, int i_query, va_list args ) case DEMUX_GET_PTS_DELAY: pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = (int64_t)var_GetInteger( p_demux, "dshow-caching" ) * 1000; + *pi64 = var_GetInteger( p_demux, "dshow-caching" ) * 1000; return VLC_SUCCESS; case DEMUX_GET_TIME: @@ -1930,13 +2022,11 @@ static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, /* Uninitialize OLE/COM */ CoUninitialize(); - if( !list_devices.size() ) return VLC_SUCCESS; + if( list_devices.empty() ) return VLC_SUCCESS; - p_item->ppsz_list = - (char **)realloc( p_item->ppsz_list, + p_item->ppsz_list = (char**)xrealloc( p_item->ppsz_list, (list_devices.size()+3) * sizeof(char *) ); - p_item->ppsz_list_text = - (char **)realloc( p_item->ppsz_list_text, + p_item->ppsz_list_text = (char**)xrealloc( p_item->ppsz_list_text, (list_devices.size()+3) * sizeof(char *) ); list::iterator iter; @@ -1961,20 +2051,31 @@ static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name, { module_config_t *p_item; bool b_audio = false; + char *psz_device = NULL; + int i_ret = VLC_SUCCESS; + + if( !EMPTY_STR( newval.psz_string ) ) + psz_device = strdup( newval.psz_string ); /* Initialize OLE/COM */ CoInitialize( 0 ); p_item = config_FindConfig( p_this, psz_name ); - if( !p_item ) return VLC_SUCCESS; + + if( !p_item ) + { + free( psz_device ); + CoUninitialize(); + return VLC_SUCCESS; + } if( !strcmp( psz_name, "dshow-adev" ) ) b_audio = true; string devicename; - if( newval.psz_string && *newval.psz_string ) + if( psz_device ) { - devicename = newval.psz_string; + devicename = psz_device ; } else { @@ -1983,7 +2084,11 @@ static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name, /* Enumerate devices */ FindCaptureDevice( p_this, NULL, &list_devices, b_audio ); - if( !list_devices.size() ) return VLC_EGENERIC; + if( list_devices.empty() ) + { + CoUninitialize(); + return VLC_EGENERIC; + } devicename = *list_devices.begin(); } @@ -1996,22 +2101,21 @@ static int ConfigDevicesCallback( vlc_object_t *p_this, char const *psz_name, } else { - /* Uninitialize OLE/COM */ - CoUninitialize(); - msg_Err( p_this, "didn't find device: %s", devicename.c_str() ); - return VLC_EGENERIC; + i_ret = VLC_EGENERIC; } /* Uninitialize OLE/COM */ CoUninitialize(); - return VLC_SUCCESS; + free( psz_device ); + return i_ret; } /***************************************************************************** * Properties *****************************************************************************/ + static void ShowPropertyPage( IUnknown *obj ) { ISpecifyPropertyPages *p_spec; @@ -2153,7 +2257,7 @@ static void ShowTunerProperties( vlc_object_t *p_this, static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph, IBaseFilter *p_device_filter ) { - int i_channel, i_country, i_input, i_amtuner_mode; + int i_channel, i_country, i_input, i_amtuner_mode, i_standard, i_frequency; long l_modes = 0; IAMTVTuner *p_TV; HRESULT hr; @@ -2164,11 +2268,15 @@ static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph, i_country = var_GetInteger( p_this, "dshow-tuner-country" ); i_input = var_GetInteger( p_this, "dshow-tuner-input" ); i_amtuner_mode = var_GetInteger( p_this, "dshow-amtuner-mode" ); + i_frequency = var_GetInteger( p_this, "dshow-tuner-frequency" ); + i_standard = + i_standards_list[var_CreateGetInteger( p_this, "dshow-tuner-standard" )]; + + if( !i_channel && !i_frequency && !i_country && !i_input ) return; /* Nothing to do */ - if( !i_channel && !i_country && !i_input ) return; /* Nothing to do */ + msg_Dbg( p_this, "tuner config: channel %i, frequency %i, country %i, input type %i, standard %s", + i_channel, i_frequency, i_country, i_input, ppsz_standards_list_text[i_standard] ); - msg_Dbg( p_this, "tuner config: channel %i, country %i, input type %i", - i_channel, i_country, i_input ); hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, p_device_filter, IID_IAMTVTuner, @@ -2203,7 +2311,107 @@ static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph, else if( i_input == 2 ) p_TV->put_InputType( 0, TunerInputAntenna ); p_TV->put_CountryCode( i_country ); - p_TV->put_Channel( i_channel, AMTUNER_SUBCHAN_NO_TUNE, + + if( i_frequency <= 0 ) p_TV->put_Channel( i_channel, AMTUNER_SUBCHAN_NO_TUNE, AMTUNER_SUBCHAN_NO_TUNE ); + + if( i_frequency > 0 || i_standard > 0) { + IKsPropertySet *pKs = NULL; + DWORD dw_supported = 0; + KSPROPERTY_TUNER_MODE_CAPS_S ModeCaps; + KSPROPERTY_TUNER_FREQUENCY_S Frequency; + KSPROPERTY_TUNER_STANDARD_S Standard; + + hr = p_TV->QueryInterface(IID_IKsPropertySet,(void **)&pKs); + if (FAILED(hr)) + { + msg_Dbg( p_this, "Couldn't QI for IKsPropertySet" ); + return; + } + + memset(&ModeCaps,0,sizeof(KSPROPERTY_TUNER_MODE_CAPS_S)); + memset(&Frequency,0,sizeof(KSPROPERTY_TUNER_FREQUENCY_S)); + memset(&Standard,0,sizeof(KSPROPERTY_TUNER_STANDARD_S)); + ModeCaps.Mode = AMTUNER_MODE_TV; + + hr = pKs->QuerySupported(PROPSETID_TUNER, + KSPROPERTY_TUNER_MODE_CAPS,&dw_supported); + if(SUCCEEDED(hr) && dw_supported&KSPROPERTY_SUPPORT_GET) + { + DWORD cbBytes=0; + hr = pKs->Get(PROPSETID_TUNER,KSPROPERTY_TUNER_MODE_CAPS, + INSTANCEDATA_OF_PROPERTY_PTR(&ModeCaps), + INSTANCEDATA_OF_PROPERTY_SIZE(ModeCaps), + &ModeCaps, + sizeof(ModeCaps), + &cbBytes); + } + else + { + msg_Dbg( p_this, "KSPROPERTY_TUNER_MODE_CAPS not supported!" ); + return; + } + + msg_Dbg( p_this, "Frequency range supproted from %d to %d.", ModeCaps.MinFrequency, ModeCaps.MaxFrequency); + msg_Dbg( p_this, "Video standards supproted by the tuner: "); + for(int i = 0 ; i < ARRAY_SIZE(ppsz_standards_list_text); i++) { + if(ModeCaps.StandardsSupported & i_standards_list[i]) + msg_Dbg( p_this, "%s, ", ppsz_standards_list_text[i]); + } + + if(i_frequency > 0) { + Frequency.Frequency=i_frequency; + if(ModeCaps.Strategy==KS_TUNER_STRATEGY_DRIVER_TUNES) + Frequency.TuningFlags=KS_TUNER_TUNING_FINE; + else + Frequency.TuningFlags=KS_TUNER_TUNING_EXACT; + + if(i_frequency>=ModeCaps.MinFrequency && i_frequency<=ModeCaps.MaxFrequency) + { + + hr = pKs->Set(PROPSETID_TUNER, + KSPROPERTY_TUNER_FREQUENCY, + INSTANCEDATA_OF_PROPERTY_PTR(&Frequency), + INSTANCEDATA_OF_PROPERTY_SIZE(Frequency), + &Frequency, + sizeof(Frequency)); + if(FAILED(hr)) + { + msg_Dbg( p_this, "Couldn't set KSPROPERTY_TUNER_FREQUENCY!" ); + return; + } + } + else + { + msg_Dbg( p_this, "Requested frequency exceeds the supported range!" ); + return; + } + } + + if(i_standard > 0) { + if(i_standard & ModeCaps.StandardsSupported ) + { + Standard.Standard = i_standard; + hr = pKs->Set(PROPSETID_TUNER, + KSPROPERTY_TUNER_STANDARD, + INSTANCEDATA_OF_PROPERTY_PTR(&Standard), + INSTANCEDATA_OF_PROPERTY_SIZE(Standard), + &Standard, + sizeof(Standard)); + if(FAILED(hr)) + { + msg_Dbg( p_this, "Couldn't set KSPROPERTY_TUNER_STANDARD!" ); + return; + } + } + else + { + msg_Dbg( p_this, "Requested video standard is not supported by the tuner!" ); + return; + } + } + pKs->Release(); + } + p_TV->Release(); }