# HG changeset patch # User voroshil # Date 1192295679 0 # Node ID d81eef9beb1ba81f504cc7b0c036abb14e44229a # Parent acb856c03bd81863869d94eecbb8c28f56de939a DirectShow based tv:// driver for win32 Teletext is also supported (but 625 system parameters are hardcoded). pthreads is required for teletext. Code is still experimental. diff -r acb856c03bd8 -r d81eef9beb1b DOCS/man/en/mplayer.1 --- a/DOCS/man/en/mplayer.1 Sat Oct 13 14:16:37 2007 +0000 +++ b/DOCS/man/en/mplayer.1 Sat Oct 13 17:14:39 2007 +0000 @@ -1909,6 +1909,23 @@ Useful when the teletext system uses a non-latin character set, but language codes are not transmitted via teletext type 28 packets for some reason. To see a list of supported language codes set this option to \-1. +.IPs "hidden_video_renderer (dshow only)" +Terminate stream with video renderer instead of Null renderer (default: off). +Will help if video freezes but audio does not. +NOTE: May not work with \-vo directx and \-vf crop combination. +.IPs "hidden_vp_renderer (dshow only)" +Terminate VideoPort pin stream with video renderer +instead of removing it from the graph (default: off). +Useful if your card has a VideoPort pin and video is choppy. +NOTE: May not work with \-vo directx and \-vf crop combination. +.IPs "system_clock (dshow only)" +Use system clock as sync source instead of the default graph clock (usually the clock +from one of the live sources in graph). +.IPs "normalize_audio_chunks (dshow only)" +Create audio chunks with a time length equal to +video frame time length (default: off). +Some audio cards create audio chunks about 0.5s in size, resulting in +choppy video when using immediatemode=0. .RE . .TP diff -r acb856c03bd8 -r d81eef9beb1b cfg-common.h --- a/cfg-common.h Sat Oct 13 14:16:37 2007 +0000 +++ b/cfg-common.h Sat Oct 13 17:14:39 2007 +0000 @@ -426,7 +426,7 @@ {"chanlist", &stream_tv_defaults.chanlist, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"norm", &stream_tv_defaults.norm, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"automute", &stream_tv_defaults.automute, CONF_TYPE_INT, CONF_RANGE, 0, 255, NULL}, -#ifdef HAVE_TV_V4L2 +#if defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) {"normid", &stream_tv_defaults.normid, CONF_TYPE_INT, 0, 0, 0, NULL}, #endif {"width", &stream_tv_defaults.width, CONF_TYPE_INT, 0, 0, 4096, NULL}, @@ -440,9 +440,12 @@ {"hue", &stream_tv_defaults.hue, CONF_TYPE_INT, CONF_RANGE, -100, 100, NULL}, {"saturation", &stream_tv_defaults.saturation, CONF_TYPE_INT, CONF_RANGE, -100, 100, NULL}, {"gain", &stream_tv_defaults.gain, CONF_TYPE_INT, CONF_RANGE, -1, 100, NULL}, -#if defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2) +#if defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) + {"buffersize", &stream_tv_defaults.buffer_size, CONF_TYPE_INT, CONF_RANGE, 16, 1024, NULL}, {"amode", &stream_tv_defaults.amode, CONF_TYPE_INT, CONF_RANGE, 0, 3, NULL}, {"volume", &stream_tv_defaults.volume, CONF_TYPE_INT, CONF_RANGE, 0, 65535, NULL}, +#endif +#if defined(HAVE_TV_V4L) || defined(HAVE_TV_V4L2) {"bass", &stream_tv_defaults.bass, CONF_TYPE_INT, CONF_RANGE, 0, 65535, NULL}, {"treble", &stream_tv_defaults.treble, CONF_TYPE_INT, CONF_RANGE, 0, 65535, NULL}, {"balance", &stream_tv_defaults.balance, CONF_TYPE_INT, CONF_RANGE, 0, 65535, NULL}, @@ -464,6 +467,16 @@ {"tlang", &stream_tv_defaults.tlang, CONF_TYPE_INT, CONF_RANGE, -1, 0x7f, NULL}, #endif /* HAVE_TV_TELETEXT */ {"audioid", &stream_tv_defaults.audio_id, CONF_TYPE_INT, CONF_RANGE, 0, 9, NULL}, +#ifdef HAVE_TV_DSHOW + {"hidden_video_renderer", &stream_tv_defaults.hidden_video_renderer, CONF_TYPE_FLAG, 0, 0, 1, NULL}, + {"nohidden_video_renderer", &stream_tv_defaults.hidden_video_renderer, CONF_TYPE_FLAG, 0, 0, 0, NULL}, + {"hidden_vp_renderer", &stream_tv_defaults.hidden_vp_renderer, CONF_TYPE_FLAG, 0, 0, 1, NULL}, + {"nohidden_vp_renderer", &stream_tv_defaults.hidden_vp_renderer, CONF_TYPE_FLAG, 0, 0, 0, NULL}, + {"system_clock", &stream_tv_defaults.system_clock, CONF_TYPE_FLAG, 0, 0, 1, NULL}, + {"nosystem_clock", &stream_tv_defaults.system_clock, CONF_TYPE_FLAG, 0, 0, 0, NULL}, + {"normalize_audio_chunks", &stream_tv_defaults.normalize_audio_chunks, CONF_TYPE_FLAG, 0, 0, 1, NULL}, + {"nonormalize_audio_chunks", &stream_tv_defaults.normalize_audio_chunks, CONF_TYPE_FLAG, 0, 0, 0, NULL}, +#endif {NULL, NULL, 0, 0, 0, 0, NULL} }; #endif /* USE_TV */ diff -r acb856c03bd8 -r d81eef9beb1b configure --- a/configure Sat Oct 13 14:16:37 2007 +0000 +++ b/configure Sat Oct 13 17:14:39 2007 +0000 @@ -612,6 +612,7 @@ _tv_v4l1=auto _tv_v4l2=auto _tv_bsdbt848=auto +_tv_dshow=auto _tv_teletext=auto _pvr=auto _network=yes @@ -975,6 +976,8 @@ --disable-tv-v4l1) _tv_v4l1=no ;; --enable-tv-v4l2) _tv_v4l2=yes ;; --disable-tv-v4l2) _tv_v4l2=no ;; + --enable-tv-dshow) _tv_dshow=yes ;; + --disable-tv-dshow) _tv_dshow=no ;; --enable-tv-teletext) _tv_teletext=yes ;; --disable-tv-teletext) _tv_teletext=no ;; --enable-radio) _radio=yes ;; @@ -6805,6 +6808,32 @@ fi #if bsd +echocheck "DirectShow TV interface" +if test "$_tv_dshow" = auto ; then + _tv_dshow=no + if test "$_tv" = yes && win32 ; then + cat > $TMPC < +int main(void) { + void* p; + CoCreateInstance((GUID*)&GUID_NULL, NULL, CLSCTX_INPROC_SERVER, &GUID_NULL, &p); + return 0; +} +EOF + cc_check -lole32 -luuid && _tv_dshow=yes + fi +fi +if test "$_tv_dshow" = yes ; then + _inputmodules="tv-dshow $_inputmodules" + _def_tv_dshow='#define HAVE_TV_DSHOW 1' + _ld_extra="$_ld_extra -lole32 -luuid" +else + _noinputmodules="tv-dshow $_noinputmodules" + _def_tv_dshow='#undef HAVE_TV_DSHOW' +fi +echores "$_tv_dshow" + + echocheck "Video 4 Linux TV interface" if test "$_tv_v4l1" = auto ; then _tv_v4l1=no @@ -6859,8 +6888,8 @@ echocheck "TV teletext interface" if test "$_tv_teletext" = auto ; then _tv_teletext=no - if test "$_freetype" = yes && test "$_pthreads" = yes ; then - if test "$_tv_v4l2" = yes || test "$_v4l" = yes ; then + if test "$_freetype" = yes && test "$_pthreads" = yes; then + if test "$_tv_v4l2" = yes || test "$_v4l" = yes || test "$_tv_dshow" = yes; then _tv_teletext=yes fi fi @@ -7621,6 +7650,7 @@ TV_V4L = $_tv_v4l TV_V4L1 = $_tv_v4l1 TV_V4L2 = $_tv_v4l2 +TV_DSHOW = $_tv_dshow TV_BSDBT848 = $_tv_bsdbt848 TV_TELETEXT = $_tv_teletext AUDIO_INPUT = $_audio_input @@ -8167,6 +8197,9 @@ /* Enable Video 4 Linux 2 TV interface support */ $_def_tv_v4l2 +/* Enable DirectShow TV interface support */ +$_def_tv_dshow + /* *BSD BrookTree headers */ $_def_ioctl_meteor_h_name $_def_ioctl_bt848_h_name diff -r acb856c03bd8 -r d81eef9beb1b help/help_mp-en.h --- a/help/help_mp-en.h Sat Oct 13 14:16:37 2007 +0000 +++ b/help/help_mp-en.h Sat Oct 13 17:14:39 2007 +0000 @@ -2099,3 +2099,49 @@ #define MSGTR_TV_Bt848UnableToStopCapture "tvi_bsdbt848: Unable to stop capture. Error: %s\n" #define MSGTR_TV_TTSupportedLanguages "Supported Teletext languages:\n" #define MSGTR_TV_TTSelectedLanguage "Selected default teletext language: %s\n" + +//tvi_dshow.c +#define MSGTR_TVI_DS_UnableConnectInputVideoDecoder "Unable to connect given input to video decoder. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableConnectInputAudioDecoder "Unable to connect given input to audio decoder. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableSelectVideoFormat "tvi_dshow: Unable to select video format. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableSelectAudioFormat "tvi_dshow: Unable to select audio format. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableGetMediaControlInterface "tvi_dshow: Unable to get IMediaControl interface. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableStartGraph "tvi_dshow: Unable to start graph! Error:0x%x\n" +#define MSGTR_TVI_DS_DeviceNotFound "tvi_dshow: Device #%d not found\n" +#define MSGTR_TVI_DS_UnableGetDeviceName "tvi_dshow: Unable to get name for device #%d\n" +#define MSGTR_TVI_DS_UsingDevice "tvi_dshow: Using device #%d: %s\n" +#define MSGTR_TVI_DS_DeviceName "tvi_dshow: Device #%d: %s\n" +#define MSGTR_TVI_DS_DirectGetFreqFailed "tvi_dshow: Unable to get frequency directly. OS built-in channels table will be used.\n" +#define MSGTR_TVI_DS_DirectSetFreqFailed "tvi_dshow: Unable to set frequency directly. OS built-in channels table will be used.\n" +#define MSGTR_TVI_DS_SupportedNorms "tvi_dshow: supported norms:" +#define MSGTR_TVI_DS_AvailableVideoInputs "tvi_dshow: available video inputs:" +#define MSGTR_TVI_DS_AvailableAudioInputs "tvi_dshow: available audio inputs:" +//following phrase will be printed near the selected audio/video input +#define MSGTR_TVI_DS_InputSelected "(selected)" +#define MSGTR_TVI_DS_UnableExtractFreqTable "tvi_dshow: Unable to load frequency table from kstvtune.ax\n" +#define MSGTR_TVI_DS_WrongDeviceParam "tvi_dshow: Wrong device parameter: %s\n" +#define MSGTR_TVI_DS_WrongDeviceIndex "tvi_dshow: Wrong device index: %d\n" +#define MSGTR_TVI_DS_WrongADeviceParam "tvi_dshow: Wrong adevice parameter: %s\n" +#define MSGTR_TVI_DS_WrongADeviceIndex "tvi_dshow: Wrong adevice index: %d\n" + +#define MSGTR_TVI_DS_SamplerateNotsupported "tvi_dshow: Samplerate %d is not supported by device. Failing back to first available.\n" +#define MSGTR_TVI_DS_VideoAdjustigNotSupported "tvi_dshow: Adjusting of brightness/hue/saturation/contrast is not supported by device\n" + +#define MSGTR_TVI_DS_ChangingWidthHeightNotSupported "tvi_dshow: Changing video width/height is not supported by device.\n" +#define MSGTR_TVI_DS_SelectingInputNotSupported "tvi_dshow: Selection of capture source is not supported by device\n" +#define MSGTR_TVI_DS_FreqTableLoaded "tvi_dshow: loaded system (%s) frequency table for country id=%d (channels:%d).\n" +#define MSGTR_TVI_DS_ErrorParsingAudioFormatStruct "tvi_dshow: Unable to parse audio format structure.\n" +#define MSGTR_TVI_DS_ErrorParsingVideoFormatStruct "tvi_dshow: Unable to parse video format structure.\n" +#define MSGTR_TVI_DS_UnableSetAudioMode "tvi_dshow: Unable to set audio mode %d. Error:0x%x\n" +#define MSGTR_TVI_DS_UnsupportedMediaType "tvi_dshow: Unsupported media type passed to %s\n" +#define MSGTR_TVI_DS_UnableGetsupportedVideoFormats "tvi_dshow: Unable to get supported media formats from video pin. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableGetsupportedAudioFormats "tvi_dshow: Unable to get supported media formats from audio pin. Error:0x%x Disabling audio.\n" +#define MSGTR_TVI_DS_UnableFindNearestChannel "tvi_dshow: Unable to find nearest channel in system frequency table\n" +#define MSGTR_TVI_DS_UnableToSetChannel "tvi_dshow: Unable to switch to nearest channel from system frequency table. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableTerminateVPPin "tvi_dshow: Unable to terminate VideoPort pin with any filter in graph. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableBuildVideoSubGraph "tvi_dshow: Unable to build video chain of capture graph. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableBuildAudioSubGraph "tvi_dshow: Unable to build audio chain of capture graph. Error:0x%x\n" +#define MSGTR_TVI_DS_UnableBuildVBISubGraph "tvi_dshow: Unable to build VBI chain of capture graph. Error:0x%x\n" +#define MSGTR_TVI_DS_GraphInitFailure "tvi_dshow: Directshow graph initialization failure.\n" +#define MSGTR_TVI_DS_NoVideoCaptureDevice "tvi_dshow: Unable to find video capture device\n" +#define MSGTR_TVI_DS_NoAudioCaptureDevice "tvi_dshow: Unable to find audio capture device\n" diff -r acb856c03bd8 -r d81eef9beb1b stream/Makefile --- a/stream/Makefile Sat Oct 13 14:16:37 2007 +0000 +++ b/stream/Makefile Sat Oct 13 17:14:39 2007 +0000 @@ -52,6 +52,7 @@ SRCS_COMMON-$(STREAMING_LIVE555) += stream_livedotcom.c SRCS_COMMON-$(TV) += stream_tv.c tv.c frequencies.c tvi_dummy.c SRCS_COMMON-$(TV_BSDBT848) += tvi_bsdbt848.c +SRCS_COMMON-$(TV_DSHOW) += tvi_dshow.c SRCS_COMMON-$(TV_TELETEXT) += tvi_vbi.c SRCS_COMMON-$(TV_V4L1) += tvi_v4l.c audio_in.c SRCS_COMMON-$(TV_V4L2) += tvi_v4l2.c audio_in.c diff -r acb856c03bd8 -r d81eef9beb1b stream/stream_tv.c --- a/stream/stream_tv.c Sat Oct 13 14:16:37 2007 +0000 +++ b/stream/stream_tv.c Sat Oct 13 17:14:39 2007 +0000 @@ -36,7 +36,7 @@ "europe-east", //chanlist "pal", //norm 0, //automute -#ifdef HAVE_TV_V4L2 +#if defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) -1, //normid #endif NULL, //device @@ -51,15 +51,18 @@ 0, //immediate; 44100, //audiorate; 0, //audio_id -#if defined(HAVE_TV_V4L) +#if defined(HAVE_TV_V4L) || defined(HAVE_TV_DSHOW) -1, //amode -1, //volume +#if defined(HAVE_TV_V4L) -1, //bass -1, //treble -1, //balance -1, //forcechan 0, //force_audio +#endif -1, //buffer_size +#if defined(HAVE_TV_V4L) 0, //mjpeg 2, //decimation 90, //quality @@ -68,6 +71,7 @@ #endif #endif NULL, //adevice +#endif 0, //brightness 0, //contrast 0, //hue @@ -80,7 +84,13 @@ 0, //scan_autostart 50, //scan_threshold - 0.5 //scan_period + 0.5, //scan_period +#ifdef HAVE_TV_DSHOW + 0, //hidden_video_renderer; + 0, //hidden_vp_renderer; + 0, //system_clock; + 0 //normalize_audio_chunks; +#endif }; #define ST_OFF(f) M_ST_OFF(tv_param_t,f) diff -r acb856c03bd8 -r d81eef9beb1b stream/tv.c --- a/stream/tv.c Sat Oct 13 14:16:37 2007 +0000 +++ b/stream/tv.c Sat Oct 13 17:14:39 2007 +0000 @@ -42,6 +42,9 @@ /* enumerating drivers (like in stream.c) */ extern tvi_info_t tvi_info_dummy; +#ifdef HAVE_TV_DSHOW +extern tvi_info_dshow; +#endif #ifdef HAVE_TV_V4L1 extern tvi_info_t tvi_info_v4l; #endif @@ -63,6 +66,9 @@ #ifdef HAVE_TV_BSDBT848 &tvi_info_bsdbt848, #endif +#ifdef HAVE_TV_DSHOW + &tvi_info_dshow, +#endif &tvi_info_dummy, NULL }; @@ -200,9 +206,14 @@ static int norm_from_string(tvi_handle_t *tvh, char* norm) { + if (1 #ifdef HAVE_TV_V4L2 - if (strcmp(tvh->tv_param->driver, "v4l2") != 0) { + && strcmp(tvh->tv_param->driver, "v4l2") != 0 && #endif +#ifdef HAVE_TV_DSHOW + && strcmp(tvh->tv_param->driver, "dshow") != 0 +#endif + ) { if (!strcasecmp(norm, "pal")) return TV_NORM_PAL; else if (!strcasecmp(norm, "ntsc")) @@ -221,7 +232,7 @@ mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TV_BogusNormParameter, norm, "PAL"); return TV_NORM_PAL; } -#ifdef HAVE_TV_V4L2 +#if defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) } else { tvi_functions_t *funcs = tvh->functions; char str[8]; @@ -361,8 +372,15 @@ /* set some params got from cmdline */ funcs->control(tvh->priv, TVI_CONTROL_SPC_SET_INPUT, &tvh->tv_param->input); +#if defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) + if (0 #ifdef HAVE_TV_V4L2 - if (!strcmp(tvh->tv_param->driver, "v4l2") && tvh->tv_param->normid >= 0) { + || (!strcmp(tvh->tv_param->driver, "v4l2") && tvh->tv_param->normid >= 0) +#endif +#ifdef HAVE_TV_DSHOW + || (!strcmp(tvh->tv_param->driver, "dshow") && tvh->tv_param->normid >= 0) +#endif + ) { mp_msg(MSGT_TV, MSGL_V, MSGTR_TV_SelectedNormId, tvh->tv_param->normid); if (funcs->control(tvh->priv, TVI_CONTROL_TUN_SET_NORM, &tvh->tv_param->normid) != TVI_CONTROL_TRUE) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TV_CannotSetNorm); @@ -376,7 +394,7 @@ if (funcs->control(tvh->priv, TVI_CONTROL_TUN_SET_NORM, &tvh->norm) != TVI_CONTROL_TRUE) { mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TV_CannotSetNorm); } -#ifdef HAVE_TV_V4L2 +#if defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) } #endif diff -r acb856c03bd8 -r d81eef9beb1b stream/tv.h --- a/stream/tv.h Sat Oct 13 14:16:37 2007 +0000 +++ b/stream/tv.h Sat Oct 13 17:14:39 2007 +0000 @@ -11,7 +11,7 @@ char *chanlist; char *norm; int automute; -#ifdef HAVE_TV_V4L2 +#if defined(HAVE_TV_V4L2) || defined(HAVE_TV_DSHOW) int normid; #endif char *device; @@ -26,15 +26,18 @@ int immediate; int audiorate; int audio_id; -#if defined(HAVE_TV_V4L) +#if defined(HAVE_TV_V4L) || defined(HAVE_TV_DSHOW) int amode; int volume; +#if defined(HAVE_TV_V4L) int bass; int treble; int balance; int forcechan; int force_audio; +#endif int buffer_size; +#if defined(HAVE_TV_V4L) int mjpeg; int decimation; int quality; @@ -43,6 +46,7 @@ #endif #endif char* adevice; +#endif int brightness; int contrast; int hue; @@ -56,6 +60,35 @@ int scan; int scan_threshold; float scan_period; + +#ifdef HAVE_TV_DSHOW + /** + Terminate stream with video renderer instead of Null renderer + Will help if video freezes but audio does not. + May not work with -vo directx and -vf crop combination. + */ + int hidden_video_renderer; + /** + For VIVO cards VP pin have to be rendered too. + This tweak will cause VidePort pin stream to be terminated with video renderer + instead of removing it from graph. + Use if your card have vp pin and video is still choppy. + May not work with -vo directx and -vf crop combination. + */ + int hidden_vp_renderer; + /** + Use system clock as sync source instead of default graph clock (usually the clock + from one of live sources in graph. + */ + int system_clock; + /** + Some audio cards creates audio chunks with about 0.5 sec size. + This can cause choppy video when using mplayer with immediatemode=0 + Use followingtweak to decrease audio chunk sizes. + It will create audio chunks with time length equal to one video frame time. + */ + int normalize_audio_chunks; +#endif } tv_param_t; extern tv_param_t stream_tv_defaults; diff -r acb856c03bd8 -r d81eef9beb1b stream/tvi_dshow.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stream/tvi_dshow.c Sat Oct 13 17:14:39 2007 +0000 @@ -0,0 +1,3298 @@ +/* + * TV support under Win32 + * + * (C) 2007 Vladimir Voroshilov . + * + * Based on tvi_dummy.c with help of tv.c, tvi_v4l2.c code . + * + * ------------------------------------------------------------------------------- + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * ------------------------------------------------------------------------------- + * + * + * WARNING: This is alpha code! + * + * Abilities: + * * Watching TV under Win32 using WDM Capture driver and DirectShow + * * Grabbing synchronized audio/video with mencoder (synchronization is beeing done by DirectShow) + * * If device driver provides IAMStreamConfig interface, user can choose width/height with "-tv height=:width=" + * * Adjusting BRIGHTNESS,HUE,SATURATION,CONTRAST if supported by device + * * Selecting Tuner,S-Video,... as media source + * * User can select used video capture device, passing -tv device= + * * User can select used audio input, passing -tv audioid= + * + * options which will not be implemented (probably sometime in future, if possible): + * * alsa + * * mjpeg + * * decimation=<1|2|4> + * * quality=<0\-100> + * * forceaudio + * * forcechan=<1\-2> + * * [volume|bass|treble|balance] + * + * Works with: + * - LifeView FlyTV Prime 34FM (SAA7134 based) with driver from Ivan Uskov + * Partially works with: + * - ATI 9200 VIVO based card + * - ATI AIW 7500 + * - nVidia Ti-4400 + * + * Known bugs: + * * stream goes with 24.93 FPS (NTSC), while reporting 25 FPS (PAL) ?! + * * direct set frequency call does not work ('Insufficient Buffer' error) + * * audio stream goes with about 1 sample/sec rate when capturing sound from audio card + * + * TODO: + * * check audio with small buffer on vivo !!! + * * norm for IAMVideoDecoder and for IAMTVtuner - differs !! + * * check how to change TVFormat on VIVO card without tuner + * * Flip image upside-down for RGB formats. + * * + * * remove debug sleep() + * * Add some notes to methods' parameters + * * refactor console messages + * * check using header files and keep only needed + * * add additional comments to methods' bodies + * + */ + + +/// \ingroup tvi_dshow + +#include "config.h" + +#include +#include "libmpcodecs/img_format.h" +#include "libaf/af_format.h" +#include "help_mp.h" +#include "osdep/timer.h" + + +#include "tv.h" +#include "mp_msg.h" +#include "frequencies.h" + + +#include "tvi_dshow.h" + +static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param); + +/* +*--------------------------------------------------------------------------------------- +* +* Data structures +* +*--------------------------------------------------------------------------------------- +*/ +/** + information about this file +*/ +tvi_info_t tvi_info_dshow = { + tvi_init_dshow, + "DirectShow TV", + "dshow", + "Vladimir Voroshilov", + "Very experimental!! Use with caution" +}; + + +/** +ringbuffer related info +*/ +typedef struct { + CRITICAL_SECTION *pMutex; ///< pointer to critical section (mutex) + char **ringbuffer; ///< ringbuffer array + double*dpts; ///< samples' timestamps + + int buffersize; ///< size of buffer in blocks + int blocksize; ///< size of individual block + int head; ///< index of first valid sample + int tail; ///< index of last valid sample + int count; ///< count of valid samples in ringbuffer + double tStart; ///< pts of first sample (first sample should have pts 0) +} grabber_ringbuffer_t; + +/** + CSampleGrabberCD definition +*/ +typedef struct CSampleGrabberCB { + ISampleGrabberCBVtbl *lpVtbl; + int refcount; + GUID interfaces[2]; + grabber_ringbuffer_t *pbuf; +} CSampleGrabberCB; + +typedef struct { + int dev_index; ///< capture device index in device list (defaul: 0, first available device) + int adev_index; ///< audio capture device index in device list (default: -1, not used) + int immediate_mode; ///< immediate mode (no sound capture) + int state; ///< state: 1-filter graph running, 0-filter graph stopped + int direct_setfreq_call; ///< 0-find nearest channels from system channel list(workaround),1-direct call to set frequency + int direct_getfreq_call; ///< 0-find frequncy from frequency table (workaround),1-direct call to get frequency + + int fcc; ///< used video format code (FourCC) + int width; ///< picture width (default: auto) + int height; ///< picture height (default: auto) + + int channels; ///< number of audio channels (default: auto) + int samplerate; ///< audio samplerate (default: auto) + + long *freq_table; ///< frequency table (in Hz) + int freq_table_len; ///< length of freq table + int first_channel; ///< channel number of first entry in freq table + int input; ///< used input + + // video related stuff + int nVideoFormatUsed; ///< index of used video format + AM_MEDIA_TYPE **arpmtVideo; ///< available video formats + VIDEO_STREAM_CONFIG_CAPS **arVideoCaps; ///< capabilities of output video + AM_MEDIA_TYPE *pmtVideo; ///< video stream properties + grabber_ringbuffer_t *v_buf; + IBaseFilter *pVideoFilter; ///< interface for capture device + IAMStreamConfig *pVideoStreamConfig; ///< for configuring video stream + + // audio related stuff + int nAudioFormatUsed; ///< index of used audio format + AM_MEDIA_TYPE **arpmtAudio; ///< available audio formats + AUDIO_STREAM_CONFIG_CAPS **arAudioCaps; ///< capabilities of output audio + AM_MEDIA_TYPE *pmtAudio; ///< audio stream properties. + grabber_ringbuffer_t *a_buf; + IBaseFilter *pAudioFilter; ///< interface for audio capture device (if adevice passed) + IAMStreamConfig *pAudioStreamConfig; ///< for configuring audio stream + + AM_MEDIA_TYPE *pmtVBI; ///< available audio formats + grabber_ringbuffer_t *vbi_buf; + + IAMTVTuner *pTVTuner; ///< interface for tuner device + IGraphBuilder *pGraph; ///< filter graph + ICaptureGraphBuilder2 *pBuilder; ///< graph builder + IMediaControl *pMediaControl; ///< interface for controlling graph (start, stop,...) + IAMVideoProcAmp *pVideoProcAmp; ///< for adjusting hue,saturation,etc + IAMCrossbar *pCrossbar; ///< for selecting input (Tuner,Composite,S-Video,...) + DWORD dwRegister; ///< allow graphedit to connect to our graph + CSampleGrabberCB* pCSGCB; ///< callback object + void *priv_vbi; ///< private VBI data structure + tt_stream_props tsp; ///< data for VBI initialization + + tv_param_t* tv_param; ///< TV parameters +} priv_t; + +#include "tvi_def.h" + +/** + country table entry structure (for loading freq table stored in kstvtuner.ax + + \note + structure have to be 2-byte aligned and have 10-byte length!! +*/ +typedef struct __attribute__((__packed__)) { + WORD CountryCode; ///< Country code + WORD CableFreqTable; ///< index of resource with frequencies for cable channels + WORD BroadcastFreqTable; ///< index of resource with frequencies for broadcast channels + DWORD VideoStandard; ///< used video standard +} TRCCountryList; +/** + information about image formats +*/ +typedef struct { + uint32_t fmt; ///< FourCC + const GUID *subtype; ///< DirectShow's subtype + int nBits; ///< number of bits + int nCompression; ///< complression + int tail; ///< number of additional bytes followed VIDEOINFOHEADER structure +} img_fmt; + +/* +*--------------------------------------------------------------------------------------- +* +* Methods forward declaration +* +*--------------------------------------------------------------------------------------- +*/ +static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize, + int blocksize); +static HRESULT show_filter_info(IBaseFilter * pFilter); +#if 0 +//defined in current MinGW release +HRESULT STDCALL GetRunningObjectTable(DWORD, IRunningObjectTable **); +HRESULT STDCALL CreateItemMoniker(LPCOLESTR, LPCOLESTR, IMoniker **); +#endif +static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t * + pbuf); +static int set_crossbar_input(priv_t * priv, int input); +static int subtype2imgfmt(const GUID * subtype); + +/* +*--------------------------------------------------------------------------------------- +* +* Global constants and variables +* +*--------------------------------------------------------------------------------------- +*/ +/** + lookup tables for physical connector types + */ +static const struct { + long type; + char *name; +} tv_physcon_types[]={ + {PhysConn_Video_Tuner, "Tuner" }, + {PhysConn_Video_Composite, "Composite" }, + {PhysConn_Video_SVideo, "S-Video" }, + {PhysConn_Video_RGB, "RGB" }, + {PhysConn_Video_YRYBY, "YRYBY" }, + {PhysConn_Video_SerialDigital, "SerialDigital" }, + {PhysConn_Video_ParallelDigital, "ParallelDigital"}, + {PhysConn_Video_VideoDecoder, "VideoDecoder" }, + {PhysConn_Video_VideoEncoder, "VideoEncoder" }, + {PhysConn_Video_SCART, "SCART" }, + {PhysConn_Video_Black, "Blaack" }, + {PhysConn_Audio_Tuner, "Tuner" }, + {PhysConn_Audio_Line, "Line" }, + {PhysConn_Audio_Mic, "Mic" }, + {PhysConn_Audio_AESDigital, "AESDiital" }, + {PhysConn_Audio_SPDIFDigital, "SPDIFDigital" }, + {PhysConn_Audio_AudioDecoder, "AudioDecoder" }, + {PhysConn_Audio_SCSI, "SCSI" }, + {PhysConn_Video_SCSI, "SCSI" }, + {PhysConn_Audio_AUX, "AUX" }, + {PhysConn_Video_AUX, "AUX" }, + {PhysConn_Audio_1394, "1394" }, + {PhysConn_Video_1394, "1394" }, + {PhysConn_Audio_USB, "USB" }, + {PhysConn_Video_USB, "USB" }, + {-1, NULL } +}; + +static const struct { + char *chanlist_name; + int country_code; +} tv_chanlist2country[]={ + {"us-bcast", 1}, + {"russia", 7}, + {"argentina", 54}, + {"japan-bcast", 81}, + {"china-bcast", 86}, + {"southafrica", 27}, + {"australia", 61}, + {"ireland", 353}, + {"france", 33}, + {"italy", 39}, + {"newzealand", 64}, + //directshow table uses eastern europe freq table for russia + {"europe-east", 7}, + //directshow table uses western europe freq table for germany + {"europe-west", 49}, + /* cable channels */ + {"us-cable", 1}, + {"us-cable-hrc", 1}, + {"japan-cable", 81}, + //default is USA + {NULL, 1} +}; + +/** + array, contains information about various supported (i hope) image formats +*/ +static const img_fmt img_fmt_list[] = { + {IMGFMT_YUY2, &MEDIASUBTYPE_YUY2, 0, IMGFMT_YUY2, 0}, + {IMGFMT_YV12, &MEDIASUBTYPE_YV12, 0, IMGFMT_YV12, 0}, + {IMGFMT_IYUV, &MEDIASUBTYPE_IYUV, 0, IMGFMT_IYUV, 0}, + {IMGFMT_I420, &MEDIASUBTYPE_I420, 0, IMGFMT_I420, 0}, + {IMGFMT_UYVY, &MEDIASUBTYPE_UYVY, 0, IMGFMT_UYVY, 0}, + {IMGFMT_YVYU, &MEDIASUBTYPE_YVYU, 0, IMGFMT_YVYU, 0}, + {IMGFMT_YVU9, &MEDIASUBTYPE_YVU9, 0, IMGFMT_YVU9, 0}, + {IMGFMT_BGR32, &MEDIASUBTYPE_RGB32, 32, 0, 0}, + {IMGFMT_BGR24, &MEDIASUBTYPE_RGB24, 24, 0, 0}, + {IMGFMT_BGR16, &MEDIASUBTYPE_RGB565, 16, 3, 12}, + {IMGFMT_BGR15, &MEDIASUBTYPE_RGB555, 16, 3, 12}, + {0, &GUID_NULL, 0, 0, 0} +}; + +#define TV_NORMS_COUNT 19 +static const struct { + long index; + char *name; +} tv_norms[TV_NORMS_COUNT] = { + { + AnalogVideo_NTSC_M, "ntsc-m"}, { + AnalogVideo_NTSC_M_J, "ntsc-mj"}, { + AnalogVideo_NTSC_433, "ntsc-433"}, { + AnalogVideo_PAL_B, "pal-b"}, { + AnalogVideo_PAL_D, "pal-d"}, { + AnalogVideo_PAL_G, "pal-g"}, { + AnalogVideo_PAL_H, "pal-h"}, { + AnalogVideo_PAL_I, "pal-i"}, { + AnalogVideo_PAL_M, "pal-m"}, { + AnalogVideo_PAL_N, "pal-n"}, { + AnalogVideo_PAL_60, "pal-60"}, { + AnalogVideo_SECAM_B, "secam-b"}, { + AnalogVideo_SECAM_D, "secam-d"}, { + AnalogVideo_SECAM_G, "secam-g"}, { + AnalogVideo_SECAM_H, "secam-h"}, { + AnalogVideo_SECAM_K, "secam-k"}, { + AnalogVideo_SECAM_K1, "secam-k1"}, { + AnalogVideo_SECAM_L, "secam-l"}, { + AnalogVideo_SECAM_L1, "secam-l1"} +}; +static long tv_available_norms[TV_NORMS_COUNT]; +static int tv_available_norms_count = 0; + + +static long *tv_available_inputs; +static int tv_available_inputs_count = 0; + +/* +*--------------------------------------------------------------------------------------- +* +* Various GUID definitions +* +*--------------------------------------------------------------------------------------- +*/ +/// CLSID definitions (used for CoCreateInstance call) +DEFINE_GUID(CLSID_SampleGrabber, 0xC1F400A0, 0x3F08, 0x11d3, 0x9F, 0x0B, + 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37); +DEFINE_GUID(CLSID_NullRenderer, 0xC1F400A4, 0x3F08, 0x11d3, 0x9F, 0x0B, + 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37); +DEFINE_GUID(CLSID_SystemDeviceEnum, 0x62BE5D10, 0x60EB, 0x11d0, 0xBD, 0x3B, + 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86); +DEFINE_GUID(CLSID_CaptureGraphBuilder2, 0xBF87B6E1, 0x8C27, 0x11d0, 0xB3, + 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); +DEFINE_GUID(CLSID_VideoInputDeviceCategory, 0x860BB310, 0x5D01, 0x11d0, + 0xBD, 0x3B, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86); +DEFINE_GUID(CLSID_AudioInputDeviceCategory, 0x33d9a762, 0x90c8, 0x11d0, + 0xbd, 0x43, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86); +DEFINE_GUID(CLSID_FilterGraph, 0xe436ebb3, 0x524f, 0x11ce, 0x9f, 0x53, + 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(CLSID_SystemClock, 0xe436ebb1, 0x524f, 0x11ce, 0x9f, 0x53, + 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +#ifdef NOT_USED +DEFINE_GUID(CLSID_CaptureGraphBuilder, 0xBF87B6E0, 0x8C27, 0x11d0, 0xB3, + 0xF0, 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); +DEFINE_GUID(CLSID_VideoPortManager, 0x6f26a6cd, 0x967b, 0x47fd, 0x87, 0x4a, + 0x7a, 0xed, 0x2c, 0x9d, 0x25, 0xa2); +DEFINE_GUID(IID_IPin, 0x56a86891, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_ICaptureGraphBuilder, 0xbf87b6e0, 0x8c27, 0x11d0, 0xb3, + 0xf0, 0x00, 0xaa, 0x00, 0x37, 0x61, 0xc5); +DEFINE_GUID(IID_IFilterGraph, 0x56a8689f, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, + 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f, + 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); +#endif + +/// IID definitions (used for QueryInterface call) +DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a, + 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IAMBufferNegotiation, 0x56ED71A0, 0xAF5F, 0x11D0, 0xB3, 0xF0, + 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); +DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, + 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); +DEFINE_GUID(IID_ISampleGrabber, 0x6B652FFF, 0x11FE, 0x4fce, 0x92, 0xAD, + 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F); +DEFINE_GUID(IID_ISampleGrabberCB, 0x0579154A, 0x2B53, 0x4994, 0xB0, 0xD0, + 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85); +DEFINE_GUID(IID_ICaptureGraphBuilder2, 0x93e5a4e0, 0x2d50, 0x11d2, 0xab, + 0xfa, 0x00, 0xa0, 0xc9, 0xc6, 0xe3, 0x8d); +DEFINE_GUID(IID_ICreateDevEnum, 0x29840822, 0x5b84, 0x11d0, 0xbd, 0x3b, + 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86); +DEFINE_GUID(IID_IGraphBuilder, 0x56a868a9, 0x0ad4, 0x11ce, 0xb0, 0x3a, + 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IAMVideoProcAmp, 0xC6E13360, 0x30AC, 0x11d0, 0xA1, 0x8C, + 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56); +DEFINE_GUID(IID_IVideoWindow, 0x56a868b4, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, + 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IMediaControl, 0x56a868b1, 0x0ad4, 0x11ce, 0xb0, 0x3a, + 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70); +DEFINE_GUID(IID_IAMTVTuner, 0x211A8766, 0x03AC, 0x11d1, 0x8D, 0x13, 0x00, + 0xAA, 0x00, 0xBD, 0x83, 0x39); +DEFINE_GUID(IID_IAMCrossbar, 0xc6e13380, 0x30ac, 0x11d0, 0xa1, 0x8c, 0x00, + 0xa0, 0xc9, 0x11, 0x89, 0x56); +DEFINE_GUID(IID_IAMStreamConfig, 0xc6e13340, 0x30ac, 0x11d0, 0xa1, 0x8c, + 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56); +DEFINE_GUID(IID_IAMAudioInputMixer, 0x54C39221, 0x8380, 0x11d0, 0xB3, 0xF0, + 0x00, 0xAA, 0x00, 0x37, 0x61, 0xC5); +DEFINE_GUID(IID_IAMTVAudio, 0x83EC1C30, 0x23D1, 0x11d1, 0x99, 0xE6, 0x00, + 0xA0, 0xC9, 0x56, 0x02, 0x66); +DEFINE_GUID(IID_IAMAnalogVideoDecoder, 0xC6E13350, 0x30AC, 0x11d0, 0xA1, + 0x8C, 0x00, 0xA0, 0xC9, 0x11, 0x89, 0x56); +DEFINE_GUID(IID_IPropertyBag, 0x55272a00, 0x42cb, 0x11ce, 0x81, 0x35, 0x00, + 0xaa, 0x00, 0x4b, 0xb8, 0x51); +DEFINE_GUID(PIN_CATEGORY_CAPTURE, 0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f, + 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); +DEFINE_GUID(PIN_CATEGORY_VIDEOPORT, 0xfb6c4285, 0x0353, 0x11d1, 0x90, 0x5f, + 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); +DEFINE_GUID(PIN_CATEGORY_PREVIEW, 0xfb6c4282, 0x0353, 0x11d1, 0x90, 0x5f, + 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); +DEFINE_GUID(PIN_CATEGORY_VBI, 0xfb6c4284, 0x0353, 0x11d1, 0x90, 0x5f, + 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); +DEFINE_GUID(PROPSETID_TUNER, 0x6a2e0605, 0x28e4, 0x11d0, 0xa1, 0x8c, 0x00, + 0xa0, 0xc9, 0x11, 0x89, 0x56); +DEFINE_GUID(MEDIATYPE_VBI, 0xf72a76e1, 0xeb0a, 0x11d0, 0xac, 0xe4, 0x00, + 0x00, 0xc0, 0xcc, 0x16, 0xba); + +#define INSTANCEDATA_OF_PROPERTY_PTR(x) (((KSPROPERTY*)(x)) + 1) +#define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY)) + +#define DEVICE_NAME_MAX_LEN 2000 + +/*--------------------------------------------------------------------------------------- +* Methods, called only from this file +*---------------------------------------------------------------------------------------*/ + +void set_buffer_preference(int nDiv,WAVEFORMATEX* pWF,IPin* pOutPin,IPin* pInPin){ + ALLOCATOR_PROPERTIES prop; + IAMBufferNegotiation* pBN; + HRESULT hr; + + prop.cbAlign = -1; + prop.cbBuffer = pWF->nAvgBytesPerSec/nDiv; + if (!prop.cbBuffer) + prop.cbBuffer = 1; + prop.cbBuffer += pWF->nBlockAlign - 1; + prop.cbBuffer -= prop.cbBuffer % pWF->nBlockAlign; + prop.cbPrefix = -1; + prop.cBuffers = -1; + + hr=OLE_QUERYINTERFACE(pOutPin,IID_IAMBufferNegotiation,pBN); + if(FAILED(hr)) + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pOutPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x\n",(unsigned int)hr); + else{ + hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop); + if(FAILED(hr)) + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow:pOutPin->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr); + OLE_RELEASE_SAFE(pBN); + } + hr=OLE_QUERYINTERFACE(pInPin,IID_IAMBufferNegotiation,pBN); + if(FAILED(hr)) + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPin->QueryInterface(IID_IAMBufferNegotiation) Error: 0x%x",(unsigned int)hr); + else{ + hr=OLE_CALL_ARGS(pBN,SuggestAllocatorProperties,&prop); + if(FAILED(hr)) + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: pInPit->SuggestAllocatorProperties Error:0x%x\n",(unsigned int)hr); + OLE_RELEASE_SAFE(pBN); + } +} +/* +*--------------------------------------------------------------------------------------- +* +* CSampleGrabberCD class. Used for receiving samples from DirectShow. +* +*--------------------------------------------------------------------------------------- +*/ +/// CSampleGrabberCD destructor +static void CSampleGrabberCB_Destroy(CSampleGrabberCB * This) +{ + free(This->lpVtbl); + free(This); +} + +/// CSampleGrabberCD IUnknown interface methods implementation +static long STDCALL CSampleGrabberCB_QueryInterface(ISampleGrabberCB * + This, + const GUID * riid, + void **ppvObject) +{ + CSampleGrabberCB *me = (CSampleGrabberCB *) This; + GUID *r; + unsigned int i = 0; + Debug printf("CSampleGrabberCB_QueryInterface(%p) called\n", This); + if (!ppvObject) + return E_POINTER; + for (r = me->interfaces; + i < sizeof(me->interfaces) / sizeof(me->interfaces[0]); r++, i++) + if (!memcmp(r, riid, sizeof(*r))) { + OLE_CALL(This, AddRef); + *ppvObject = This; + return 0; + } + Debug printf("Query failed! (GUID: 0x%x)\n", *(unsigned int *) riid); + return E_NOINTERFACE; +} + +static long STDCALL CSampleGrabberCB_AddRef(ISampleGrabberCB * This) +{ + CSampleGrabberCB *me = (CSampleGrabberCB *) This; + Debug printf("CSampleGrabberCB_AddRef(%p) called (ref:%d)\n", This, + me->refcount); + return ++(me->refcount); +} + +static long STDCALL CSampleGrabberCB_Release(ISampleGrabberCB * This) +{ + CSampleGrabberCB *me = (CSampleGrabberCB *) This; + Debug printf("CSampleGrabberCB_Release(%p) called (new ref:%d)\n", + This, me->refcount - 1); + if (--(me->refcount) == 0) + CSampleGrabberCB_Destroy(me); + return 0; +} + + +HRESULT STDCALL CSampleGrabberCB_BufferCB(ISampleGrabberCB * This, + double SampleTime, + BYTE * pBuffer, long lBufferLen) +{ + CSampleGrabberCB *this = (CSampleGrabberCB *) This; + grabber_ringbuffer_t *rb = this->pbuf; + + if (!lBufferLen) + return E_FAIL; + + if (!rb->ringbuffer) { + rb->buffersize /= lBufferLen; + if (init_ringbuffer(rb, rb->buffersize, lBufferLen) != S_OK) + return E_FAIL; + } + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: BufferCB(%p): len=%ld ts=%f\n", This, lBufferLen, SampleTime); + EnterCriticalSection(rb->pMutex); + if (rb->count >= rb->buffersize) { + rb->head = (rb->head + 1) % rb->buffersize; + rb->count--; + } + + memcpy(rb->ringbuffer[rb->tail], pBuffer, + lBufferLen < rb->blocksize ? lBufferLen : rb->blocksize); + rb->dpts[rb->tail] = SampleTime; + rb->tail = (rb->tail + 1) % rb->buffersize; + rb->count++; + LeaveCriticalSection(rb->pMutex); + + return S_OK; +} + +/// wrapper. directshow does the same when BufferCB callback is requested +HRESULT STDCALL CSampleGrabberCB_SampleCB(ISampleGrabberCB * This, + double SampleTime, + LPMEDIASAMPLE pSample) +{ + char* buf; + long len; + long long tStart,tEnd; + HRESULT hr; + grabber_ringbuffer_t *rb = ((CSampleGrabberCB*)This)->pbuf; + + len=OLE_CALL(pSample,GetSize); + tStart=tEnd=0; + hr=OLE_CALL_ARGS(pSample,GetTime,&tStart,&tEnd); + if(FAILED(hr)){ + return hr; + } + mp_msg(MSGT_TV, MSGL_DBG4,"tvi_dshow: SampleCB(%p): %d/%d %f\n", This,rb->count,rb->buffersize,1e-7*tStart); + hr=OLE_CALL_ARGS(pSample,GetPointer,(void*)&buf); + if(FAILED(hr)){ + return hr; + } + hr=CSampleGrabberCB_BufferCB(This,1e-7*tStart,buf,len); + return hr; + +} + +/// main grabbing routine +static CSampleGrabberCB *CSampleGrabberCB_Create(grabber_ringbuffer_t * + pbuf) +{ + CSampleGrabberCB *This = malloc(sizeof(CSampleGrabberCB)); + if (!This) + return NULL; + + This->lpVtbl = malloc(sizeof(ISampleGrabberVtbl)); + if (!This->lpVtbl) { + CSampleGrabberCB_Destroy(This); + return NULL; + } + This->refcount = 1; + This->lpVtbl->QueryInterface = CSampleGrabberCB_QueryInterface; + This->lpVtbl->AddRef = CSampleGrabberCB_AddRef; + This->lpVtbl->Release = CSampleGrabberCB_Release; + This->lpVtbl->SampleCB = CSampleGrabberCB_SampleCB; + This->lpVtbl->BufferCB = CSampleGrabberCB_BufferCB; + + This->interfaces[0] = IID_IUnknown; + This->interfaces[1] = IID_ISampleGrabberCB; + + This->pbuf = pbuf; + + return This; +} + +/* +*--------------------------------------------------------------------------------------- +* +* ROT related methods (register, unregister) +* +*--------------------------------------------------------------------------------------- +*/ +/** +Registering graph in ROT. User will be able to connect to graph from GraphEdit. +*/ +static HRESULT AddToRot(IUnknown * pUnkGraph, DWORD * pdwRegister) +{ + IMoniker *pMoniker; + IRunningObjectTable *pROT; + WCHAR wsz[256]; + HRESULT hr; + + if (FAILED(GetRunningObjectTable(0, &pROT))) { + return E_FAIL; + } + wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR) pUnkGraph, + GetCurrentProcessId()); + hr = CreateItemMoniker(L"!", wsz, &pMoniker); + if (SUCCEEDED(hr)) { + hr = OLE_CALL_ARGS(pROT, Register, ROTFLAGS_REGISTRATIONKEEPSALIVE, + pUnkGraph, pMoniker, pdwRegister); + OLE_RELEASE_SAFE(pMoniker); + } + OLE_RELEASE_SAFE(pROT); + return hr; +} + +/// Unregistering graph in ROT +static void RemoveFromRot(DWORD dwRegister) +{ + IRunningObjectTable *pROT; + if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) { + OLE_CALL_ARGS(pROT, Revoke, dwRegister); + OLE_RELEASE_SAFE(pROT); + } +} + +/* +*--------------------------------------------------------------------------------------- +* +* ringbuffer related methods (init, destroy) +* +*--------------------------------------------------------------------------------------- +*/ +/** + * \brief ringbuffer destroying routine + * + * \param rb pointer to empty (just allocated) ringbuffer structure + * + * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure + */ +static void destroy_ringbuffer(grabber_ringbuffer_t * rb) +{ + int i; + + if (!rb) + return; + + if (rb->ringbuffer) { + for (i = 0; i < rb->buffersize; i++) + if (rb->ringbuffer[i]) + free(rb->ringbuffer[i]); + free(rb->ringbuffer); + rb->ringbuffer = NULL; + } + if (rb->dpts) { + free(rb->dpts); + rb->dpts = NULL; + } + if (rb->pMutex) { + DeleteCriticalSection(rb->pMutex); + free(rb->pMutex); + rb->pMutex = NULL; + } + + rb->blocksize = 0; + rb->buffersize = 0; + rb->head = 0; + rb->tail = 0; + rb->count = 0; +} + +/** + * \brief ringbuffer initialization + * + * \param rb pointer to empty (just allocated) ringbuffer structure + * \param buffersize size of buffer in blocks + * \param blocksize size of buffer's block + * + * \return S_OK if success + * \return E_OUTOFMEMORY not enough memory + * + * \note routine does not allocates memory for grabber_rinbuffer_s structure + */ +static HRESULT init_ringbuffer(grabber_ringbuffer_t * rb, int buffersize, + int blocksize) +{ + int i; + + if (!rb) + return E_OUTOFMEMORY; + + rb->buffersize = buffersize < 2 ? 2 : buffersize; + rb->blocksize = blocksize; + + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Capture buffer: %d blocks of %d bytes.\n", + rb->buffersize, rb->blocksize); + + rb->ringbuffer = (char **) malloc(rb->buffersize * sizeof(char *)); + if (!rb) + return E_POINTER; + memset(rb->ringbuffer, 0, rb->buffersize * sizeof(char *)); + + for (i = 0; i < rb->buffersize; i++) { + rb->ringbuffer[i] = (char *) malloc(rb->blocksize * sizeof(char)); + if (!rb->ringbuffer[i]) { + destroy_ringbuffer(rb); + return E_OUTOFMEMORY; + } + } + rb->dpts = (double*) malloc(rb->buffersize * sizeof(double)); + if (!rb->dpts) { + destroy_ringbuffer(rb); + return E_OUTOFMEMORY; + } + rb->head = 0; + rb->tail = 0; + rb->count = 0; + rb->tStart = -1; + rb->pMutex = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION)); + if (!rb->pMutex) { + destroy_ringbuffer(rb); + return E_OUTOFMEMORY; + } + InitializeCriticalSection(rb->pMutex); + return S_OK; +} + +/* +*--------------------------------------------------------------------------------------- +* +* Tuner related methods (frequency, capabilities, etc +* +*--------------------------------------------------------------------------------------- +*/ +/** + * \brief returns string with name for givend PsysCon_* constant + * + * \param lPhysicalType constant from PhysicalConnectorType enumeration + * + * \return pointer to string with apropriate name + * + * \note + * Caller should not free returned pointer + */ +static char *physcon2str(const long lPhysicalType) +{ + int i; + for(i=0; tv_physcon_types[i].name; i++) + if(tv_physcon_types[i].type==lPhysicalType) + return tv_physcon_types[i].name; + return "Unknown"; +}; + +/** + * \brief converts MPlayer's chanlist to system country code. + * + * \param chanlist MPlayer's chanlist name + * + * \return system country code + * + * \remarks + * After call to IAMTVTuner::put_CountryCode with returned value tuner switches to frequency table used in specified + * country (which is usually larger then MPlayer's one, so workaround will work fine). + * + * \todo + * Resolve trouble with cable channels (DirectShow's tuners must be switched between broadcast and cable channels modes. + */ +static int chanlist2country(char *chanlist) +{ + int i; + for(i=0; tv_chanlist2country[i].chanlist_name; i++) + if (!strcmp(chanlist, tv_chanlist2country[i].chanlist_name)) + break; + return tv_chanlist2country[i].country_code; +} + +/** + * \brief loads specified resource from module and return pointer to it + * + * \param hDLL valid module desriptor + * \param index index of resource. resource with name "#" will be loaded + * + * \return pointer to loader resource or NULL if error occured + */ +static void *GetRC(HMODULE hDLL, int index) +{ + char szRCDATA[10]; + char szName[10]; + HRSRC hRes; + HGLOBAL hTable; + + snprintf(szRCDATA, 10, "#%d", (int)RT_RCDATA); + snprintf(szName, 10, "#%d", index); + + hRes = FindResource(hDLL, szName, szRCDATA); + if (!hRes) { + return NULL; + } + hTable = LoadResource(hDLL, hRes); + if (!hTable) { + return NULL; + } + return LockResource(hTable); +} + +/** + * \brief loads frequency table for given country from kstvtune.ax + * + * \param[in] nCountry - country code + * \param[in] nInputType (TunerInputCable or TunerInputAntenna) + * \param[out] pplFreqTable - address of variable that receives pointer to array, containing frequencies + * \param[out] pnLen length of array + * \param[out] pnFirst - channel number of first entry in array (nChannelMax) + * + * \return S_OK if success + * \return E_POINTER pplFreqTable==NULL || plFirst==NULL || pnLen==NULL + * \return E_FAIL error occured during load + * + * \remarks + * - array must be freed by caller + * - MSDN says that it is not neccessery to unlock or free resource. It will be done after unloading DLL + */ +static HRESULT load_freq_table(int nCountry, int nInputType, + long **pplFreqTable, int *pnLen, + int *pnFirst) +{ + HMODULE hDLL; + long *plFreqTable; + TRCCountryList *pCountryList; + int i, index; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: load_freq_table called %d (%d)\n",nCountry,nInputType); + /* ASSERT(sizeof(TRCCountryList)==10); // need properly aligned structure */ + + if (!pplFreqTable || !pnFirst || !pnLen) + return E_POINTER; + if (!nCountry) + return E_FAIL; + + hDLL = LoadLibrary("kstvtune.ax"); + if (!hDLL) { + return E_FAIL; + } + pCountryList = GetRC(hDLL, 9999); + if (!pCountryList) { + FreeLibrary(hDLL); + return E_FAIL; + } + for (i = 0; pCountryList[i].CountryCode != 0; i++) + if (pCountryList[i].CountryCode == nCountry) + break; + if (pCountryList[i].CountryCode == 0) { + FreeLibrary(hDLL); + return E_FAIL; + } + if (nInputType == TunerInputCable) + index = pCountryList[i].CableFreqTable; + else + index = pCountryList[i].BroadcastFreqTable; + + plFreqTable = GetRC(hDLL, index); //First element is number of first channel, second - number of last channel + if (!plFreqTable) { + FreeLibrary(hDLL); + return E_FAIL; + } + *pnFirst = plFreqTable[0]; + *pnLen = (int) (plFreqTable[1] - plFreqTable[0] + 1); + *pplFreqTable = (long *) malloc((*pnLen) * sizeof(long)); + if (!*pplFreqTable) { + FreeLibrary(hDLL); + return E_FAIL; + } + for (i = 0; i < *pnLen; i++) { + (*pplFreqTable)[i] = plFreqTable[i + 2]; + } + FreeLibrary(hDLL); + return S_OK; +} + +/** + * \brief tunes to given frequency through IKsPropertySet call + * + * \param pTVTuner IAMTVTuner interface of capture device + * \param lFreq frequency to tune (in Hz) + * + * \return S_OK success + * \return apropriate error code otherwise + * + * \note + * Due to either bug in driver or error in following code calll to IKsProperty::Set + * in this methods always fail with error 0x8007007a. + * + * \todo test code on other machines and an error + */ +static HRESULT set_frequency_direct(IAMTVTuner * pTVTuner, long lFreq) +{ + HRESULT hr; + DWORD dwSupported = 0; + DWORD cbBytes = 0; + KSPROPERTY_TUNER_MODE_CAPS_S mode_caps; + KSPROPERTY_TUNER_FREQUENCY_S frequency; + IKsPropertySet *pKSProp; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency_direct called\n"); + + memset(&mode_caps, 0, sizeof(mode_caps)); + memset(&frequency, 0, sizeof(frequency)); + + hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp); + if (FAILED(hr)) + return hr; //no IKsPropertySet interface + + mode_caps.Mode = AMTUNER_MODE_TV; + hr = OLE_CALL_ARGS(pKSProp, QuerySupported, &PROPSETID_TUNER, + KSPROPERTY_TUNER_MODE_CAPS, &dwSupported); + if (FAILED(hr)) { + OLE_RELEASE_SAFE(pKSProp); + return hr; + } + + if (!dwSupported & KSPROPERTY_SUPPORT_GET) { + OLE_RELEASE_SAFE(pKSProp); + return E_FAIL; //PROPSETID_TINER not supported + } + + hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER, + KSPROPERTY_TUNER_MODE_CAPS, + INSTANCEDATA_OF_PROPERTY_PTR(&mode_caps), + INSTANCEDATA_OF_PROPERTY_SIZE(mode_caps), + &mode_caps, sizeof(mode_caps), &cbBytes); + + frequency.Frequency = lFreq; + + if (mode_caps.Strategy == KS_TUNER_STRATEGY_DRIVER_TUNES) + frequency.TuningFlags = KS_TUNER_TUNING_FINE; + else + frequency.TuningFlags = KS_TUNER_TUNING_EXACT; + + if (lFreq < mode_caps.MinFrequency || lFreq > mode_caps.MaxFrequency) { + OLE_RELEASE_SAFE(pKSProp); + return E_FAIL; + } + + hr = OLE_CALL_ARGS(pKSProp, Set, &PROPSETID_TUNER, + KSPROPERTY_TUNER_FREQUENCY, + INSTANCEDATA_OF_PROPERTY_PTR(&frequency), + INSTANCEDATA_OF_PROPERTY_SIZE(frequency), + &frequency, sizeof(frequency)); + if (FAILED(hr)) { + OLE_RELEASE_SAFE(pKSProp); + return hr; + } + + OLE_RELEASE_SAFE(pKSProp); + + return S_OK; +} + +/** + * \brief find channel with nearest frequency and set it + * + * \param priv driver's private data + * \param lFreq frequency in Hz + * + * \return S_OK if success + * \return E_FAIL if error occured + */ +static HRESULT set_nearest_freq(priv_t * priv, long lFreq) +{ + HRESULT hr; + int i; + long lFreqDiff=-1; + int nChannel; + TunerInputType tunerInput; + long lInput; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_nearest_freq called\n"); + if(priv->freq_table_len == -1 && !priv->freq_table) { + + hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput); + if(FAILED(hr)){ //Falling back to 0 + lInput=0; + } + + hr = OLE_CALL_ARGS(priv->pTVTuner, get_InputType, lInput, &tunerInput); + + if (load_freq_table(chanlist2country(priv->tv_param->chanlist), tunerInput, &(priv->freq_table), &(priv->freq_table_len), &(priv->first_channel)) != S_OK) {//FIXME + priv->freq_table_len=0; + priv->freq_table=NULL; + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableExtractFreqTable); + return E_FAIL; + }; + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_FreqTableLoaded, tunerInput == TunerInputAntenna ? "broadcast" : "cable", + chanlist2country(priv->tv_param->chanlist), priv->freq_table_len); + } + + if (priv->freq_table_len <= 0) + return E_FAIL; + + //FIXME: rewrite search algo + nChannel = -1; + for (i = 0; i < priv->freq_table_len; i++) { + if (nChannel == -1 || labs(lFreq - priv->freq_table[i]) < lFreqDiff) { + nChannel = priv->first_channel + i; + lFreqDiff = labs(lFreq - priv->freq_table[i]); + } + } + if (nChannel == -1) { + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableFindNearestChannel); + return E_FAIL; + } + hr = OLE_CALL_ARGS(priv->pTVTuner, put_Channel, nChannel, + AMTUNER_SUBCHAN_DEFAULT, AMTUNER_SUBCHAN_DEFAULT); + if (FAILED(hr)) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableToSetChannel, (unsigned int)hr); + return E_FAIL; + } + return S_OK; +} + +/** + * \brief setting frequency. decides whether use direct call/workaround + * + * \param priv driver's private data + * \param lFreq frequency in Hz + * + * \return TVI_CONTROL_TRUE if success + * \return TVI_CONTROL_FALSE if error occured + * + * \todo check for freq boundary + */ +static int set_frequency(priv_t * priv, long lFreq) +{ + HRESULT hr; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_frequency called\n"); + if (!priv->pTVTuner) + return TVI_CONTROL_FALSE; + if (priv->direct_setfreq_call) { //using direct call to set frequency + hr = set_frequency_direct(priv->pTVTuner, lFreq); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DirectSetFreqFailed); + priv->direct_setfreq_call = 0; + } + } + if (!priv->direct_setfreq_call) { + hr = set_nearest_freq(priv, lFreq); + } + if (FAILED(hr)) + return TVI_CONTROL_FALSE; +#ifdef DEPRECATED + priv->pGrabber->ClearBuffer(priv->pGrabber); +#endif + return TVI_CONTROL_TRUE; +} + +/** + * \brief return current frequency from tuner (in Hz) + * + * \param pTVTuner IAMTVTuner interface of tuner + * \param plFreq address of variable that receives current frequency + * + * \return S_OK success + * \return E_POINTER pTVTuner==NULL || plFreq==NULL + * \return apropriate error code otherwise + */ +static HRESULT get_frequency_direct(IAMTVTuner * pTVTuner, long *plFreq) +{ + HRESULT hr; + KSPROPERTY_TUNER_STATUS_S TunerStatus; + DWORD cbBytes; + IKsPropertySet *pKSProp; + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency_direct called\n"); + + if (!plFreq) + return E_POINTER; + + hr = OLE_QUERYINTERFACE(pTVTuner, IID_IKsPropertySet, pKSProp); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq QueryInterface failed\n"); + return hr; + } + + hr = OLE_CALL_ARGS(pKSProp, Get, &PROPSETID_TUNER, + KSPROPERTY_TUNER_STATUS, + INSTANCEDATA_OF_PROPERTY_PTR(&TunerStatus), + INSTANCEDATA_OF_PROPERTY_SIZE(TunerStatus), + &TunerStatus, sizeof(TunerStatus), &cbBytes); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get freq Get failure\n"); + return hr; + } + *plFreq = TunerStatus.CurrentFrequency; + return S_OK; +} + +/** + * \brief gets current frequency + * + * \param priv driver's private data structure + * \param plFreq - pointer to long int to store frequency to (in Hz) + * + * \return TVI_CONTROL_TRUE if success, TVI_CONTROL_FALSE otherwise + */ +static int get_frequency(priv_t * priv, long *plFreq) +{ + HRESULT hr; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_frequency called\n"); + + if (!plFreq || !priv->pTVTuner) + return TVI_CONTROL_FALSE; + + if (priv->direct_getfreq_call) { //using direct call to get frequency + hr = get_frequency_direct(priv->pTVTuner, plFreq); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_DirectGetFreqFailed); + priv->direct_getfreq_call = 0; + } + } + if (!priv->direct_getfreq_call) { + hr=OLE_CALL_ARGS(priv->pTVTuner, get_VideoFrequency, plFreq); + if (FAILED(hr)) + return TVI_CONTROL_FALSE; + + } + return TVI_CONTROL_TRUE; +} + +/** + * \brief get tuner capabilities + * + * \param priv driver's private data + */ +static void get_capabilities(priv_t * priv) +{ + long lAvailableFormats; + HRESULT hr; + int i; + long lInputPins, lOutputPins, lRelated, lPhysicalType; + IEnumPins *pEnum; + char tmp[200]; + IPin *pPin = 0; + PIN_DIRECTION ThisPinDir; + PIN_INFO pi; + IAMAudioInputMixer *pIAMixer; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_capabilities called\n"); + if (priv->pTVTuner) { + + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_SupportedNorms); + hr = OLE_CALL_ARGS(priv->pTVTuner, get_AvailableTVFormats, + &lAvailableFormats); + if (FAILED(hr)) + tv_available_norms_count = 0; + else { + for (i = 0; i < TV_NORMS_COUNT; i++) { + if (lAvailableFormats & tv_norms[i].index) { + tv_available_norms[tv_available_norms_count] = i; + mp_msg(MSGT_TV, MSGL_V, " %d=%s;", + tv_available_norms_count + 1, tv_norms[i].name); + tv_available_norms_count++; + } + } + } + mp_msg(MSGT_TV, MSGL_INFO, "\n"); + } + if (priv->pCrossbar) { + OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, + &lInputPins); + + tv_available_inputs = (long *) malloc(sizeof(long) * lInputPins); + tv_available_inputs_count = 0; + + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableVideoInputs); + for (i = 0; i < lInputPins; i++) { + OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1, i, + &lRelated, &lPhysicalType); + + if (lPhysicalType < 0x1000) { + tv_available_inputs[tv_available_inputs_count++] = i; + mp_msg(MSGT_TV, MSGL_V, " %d=%s;", + tv_available_inputs_count - 1, + physcon2str(lPhysicalType)); + } + } + mp_msg(MSGT_TV, MSGL_INFO, "\n"); + + set_crossbar_input(priv, 0); + } + + if (priv->adev_index != -1) { + hr = OLE_CALL_ARGS(priv->pAudioFilter, EnumPins, &pEnum); + if (FAILED(hr)) + return; + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_AvailableAudioInputs); + i = 0; + while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) { + memset(&pi, 0, sizeof(pi)); + memset(tmp, 0, 200); + OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir); + if (ThisPinDir == PINDIR_INPUT) { + OLE_CALL_ARGS(pPin, QueryPinInfo, &pi); + wtoa(pi.achName, tmp, 200); + OLE_RELEASE_SAFE(pi.pFilter); + mp_msg(MSGT_TV, MSGL_V, " %d=%s", i, tmp); + mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin); + hr = OLE_QUERYINTERFACE(pPin, IID_IAMAudioInputMixer,pIAMixer); + if (SUCCEEDED(hr)) { + if (i == priv->tv_param->audio_id) { + OLE_CALL_ARGS(pIAMixer, put_Enable, TRUE); + if(priv->tv_param->volume>0) + OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.01 * priv->tv_param->volume); +#if 0 + else + OLE_CALL_ARGS(pIAMixer, put_MixLevel, 1.0); +#endif + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_InputSelected); + } else { + OLE_CALL_ARGS(pIAMixer, put_Enable, FALSE); +#if 0 + OLE_CALL_ARGS(pIAMixer, put_MixLevel, 0.0); +#endif + } + OLE_RELEASE_SAFE(pIAMixer); + } + mp_msg(MSGT_TV, MSGL_V, ";"); + OLE_RELEASE_SAFE(pPin); + i++; + } + } + mp_msg(MSGT_TV, MSGL_INFO, "\n"); + OLE_RELEASE_SAFE(pEnum); + } +} + +/* +*--------------------------------------------------------------------------------------- +* +* Filter related methods +* +*--------------------------------------------------------------------------------------- +*/ +/** + * \brief building in graph audio/video capture chain + * + * \param priv driver's private data + * \param pCaptureFilter pointer to capture device's IBaseFilter interface + * \param pbuf ringbuffer data structure + * \param pmt media type for chain (AM_MEDIA_TYPE) + * + * \note routine does not frees memory, allocated for grabber_rinbuffer_s structure + */ +static HRESULT build_sub_graph(priv_t * priv, IBaseFilter * pCaptureFilter, + grabber_ringbuffer_t * pbuf, + AM_MEDIA_TYPE * pmt, const GUID* ppin_category) +{ + HRESULT hr; + + IPin *pSGIn; + IPin *pSGOut; + IPin *pNRIn=NULL; + IPin *pCapturePin; + + IBaseFilter *pNR = NULL; + IBaseFilter *pSGF = NULL; + + ISampleGrabber *pSG = NULL; + + hr=S_OK; + do{ + hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, + (IUnknown *) pCaptureFilter, + PINDIR_OUTPUT, ppin_category, + &(pmt->majortype), FALSE, 0, &pCapturePin); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: FindPin(pCapturePin) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + /* Addinf SampleGrabber filter for video stream */ + hr = CoCreateInstance((GUID *) & CLSID_SampleGrabber, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pSGF); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pSGF, L"Sample Grabber"); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(SampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pSGF,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pSGIn); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGIn) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pSGF,PINDIR_OUTPUT, NULL, NULL, FALSE, 0, &pSGOut); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pSGOut) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + + /* creating ringbuffer for video samples */ + priv->pCSGCB = CSampleGrabberCB_Create(pbuf); + if(!priv->pCSGCB){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CSampleGrabberCB_Create(pbuf) call failed. Error:0x%x\n", (unsigned int)E_OUTOFMEMORY); + break; + } + + /* initializing SampleGrabber filter */ + hr = OLE_QUERYINTERFACE(pSGF, IID_ISampleGrabber, pSG); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: QueryInterface(IID_ISampleGrabber) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(pSG, SetMediaType, pmt); //set desired mediatype + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetMediaType(pSG) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + // hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) pCSGCB, 1); //we want to receive copy of sample's data + hr = OLE_CALL_ARGS(pSG, SetCallback, (ISampleGrabberCB *) priv->pCSGCB, 0); //we want to receive sample + + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetCallback(pSG) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(pSG, SetOneShot, FALSE); //... for all frames + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetOneShot(pSG) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(pSG, SetBufferSamples, FALSE); //... do not buffer samples in sample grabber + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: SetBufferSamples(pSG) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + OLE_RELEASE_SAFE(pSG); + + if(priv->tv_param->normalize_audio_chunks && !memcmp(&(pmt->majortype),&(MEDIATYPE_Audio),16)){ + set_buffer_preference(20,(WAVEFORMATEX*)(pmt->pbFormat),pCapturePin,pSGIn); + } + + /* connecting filters together: VideoCapture --> SampleGrabber */ + hr = OLE_CALL_ARGS(priv->pGraph, Connect, pCapturePin, pSGIn); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pCapturePin<->pSGIn connection. Error:0x%x\n", (unsigned int)hr); + break; + } + + if(priv->tv_param->hidden_video_renderer){ + IEnumFilters* pEnum; + IBaseFilter* pFilter; + + hr=OLE_CALL_ARGS(priv->pBuilder,RenderStream,NULL,NULL,(IUnknown*)pCapturePin,NULL,NULL); + + OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum); + while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) { + LPVIDEOWINDOW pVideoWindow; + hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow); + if (SUCCEEDED(hr)) + { + OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0); + OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0); + OLE_RELEASE_SAFE(pVideoWindow); + } + OLE_RELEASE_SAFE(pFilter); + } + OLE_RELEASE_SAFE(pEnum); + }else + { + /* adding sink for video stream */ + hr = CoCreateInstance((GUID *) & CLSID_NullRenderer, NULL,CLSCTX_INPROC_SERVER, &IID_IBaseFilter,(void *) &pNR); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: CoCreateInstance(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, pNR, L"Null Renderer"); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: AddFilter(NullRenderer) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, (IUnknown *) pNR,PINDIR_INPUT, NULL, NULL, FALSE, 0, &pNRIn); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: FindPin(pNRIn) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + /* + Prevent ending VBI chain with NullRenderer filter, because this causes VBI pin disconnection + */ + if(memcmp(&(pmt->majortype),&MEDIATYPE_VBI,16)){ + /* connecting filters together: SampleGrabber --> NullRenderer */ + hr = OLE_CALL_ARGS(priv->pGraph, Connect, pSGOut, pNRIn); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Unable to create pSGOut<->pNRIn connection. Error:0x%x\n", (unsigned int)hr); + break; + } + } + } + + hr = S_OK; + } while(0); + OLE_RELEASE_SAFE(pSGF); + OLE_RELEASE_SAFE(pSGIn); + OLE_RELEASE_SAFE(pSGOut); + OLE_RELEASE_SAFE(pNR); + OLE_RELEASE_SAFE(pNRIn); + OLE_RELEASE_SAFE(pCapturePin); + + return hr; +} + +/** + * \brief configures crossbar for grabbing video stream from given input + * + * \param priv driver's private data + * \param input index of available video input to get data from + * + * \return TVI_CONTROL_TRUE success + * \return TVI_CONTROL_FALSE error + */ +static int set_crossbar_input(priv_t * priv, int input) +{ + HRESULT hr; + int i, nVideoDecoder, nAudioDecoder; + long lInput, lInputRelated, lRelated, lPhysicalType, lOutputPins, + lInputPins; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Configuring crossbar\n"); + if (!priv->pCrossbar || input < 0 + || input >= tv_available_inputs_count) + return TVI_CONTROL_FALSE; + + OLE_CALL_ARGS(priv->pCrossbar, get_PinCounts, &lOutputPins, &lInputPins); + + lInput = tv_available_inputs[input]; + + if (lInput < 0 || lInput >= lInputPins) + return TVI_CONTROL_FALSE; + + OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 1 /* input */ , lInput, + &lInputRelated, &lPhysicalType); + + nVideoDecoder = nAudioDecoder = -1; + for (i = 0; i < lOutputPins; i++) { + OLE_CALL_ARGS(priv->pCrossbar, get_CrossbarPinInfo, 0 /*output */ , i, + &lRelated, &lPhysicalType); + if (lPhysicalType == PhysConn_Video_VideoDecoder) + nVideoDecoder = i; + if (lPhysicalType == PhysConn_Audio_AudioDecoder) + nAudioDecoder = i; + } + if (nVideoDecoder >= 0) { + //connecting given input with video decoder + hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nVideoDecoder, lInput); + if (hr != S_OK) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputVideoDecoder, (unsigned int)hr); + return TVI_CONTROL_FALSE; + } + } + if (nAudioDecoder >= 0 && lInputRelated >= 0) { + hr = OLE_CALL_ARGS(priv->pCrossbar, Route, nAudioDecoder, + lInputRelated); + if (hr != S_OK) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableConnectInputAudioDecoder, (unsigned int)hr); + return TVI_CONTROL_FALSE; + } + } + return TVI_CONTROL_TRUE; +} + +/** + * \brief adjusts video control (hue,saturation,contrast,brightess) + * + * \param priv driver's private data + * \param control which control to adjust + * \param value new value for control (0-100) + * + * \return TVI_CONTROL_TRUE success + * \return TVI_CONTROL_FALSE error + */ +static int set_control(priv_t * priv, int control, int value) +{ + long lMin, lMax, lStepping, lDefault, lFlags, lValue; + HRESULT hr; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: set_control called\n"); + if (value < -100 || value > 100 || !priv->pVideoProcAmp) + return TVI_CONTROL_FALSE; + + hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control, + &lMin, &lMax, &lStepping, &lDefault, &lFlags); + if (FAILED(hr) || lFlags != VideoProcAmp_Flags_Manual) + return TVI_CONTROL_FALSE; + + lValue = lMin + (value + 100) * (lMax - lMin) / 200; + /* + Workaround for ATI AIW 7500. The driver reports: max=255, stepping=256 + */ + if (lStepping > lMax) { + mp_msg(MSGT_TV, MSGL_DBG3, + "tvi_dshow: Stepping (%ld) is bigger than max value (%ld) for control %d. Assuming 1\n", + lStepping, lMax,control); + lStepping = 1; + } + lValue -= lValue % lStepping; + hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Set, control, lValue, + VideoProcAmp_Flags_Manual); + if (FAILED(hr)) + return TVI_CONTROL_FALSE; + + return TVI_CONTROL_TRUE; +} + +/** + * \brief get current value of video control (hue,saturation,contrast,brightess) + * + * \param priv driver's private data + * \param control which control to adjust + * \param pvalue address of variable thar receives current value + * + * \return TVI_CONTROL_TRUE success + * \return TVI_CONTROL_FALSE error + */ +static int get_control(priv_t * priv, int control, int *pvalue) +{ + long lMin, lMax, lStepping, lDefault, lFlags, lValue; + HRESULT hr; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: get_control called\n"); + if (!pvalue || !priv->pVideoProcAmp) + return TVI_CONTROL_FALSE; + + hr = OLE_CALL_ARGS(priv->pVideoProcAmp, GetRange, control, + &lMin, &lMax, &lStepping, &lDefault, &lFlags); + if (FAILED(hr)) + return TVI_CONTROL_FALSE; + if (lMin == lMax) { + *pvalue = lMin; + return TVI_CONTROL_TRUE; + } + + hr = OLE_CALL_ARGS(priv->pVideoProcAmp, Get, control, &lValue, &lFlags); + if (FAILED(hr)) + return TVI_CONTROL_FALSE; + + *pvalue = 200 * (lValue - lMin) / (lMax - lMin) - 100; + + return TVI_CONTROL_TRUE; +} + +/** + * \brief extracts fcc,width,height from AM_MEDIA_TYPE + * + * \param pmt pointer to AM_MEDIA_TYPE to extract data from + * \param pfcc address of variable that receives FourCC + * \param pwidth address of variable that receives width + * \param pheight address of variable that recevies height + * + * \return 1 if data extracted successfully, 0 - otherwise + */ +static int extract_video_format(AM_MEDIA_TYPE * pmt, int *pfcc, + int *pwidth, int *pheight) +{ + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_video_format called\n"); + if (!pmt) + return 0; + if (!pmt->pbFormat) + return 0; + if (memcmp(&(pmt->formattype), &FORMAT_VideoInfo, 16) != 0) + return 0; + if (pfcc) + *pfcc = subtype2imgfmt(&(pmt->subtype)); + if (pwidth) + *pwidth = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biWidth; + if (pheight) + *pheight = ((VIDEOINFOHEADER *) pmt->pbFormat)->bmiHeader.biHeight; + return 1; +} + +/** + * \brief extracts samplerate,bits,channels from AM_MEDIA_TYPE + * + * \param pmt pointer to AM_MEDIA_TYPE to extract data from + * \param pfcc address of variable that receives samplerate + * \param pwidth address of variable that receives number of bits per sample + * \param pheight address of variable that recevies number of channels + * + * \return 1 if data extracted successfully, 0 - otherwise + */ +static int extract_audio_format(AM_MEDIA_TYPE * pmt, int *psamplerate, + int *pbits, int *pchannels) +{ + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: extract_audio_format called\n"); + if (!pmt) + return 0; + if (!pmt->pbFormat) + return 0; + if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0) + return 0; + if (psamplerate) + *psamplerate = ((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec; + if (pbits) + *pbits = ((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample; + if (pchannels) + *pchannels = ((WAVEFORMATEX *) pmt->pbFormat)->nChannels; + return 1; +} + +/** + * \brief checks if AM_MEDIA_TYPE compatible with given samplerate,bits,channels + * + * \param pmt pointer to AM_MEDIA_TYPE for check + * \param samplerate audio samplerate + * \param bits bits per sample + * \param channels number of audio channels + * + * \return 1 if AM_MEDIA_TYPE compatible + * \return 0 if not + */ +static int check_audio_format(AM_MEDIA_TYPE * pmt, int samplerate, + int bits, int channels) +{ + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_audio_format called\n"); + if (!pmt) + return 0; + if (memcmp(&(pmt->majortype), &MEDIATYPE_Audio, 16) != 0) + return 0; + if (memcmp(&(pmt->subtype), &MEDIASUBTYPE_PCM, 16) != 0) + return 0; + if (memcmp(&(pmt->formattype), &FORMAT_WaveFormatEx, 16) != 0) + return 0; + if (!pmt->pbFormat) + return 0; + if (((WAVEFORMATEX *) pmt->pbFormat)->nSamplesPerSec != samplerate) + return 0; + if (((WAVEFORMATEX *) pmt->pbFormat)->wBitsPerSample != bits) + return 0; + if (channels > 0 + && ((WAVEFORMATEX *) pmt->pbFormat)->nChannels != channels) + return 0; + + return 1; +} + +/** + * \brief checks if AM_MEDIA_TYPE compatible with given fcc,width,height + * + * \param pmt pointer to AM_MEDIA_TYPE for check + * \param fcc FourCC (compression) + * \param width width of picture + * \param height height of picture + * + * \return 1 if AM_MEDIA_TYPE compatible + & \return 0 if not + * + * \note + * width and height are currently not used + * + * \todo + * add width/height check + */ +static int check_video_format(AM_MEDIA_TYPE * pmt, int fcc, int width, + int height) +{ + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: check_video_format called\n"); + if (!pmt) + return 0; + if (memcmp(&(pmt->majortype), &MEDIATYPE_Video, 16) != 0) + return 0; + if (subtype2imgfmt(&(pmt->subtype)) != fcc) + return 0; + return 1; +} + +/** + * \brief converts DirectShow subtype to MPlayer's IMGFMT + * + * \param subtype DirectShow subtype for video format + * + * \return MPlayer's IMGFMT or 0 if error occured + */ +static int subtype2imgfmt(const GUID * subtype) +{ + int i; + for (i = 0; img_fmt_list[i].fmt; i++) { + if (memcmp(subtype, img_fmt_list[i].subtype, 16) == 0) + return img_fmt_list[i].fmt; + } + return 0; +} + +/** + * \brief prints filter name and it pins + * + * \param pFilter - IBaseFilter to get data from + * + * \return S_OK if success, error code otherwise + */ +static HRESULT show_filter_info(IBaseFilter * pFilter) +{ + char tmp[200]; + FILTER_INFO fi; + LPENUMPINS pEnum = 0; + IPin *pPin = 0; + PIN_DIRECTION ThisPinDir; + PIN_INFO pi; + HRESULT hr; + int i; + + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: show_filter_info called\n"); + memset(&fi, 0, sizeof(fi)); + memset(tmp, 0, 200); + + OLE_CALL_ARGS(pFilter, QueryFilterInfo, &fi); + OLE_RELEASE_SAFE(fi.pGraph); + wtoa(fi.achName, tmp, 200); + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: BaseFilter (%p): Name=%s, Graph=%p output pins:", + pFilter, tmp, fi.pGraph); + hr = OLE_CALL_ARGS(pFilter, EnumPins, &pEnum); + if (FAILED(hr)) + return hr; + i = 0; + while (OLE_CALL_ARGS(pEnum, Next, 1, &pPin, NULL) == S_OK) { + memset(&pi, 0, sizeof(pi)); + memset(tmp, 0, 200); + OLE_CALL_ARGS(pPin, QueryDirection, &ThisPinDir); + if (ThisPinDir == PINDIR_OUTPUT) { + OLE_CALL_ARGS(pPin, QueryPinInfo, &pi); + wtoa(pi.achName, tmp, 200); + OLE_RELEASE_SAFE(pi.pFilter); + mp_msg(MSGT_TV, MSGL_DBG2, " %d=%s", i, tmp); + mp_msg(MSGT_TV, MSGL_DBG3, " (%p)", pPin); + mp_msg(MSGT_TV, MSGL_DBG2, ";"); + OLE_RELEASE_SAFE(pPin); + i++; + } + } + mp_msg(MSGT_TV, MSGL_DBG2, "\n"); + OLE_RELEASE_SAFE(pEnum); + return S_OK; +} + +/** + * \brief gets device's frendly in ANSI encoding + * + * \param pM IMoniker interface, got in enumeration process + * \param category device category + * + * \return TVI_CONTROL_TRUE if operation succeded, TVI_CONTROL_FALSE - otherwise + */ +static int get_device_name(IMoniker * pM, char *pBuf, int nLen) +{ + HRESULT hr; + VARIANT var; + IPropertyBag *pPropBag; + hr = OLE_CALL_ARGS(pM, BindToStorage, 0, 0, &IID_IPropertyBag,(void *) &pPropBag); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Call to BindToStorage failed\n"); + return TVI_CONTROL_FALSE; + } + var.vt = VT_BSTR; + hr = OLE_CALL_ARGS(pPropBag, Read, L"Description", (LPVARIANT) & var, + NULL); + if (FAILED(hr)) { + hr = OLE_CALL_ARGS(pPropBag, Read, L"FriendlyName", (LPVARIANT) & var, + NULL); + } + OLE_RELEASE_SAFE(pPropBag); + if (SUCCEEDED(hr)) { + wtoa(var.bstrVal, pBuf, nLen); + return TVI_CONTROL_TRUE; + } + return TVI_CONTROL_FALSE; +} + +/** + * \brief find capture device at given index + * + * \param index device index to search for (-1 mean only print available) + * \param category device category + * + * \return IBaseFilter interface for capture device with given index + * + * Sample values for category: + * CLSID_VideoInputDeviceCategory - Video Capture Sources + * CLSID_AudioInputDeviceCategory - Audio Capture Sources + * See DirectShow SDK documentation for other possible values + */ +static IBaseFilter *find_capture_device(int index, REFCLSID category) +{ + IBaseFilter *pFilter = NULL; + ICreateDevEnum *pDevEnum = NULL; + IEnumMoniker *pClassEnum = NULL; + IMoniker *pM; + HRESULT hr; + ULONG cFetched; + int i; + char tmp[DEVICE_NAME_MAX_LEN + 1]; + hr = CoCreateInstance((GUID *) & CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum, + (void *) &pDevEnum); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create device enumerator\n"); + return NULL; + } + + hr = OLE_CALL_ARGS(pDevEnum, CreateClassEnumerator, category, &pClassEnum, 0); + OLE_RELEASE_SAFE(pDevEnum); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to create class enumerator\n"); + return NULL; + } + if (hr == S_FALSE) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: No capture devices found\n"); + return NULL; + } + + OLE_CALL(pClassEnum,Reset); + for (i = 0; OLE_CALL_ARGS(pClassEnum, Next, 1, &pM, &cFetched) == S_OK; i++) { + if(get_device_name(pM, tmp, DEVICE_NAME_MAX_LEN)!=TVI_CONTROL_TRUE) + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableGetDeviceName, i); + else + mp_msg(MSGT_TV, MSGL_V, MSGTR_TVI_DS_DeviceName, i, tmp); + if (index != -1 && i == index) { + mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_UsingDevice, index, tmp); + hr = OLE_CALL_ARGS(pM, BindToObject, 0, 0, &IID_IBaseFilter,(void *) &pFilter); + if (FAILED(hr)) + pFilter = NULL; + } + OLE_RELEASE_SAFE(pM); + } + if (index != -1 && !pFilter) { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_DeviceNotFound, + index); + } + OLE_RELEASE_SAFE(pClassEnum); + + return pFilter; +} + +/** + * \brief get array of available formats through call to IAMStreamConfig::GetStreamCaps + * + * \param[in] pVideoStreamConfig IAMStreamConfig of used capture device's output pin + * \param[in] pMediaType MEDIATYPE_Video or MEDIATYPE_Audio + * \param[out] parpmtMedia address of variable that receives pointer to array of AM_MEDIA_TYPE structures + * \param[out] parCaps address of variable thar receives pointer to array of VIDEOSTREAM_CONFIG_CAPS or AUDIO_STREAM_CONFIG_CAPS + * + * \return S_OK success + * \return E_POINTER one of parameters is NULL + * \return E_FAIL required size of buffer is unknown for given media type + * \return E_OUTOFMEMORY not enough memory + * \return other error code from called methods + * + * \remarks + * last item or returned arrays will be NULL + */ +static HRESULT get_available_formats_stream(IAMStreamConfig * + pStreamConfig, + const GUID * pMediaType, + AM_MEDIA_TYPE *** parpmtMedia, + void ***parCaps) +{ + AM_MEDIA_TYPE **arpmt; + void **pBuf=NULL; + + HRESULT hr; + int i, count, size; + int done; + + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: get_available_formats_stream called\n"); + + if (!pStreamConfig || !pMediaType || !parpmtMedia || !parCaps) + return E_POINTER; + + hr=OLE_CALL_ARGS(pStreamConfig, GetNumberOfCapabilities, &count, &size); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: Call to GetNumberOfCapabilities failed (get_available_formats_stream)\n"); + return hr; + } + if (memcmp(pMediaType, &MEDIATYPE_Video, 16) == 0){ + if (size != sizeof(VIDEO_STREAM_CONFIG_CAPS)) { + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: Wrong video structure size for GetNumberOfCapabilities (get_available_formats_stream)\n"); + return E_FAIL; + } else if (memcmp(pMediaType, &MEDIATYPE_Audio, 16) == 0){ + if (size != sizeof(AUDIO_STREAM_CONFIG_CAPS)) { + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: Wrong audio structure size for GetNumberOfCapabilities (get_available_formats_stream)\n"); + return E_FAIL; + } else { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_stream"); + return E_FAIL; + } + } + } + done = 0; + + arpmt = (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *)); + if (arpmt) { + memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *)); + + pBuf = (void **) malloc((count + 1) * sizeof(void *)); + if (pBuf) { + memset(pBuf, 0, (count + 1) * sizeof(void *)); + + for (i = 0; i < count; i++) { + pBuf[i] = malloc(size); + + if (!pBuf[i]) + break; + + hr = OLE_CALL_ARGS(pStreamConfig, GetStreamCaps, i, + &(arpmt[i]), pBuf[i]); + if (FAILED(hr)) + break; + } + if (i == count) { + *parpmtMedia = arpmt; + *parCaps = pBuf; + done = 1; + } + } + } + if (!done) { + for (i = 0; i < count; i++) { + if (pBuf && pBuf[i]) + free(pBuf[i]); + if (arpmt && arpmt[i]) + DeleteMediaType(arpmt[i]); + } + if (pBuf) + free(pBuf); + if (arpmt) + free(arpmt); + if (hr != S_OK) { + mp_msg(MSGT_TV, MSGL_DBG4, "tvi_dshow: Call to GetStreamCaps failed (get_available_formats_stream)\n"); + return hr; + } else + return E_OUTOFMEMORY; + } + return S_OK; +} + +/** + * \brief returns allocates an array and store available media formats for given pin type to it + * + * \param pBuilder ICaptureGraphBuilder2 interface of graph builder + * \param pFilter IBaseFilter interface of capture device + * \param pMediaType media type for search (MEDIATYPE_Video or MEDIATYPE_Audio) + * \param[out] parpmtMediaType address of variable that receives pointer to array + * + * \return S_OK success + * \return E_POINTER one of given pointers is null + * \return apropriate error code otherwise + */ +static HRESULT get_available_formats_pin(ICaptureGraphBuilder2 * pBuilder, + IBaseFilter * pFilter, + const GUID * pMediaType, + AM_MEDIA_TYPE *** parpmtMedia, + void ***parCaps) +{ + IEnumMediaTypes *pEnum; + IPin *pPin; + int i, count, size; + ULONG cFetched; + AM_MEDIA_TYPE *pmt; + HRESULT hr; + void **pBuf; + AM_MEDIA_TYPE **arpmt; //This will be real array + int isvideo; + VIDEO_STREAM_CONFIG_CAPS *pVideoCaps; + AUDIO_STREAM_CONFIG_CAPS *pAudioCaps; + int p1, p2, p3; + + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: get_available_formats_pin called\n"); + if (!pBuilder || !pFilter || !pMediaType || !parpmtMedia) + return E_POINTER; + + hr = OLE_CALL_ARGS(pBuilder, FindPin, (IUnknown *) pFilter, + PINDIR_OUTPUT, NULL, pMediaType, FALSE, 0, &pPin); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: Call to FindPin failed (get_available_formats_pin)\n"); + return hr; + } + if (memcmp(pMediaType, &MEDIATYPE_Video, 16) == 0) { + isvideo = 1; + size = sizeof(VIDEO_STREAM_CONFIG_CAPS); + } else if (memcmp(pMediaType, &MEDIATYPE_Audio, 16) == 0) { + isvideo = 0; + size = sizeof(AUDIO_STREAM_CONFIG_CAPS); + } else { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnsupportedMediaType,"get_available_formats_pin"); + return E_FAIL; + } + + hr = OLE_CALL_ARGS(pPin, EnumMediaTypes, &pEnum); + OLE_RELEASE_SAFE(pPin); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG4, + "tvi_dshow: Call to EnumMediaTypes failed (get_available_formats_pin)\n"); + return hr; + } + for (i = 0; OLE_CALL_ARGS(pEnum, Next, 1, &pmt, &cFetched) == S_OK; i++) { + if (!pmt) + break; + } + OLE_CALL(pEnum,Reset); + + count = i; + arpmt = + (AM_MEDIA_TYPE **) malloc((count + 1) * sizeof(AM_MEDIA_TYPE *)); + if (!arpmt) + return E_OUTOFMEMORY; + memset(arpmt, 0, (count + 1) * sizeof(AM_MEDIA_TYPE *)); + + for (i = 0; + i < count + && OLE_CALL_ARGS(pEnum, Next, 1, &(arpmt[i]), &cFetched) == S_OK; + i++); + + OLE_RELEASE_SAFE(pEnum); + + + pBuf = (void **) malloc((count + 1) * sizeof(void *)); + if (!pBuf) { + for (i = 0; i < count; i++) + if (arpmt[i]) + DeleteMediaType(arpmt[i]); + free(arpmt); + return E_OUTOFMEMORY; + } + memset(pBuf, 0, (count + 1) * sizeof(void *)); + + for (i = 0; i < count; i++) { + pBuf[i] = malloc(size); + if (!pBuf[i]) + break; + memset(pBuf[i], 0, size); + + if (isvideo) { + pVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *) pBuf[i]; + extract_video_format(arpmt[i], NULL, &p1, &p2); + pVideoCaps->MaxOutputSize.cx = pVideoCaps->MinOutputSize.cx = + p1; + pVideoCaps->MaxOutputSize.cy = pVideoCaps->MinOutputSize.cy = + p2; + } else { + pAudioCaps = (AUDIO_STREAM_CONFIG_CAPS *) pBuf[i]; + extract_audio_format(arpmt[i], &p1, &p2, &p3); + pAudioCaps->MaximumSampleFrequency = + pAudioCaps->MinimumSampleFrequency = p1; + pAudioCaps->MaximumBitsPerSample = + pAudioCaps->MinimumBitsPerSample = p2; + pAudioCaps->MaximumChannels = pAudioCaps->MinimumChannels = p3; + } + + } + if (i != count) { + for (i = 0; i < count; i++) { + if (arpmt[i]) + DeleteMediaType(arpmt[i]); + if (pBuf[i]) + free(pBuf[i]); + } + free(arpmt); + free(pBuf); + return E_OUTOFMEMORY; + } + *parpmtMedia = arpmt; + *parCaps = pBuf; + + return S_OK; +} + +/* +*--------------------------------------------------------------------------------------- +* +* Public methods +* +*--------------------------------------------------------------------------------------- +*/ +/** + * \brief fills given buffer with audio data (usually one block) + * + * \param priv driver's private data structure + * \param buffer buffer to store data to + * \param len buffer's size in bytes (usually one block size) + * + * \return audio pts if audio present, 1 - otherwise + */ +static double grab_audio_frame(priv_t * priv, char *buffer, int len) +{ + int bytes = 0; + int i; + double pts; + grabber_ringbuffer_t *rb = priv->a_buf; + grabber_ringbuffer_t *vrb = priv->v_buf; + + if (!rb || !rb->ringbuffer) + return 1; + + if(vrb && vrb->tStart<0){ + memset(buffer,0,len); + return 0; + } + if(vrb && rb->tStart<0) + rb->tStart=vrb->tStart; + + if (len < rb->blocksize) + bytes = len; + else + bytes = rb->blocksize; + + mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (audio) called. %d blocks in buffer, %d bytes requested\n", + rb->count, len); + if(!rb->count){ + mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n"); + for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000); + if(!rb->count){ + mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n"); + return 0; + } + mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n"); + } + + EnterCriticalSection(rb->pMutex); + pts=rb->dpts[rb->head]-rb->tStart; + memcpy(buffer, rb->ringbuffer[rb->head], bytes); + rb->head = (rb->head + 1) % rb->buffersize; + rb->count--; + LeaveCriticalSection(rb->pMutex); + return pts; +} + +/** + * \brief returns audio frame size + * + * \param priv driver's private data structure + * + * \return audio block size if audio enabled and 1 - otherwise + */ +static int get_audio_framesize(priv_t * priv) +{ + if (!priv->a_buf) + return 1; //no audio + mp_msg(MSGT_TV,MSGL_DBG3,"get_audio_framesize: %d\n",priv->a_buf->blocksize); + return priv->a_buf->blocksize; +} + +#ifdef HAVE_TV_TELETEXT +static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp) +{ + if(!priv || !ptsp) + return TVI_CONTROL_FALSE; + +//STUBS!!! + ptsp->interlaced=0; + ptsp->offset=256; + + ptsp->sampling_rate=27e6; + ptsp->samples_per_line=720; + + ptsp->count[0]=16; + ptsp->count[1]=16; +//END STUBS!!! + ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]); + + mp_msg(MSGT_TV,MSGL_V,"vbi_get_props: sampling_rate=%d,offset:%d,samples_per_line: %d\n interlaced:%s, count=[%d,%d]\n", + ptsp->sampling_rate, + ptsp->offset, + ptsp->samples_per_line, + ptsp->interlaced?"Yes":"No", + ptsp->count[0], + ptsp->count[1]); + + return TVI_CONTROL_TRUE; +} + +static void vbi_grabber(priv_t* priv) +{ + grabber_ringbuffer_t *rb = priv->vbi_buf; + int i; + unsigned char* buf; + if (!rb || !rb->ringbuffer) + return; + + buf=calloc(1,rb->blocksize); + for(i=0; i<23 && rb->count; i++){ + memcpy(buf,rb->ringbuffer[rb->head],rb->blocksize); + teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf); + rb->head = (rb->head + 1) % rb->buffersize; + rb->count--; + } + free(buf); +} +#endif //HAVE_TV_TELETEXT + +/** + * \brief fills given buffer with video data (usually one frame) + * + * \param priv driver's private data structure + * \param buffer buffer to store data to + * \param len buffer's size in bytes (usually one frame size) + * + * \return frame size if video present, 0 - otherwise + */ +static double grab_video_frame(priv_t * priv, char *buffer, int len) +{ + int bytes = 0; + int i; + double pts; + grabber_ringbuffer_t *rb = priv->v_buf; + + if (!rb || !rb->ringbuffer) + return 1; + if (len < rb->blocksize) + bytes = len; + else + bytes = rb->blocksize; + + mp_msg(MSGT_TV, MSGL_DBG3,"tvi_dshow: FillBuffer (video) called. %d blocks in buffer, %d bytes requested\n", + rb->count, len); + if(!rb->count){ + mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting for frame\n"); + for(i=0;i<1000 && !rb->count;i++) usec_sleep(1000); + if(!rb->count){ + mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: waiting timeout\n"); + return 0; + } + mp_msg(MSGT_TV,MSGL_DBG4,"tvi_dshow: got frame!\n"); + } + EnterCriticalSection(rb->pMutex); + if(rb->tStart<0) + rb->tStart=rb->dpts[rb->head]; + pts=rb->dpts[rb->head]-rb->tStart; + memcpy(buffer, rb->ringbuffer[rb->head], bytes); + rb->head = (rb->head + 1) % rb->buffersize; + rb->count--; + LeaveCriticalSection(rb->pMutex); + +#ifdef HAVE_TV_TELETEXT + vbi_grabber(priv); +#endif + return pts; +} + +/** + * \brief returns frame size + * + * \param priv driver's private data structure + * + * \return frame size if video present, 0 - otherwise + */ +static int get_video_framesize(priv_t * priv) +{ +// if(!priv->pmtVideo) return 1; //no video +// return(priv->pmtVideo->lSampleSize); + if (!priv->v_buf) + return 1; //no video +mp_msg(MSGT_TV,MSGL_DBG3,"geT_video_framesize: %d\n",priv->v_buf->blocksize); + return priv->v_buf->blocksize; +} + +/** + * \brief calculate audio buffer size + * \param video_buf_size size of video buffer in bytes + * \param video_bitrate video bit rate + * \param audio_bitrate audio bit rate + * \return audio buffer isze in bytes + * + * \remarks length of video buffer and resulted audio buffer calculated in + * seconds will be the same. + */ +static inline int audio_buf_size_from_video(int video_buf_size, int video_bitrate, int audio_bitrate) +{ + int audio_buf_size = audio_bitrate * (video_buf_size / video_bitrate); + mp_msg(MSGT_TV,MSGL_DBG2,"tvi_dshow: Audio capture buffer: %d * %d / %d = %d\n", + audio_bitrate,video_buf_size,video_bitrate,audio_buf_size); + return audio_buf_size; +} + +/** + * \brief playback/capture real start + * + * \param priv driver's private data structure + * + * \return 1 if success, 0 - otherwise + * + * TODO: move some code from init() here + */ +static int start(priv_t * priv) +{ + HRESULT hr; + + if (priv->pVideoStreamConfig) { + hr = OLE_CALL_ARGS(priv->pVideoStreamConfig, SetFormat, priv->pmtVideo); + if (FAILED(hr)) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectVideoFormat, (unsigned int)hr); + } + } + + if (!priv->immediate_mode && priv->pAudioStreamConfig) { + hr = OLE_CALL_ARGS(priv->pAudioStreamConfig, SetFormat, + priv->pmtAudio); + if (FAILED(hr)) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableSelectAudioFormat, (unsigned int)hr); + } + } + + priv->v_buf=calloc(1,sizeof(grabber_ringbuffer_t)); + + if (priv->tv_param->buffer_size >= 0) { + priv->v_buf->buffersize = priv->tv_param->buffer_size; + } else { + priv->v_buf->buffersize = 16; + } + + priv->v_buf->buffersize *= 1024 * 1024; + hr=build_sub_graph(priv, priv->pVideoFilter, priv->v_buf, priv->pmtVideo,&PIN_CATEGORY_CAPTURE); + if(FAILED(hr)){ + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVideoSubGraph,(unsigned int)hr); + return 0; + } + if(priv->pmtAudio && !priv->immediate_mode){ + priv->a_buf=calloc(1,sizeof(grabber_ringbuffer_t)); + + /* let the audio buffer be the same size (in seconds) than video one */ + priv->a_buf->buffersize=audio_buf_size_from_video( + priv->v_buf->buffersize, + (((VIDEOINFOHEADER *) priv->pmtVideo->pbFormat)->dwBitRate), + (((WAVEFORMATEX *) (priv->pmtAudio->pbFormat))->nAvgBytesPerSec)); + + hr=build_sub_graph(priv, priv->pAudioFilter, priv->a_buf,priv->pmtAudio,&PIN_CATEGORY_CAPTURE); + if(FAILED(hr)){ + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildAudioSubGraph,(unsigned int)hr); + return 0; + } + } + +#ifdef HAVE_TV_TELETEXT + if(priv->tv_param->tdevice) + { + priv->vbi_buf=calloc(1,sizeof(grabber_ringbuffer_t)); + init_ringbuffer(priv->vbi_buf,24,priv->tsp.bufsize); + + priv->pmtVBI=calloc(1,sizeof(AM_MEDIA_TYPE)); + priv->pmtVBI->majortype=MEDIATYPE_VBI; + hr=build_sub_graph(priv, priv->pVideoFilter, priv->vbi_buf,priv->pmtVBI,&PIN_CATEGORY_VBI); + if(FAILED(hr)){ + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_UnableBuildVBISubGraph,(unsigned int)hr); + return 0; + } + } +#endif + /* + Graph is ready to capture. Starting graph. + */ + if (mp_msg_test(MSGT_TV, MSGL_DBG2)) { + mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause 10sec\n"); + usec_sleep(10000000); + mp_msg(MSGT_TV, MSGL_DBG2, "Debug pause end\n"); + } + if (!priv->pMediaControl) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)E_POINTER); + return 0; + } + hr = OLE_CALL(priv->pMediaControl, Run); + if (FAILED(hr)) { + mp_msg(MSGT_TV,MSGL_ERR,MSGTR_TVI_DS_UnableStartGraph, (unsigned int)hr); + return 0; + } + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Graph is started.\n"); + priv->state = 1; + + return (1); +} + +/** + * \brief driver initialization + * + * \param priv driver's private data structure + * + * \return 1 if success, 0 - otherwise + */ +static int init(priv_t * priv) +{ + HRESULT hr; + int result = 0; + long lInput, lTunerInput; + IEnumFilters *pEnum; + IBaseFilter *pFilter; + IPin *pVPOutPin; + + priv->state=0; + CoInitialize(NULL); + do{ + hr = CoCreateInstance((GUID *) & CLSID_FilterGraph, NULL, + CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, + (void **) &priv->pGraph); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(FilterGraph) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + //Debug + if (mp_msg_test(MSGT_TV, MSGL_DBG2)) { + AddToRot((IUnknown *) priv->pGraph, &(priv->dwRegister)); + } + + hr = CoCreateInstance((GUID *) & CLSID_CaptureGraphBuilder2, NULL, + CLSCTX_INPROC_SERVER, &IID_ICaptureGraphBuilder2, + (void **) &priv->pBuilder); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: CoCreateInstance(CaptureGraphBuilder) call failed. Error:0x%x\n", (unsigned int)hr); + break; + } + + hr = OLE_CALL_ARGS(priv->pBuilder, SetFiltergraph, priv->pGraph); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_ERR, "tvi_dshow: SetFiltergraph call failed. Error:0x%x\n",(unsigned int)hr); + break; + } + + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available video capture devices\n"); + priv->pVideoFilter = find_capture_device(priv->dev_index, &CLSID_VideoInputDeviceCategory); + if(!priv->pVideoFilter){ + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoVideoCaptureDevice); + break; + } + hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->pVideoFilter, NULL); + if(FAILED(hr)){ + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to add video capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr); + break; + } + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Searching for available audio capture devices\n"); + if (priv->adev_index != -1) { + priv->pAudioFilter = find_capture_device(priv->adev_index, &CLSID_AudioInputDeviceCategory); //output available audio edevices + if(!priv->pAudioFilter){ + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_NoAudioCaptureDevice); + break; + } + + hr = OLE_CALL_ARGS(priv->pGraph, AddFilter, priv->pAudioFilter, NULL); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Unable to add audio capture device to Directshow graph. Error:0x%x\n", (unsigned int)hr); + break; + } + } else + hr = OLE_QUERYINTERFACE(priv->pVideoFilter, IID_IBaseFilter, priv->pAudioFilter); + + hr = OLE_QUERYINTERFACE(priv->pVideoFilter, IID_IAMVideoProcAmp,priv->pVideoProcAmp); + if (FAILED(hr) && hr != E_NOINTERFACE) + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IID_IAMVideoProcAmp failed (0x%x).\n", (unsigned int)hr); + + if (hr != S_OK) { + mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_VideoAdjustigNotSupported); + priv->pVideoProcAmp = NULL; + } + + hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, + &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + priv->pVideoFilter, + &IID_IAMCrossbar, (void **) &(priv->pCrossbar)); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_SelectingInputNotSupported); + priv->pCrossbar = NULL; + } + + hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, + &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Video, + priv->pVideoFilter, + &IID_IAMStreamConfig, + (void **) &(priv->pVideoStreamConfig)); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_INFO, MSGTR_TVI_DS_ChangingWidthHeightNotSupported); + priv->pVideoStreamConfig = NULL; + } + + hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, + &PIN_CATEGORY_CAPTURE, + &MEDIATYPE_Audio, + priv->pAudioFilter, + &IID_IAMStreamConfig, + (void **) &(priv->pAudioStreamConfig)); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Get IAMStreamConfig(audio) failed (0x%x).\n", (unsigned int)hr); + priv->pAudioStreamConfig = NULL; + } + + if (priv->tv_param->amode >= 0) { + IAMTVAudio *pTVAudio; + hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, NULL, NULL,priv->pVideoFilter,&IID_IAMTVAudio, (void *) &pTVAudio); + if (hr == S_OK) { + switch (priv->tv_param->amode) { + case 0: + hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_MONO); + break; + case 1: + hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, AMTVAUDIO_MODE_STEREO); + break; + case 2: + hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, + AMTVAUDIO_MODE_LANG_A); + break; + case 3: + hr = OLE_CALL_ARGS(pTVAudio, put_TVAudioMode, + AMTVAUDIO_MODE_LANG_B); + break; + } + OLE_RELEASE_SAFE(pTVAudio); + if (FAILED(hr)) + mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_UnableSetAudioMode, priv->tv_param->amode,(unsigned int)hr); + } + } + /* + Getting available video formats (last pointer in array will be NULL) + First tryin to call IAMStreamConfig::GetStreamCaos. this will give us additional information such as + min/max picture dimensions, etc. If this call fails trying IPIn::EnumMediaTypes with default + min/max values. + */ + + hr = get_available_formats_stream(priv->pVideoStreamConfig, + &MEDIATYPE_Video, + &(priv->arpmtVideo), + (void ***) &(priv->arVideoCaps)); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available video formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr); + hr = get_available_formats_pin(priv->pBuilder, priv->pVideoFilter, + &MEDIATYPE_Video, + &(priv->arpmtVideo), + (void ***) &(priv->arVideoCaps)); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableGetsupportedVideoFormats, (unsigned int)hr); + break; + } + } + priv->nVideoFormatUsed = 0; + + if (!priv->arpmtVideo[priv->nVideoFormatUsed] + || !extract_video_format(priv->arpmtVideo[priv->nVideoFormatUsed], + &(priv->fcc), &(priv->width), + &(priv->height))) { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingVideoFormatStruct); + break; + } + priv->pmtVideo = CreateMediaType(priv->arpmtVideo[priv->nVideoFormatUsed]); + /* + Getting available audio formats (last pointer in array will be NULL) + */ + hr = get_available_formats_stream(priv->pAudioStreamConfig, + &MEDIATYPE_Audio, + &(priv->arpmtAudio), + (void ***) &(priv->arAudioCaps)); + if (FAILED(hr)) { + mp_msg(MSGT_TV, MSGL_DBG2, "Unable to use IAMStreamConfig for retriving available audio formats (Error:0x%x). Using EnumMediaTypes instead\n", (unsigned int)hr); + hr = get_available_formats_pin(priv->pBuilder, priv->pAudioFilter, + &MEDIATYPE_Audio, + &(priv->arpmtAudio), + (void ***) &(priv->arAudioCaps)); + if (FAILED(hr)) { + mp_msg(MSGT_TV,MSGL_WARN, MSGTR_TVI_DS_UnableGetsupportedAudioFormats, (unsigned int)hr); + /* + Following combination will disable sound + */ + priv->arpmtAudio = (AM_MEDIA_TYPE **) malloc(sizeof(AM_MEDIA_TYPE *)); + priv->arpmtAudio[0] = NULL; + priv->pmtAudio = NULL; + priv->pAudioStreamConfig = NULL; + } + } + /* debug */ + { + int i; + for (i = 0; priv->arpmtVideo[i]; i++) { + DisplayMediaType("Available video format", priv->arpmtVideo[i]); + } + for (i = 0; priv->arpmtAudio[i]; i++) { + DisplayMediaType("Available audio format", priv->arpmtAudio[i]); + } + } + priv->nAudioFormatUsed = 0; + if (priv->arpmtAudio[priv->nAudioFormatUsed]) { + priv->pmtAudio = CreateMediaType(priv->arpmtAudio[priv->nAudioFormatUsed]); + if (!extract_audio_format(priv->pmtAudio, &(priv->samplerate), NULL, NULL)) { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_ErrorParsingAudioFormatStruct); + DisplayMediaType("audio format failed",priv->arpmtAudio[priv->nAudioFormatUsed]); + break; + } + } + show_filter_info(priv->pVideoFilter); + + hr = OLE_QUERYINTERFACE(priv->pGraph, IID_IMediaControl,priv->pMediaControl); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableGetMediaControlInterface,(unsigned int)hr); + break; + } + hr = OLE_CALL_ARGS(priv->pBuilder, FindInterface, + &PIN_CATEGORY_CAPTURE, NULL, + priv->pVideoFilter, + &IID_IAMTVTuner, (void **) &(priv->pTVTuner)); + + if (!priv->pTVTuner) { + mp_msg(MSGT_TV, MSGL_DBG2, "tvi_dshow: Unable to access IAMTVTuner (0x%x)\n", (unsigned int)hr); + } + + // shows Tuner capabilities + get_capabilities(priv); + + if (priv->pTVTuner) { + hr = OLE_CALL_ARGS(priv->pTVTuner, put_CountryCode, + chanlist2country(priv->tv_param->chanlist)); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_CountryCode failed. Error:0x%x\n",(unsigned int)hr); + break; + } + + hr = OLE_CALL_ARGS(priv->pTVTuner, put_Mode, AMTUNER_MODE_TV); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_Mode failed. Error:0x%x\n",(unsigned int)hr); + break; + } + + hr = OLE_CALL_ARGS(priv->pTVTuner, get_ConnectInput, &lInput); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to get_ConnectInput failed. Error:0x%x\n",(unsigned int)hr); + break; + } + + /* small hack */ + lTunerInput = strstr(priv->tv_param->chanlist, "cable") ? TunerInputCable : TunerInputAntenna; + + hr = OLE_CALL_ARGS(priv->pTVTuner, put_InputType, lInput, lTunerInput); + if(FAILED(hr)){ + mp_msg(MSGT_TV,MSGL_DBG2, "tvi_dshow: Call to put_InputType failed. Error:0x%x\n",(unsigned int)hr); + break; + } + + } + + /** + for VIVO cards we should check if preview pin is available on video capture device. + If it is not, we have to connect Video Port Manager filter to VP pin of capture device filter. + Otherwise we will get 0x8007001f (Device is not functioning properly) when attempting to start graph + */ + hr = OLE_CALL_ARGS(priv->pBuilder, FindPin, + (IUnknown *) priv->pVideoFilter, + PINDIR_OUTPUT, + &PIN_CATEGORY_VIDEOPORT, NULL, FALSE, + 0, (IPin **) & pVPOutPin); + if (SUCCEEDED(hr)) { + hr = OLE_CALL_ARGS(priv->pGraph, Render, pVPOutPin); + OLE_RELEASE_SAFE(pVPOutPin); + + if (FAILED(hr)) { + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_UnableTerminateVPPin, (unsigned int)hr); + break; + } + } + + OLE_CALL_ARGS(priv->pGraph, EnumFilters, &pEnum); + while (OLE_CALL_ARGS(pEnum, Next, 1, &pFilter, NULL) == S_OK) { + LPVIDEOWINDOW pVideoWindow; + hr = OLE_QUERYINTERFACE(pFilter, IID_IVideoWindow, pVideoWindow); + if (SUCCEEDED(hr)) + { + if(priv->tv_param->hidden_vp_renderer){ + OLE_CALL_ARGS(pVideoWindow,put_Visible,/* OAFALSE*/ 0); + OLE_CALL_ARGS(pVideoWindow,put_AutoShow,/* OAFALSE*/ 0); + }else + { + OLE_CALL_ARGS(priv->pGraph, RemoveFilter, pFilter); + } + OLE_RELEASE_SAFE(pVideoWindow); + } + OLE_RELEASE_SAFE(pFilter); + } + OLE_RELEASE_SAFE(pEnum); + if(priv->tv_param->system_clock) + { + LPREFERENCECLOCK rc; + IBaseFilter* pBF; + hr = CoCreateInstance((GUID *) & CLSID_SystemClock, NULL, + CLSCTX_INPROC_SERVER, &IID_IReferenceClock, + (void *) &rc); + + OLE_QUERYINTERFACE(priv->pBuilder,IID_IBaseFilter,pBF); + OLE_CALL_ARGS(pBF,SetSyncSource,rc); + } +#ifdef HAVE_TV_TELETEXT + if(vbi_get_props(priv,&(priv->tsp))!=TVI_CONTROL_TRUE) + break; +#endif + result = 1; + } while(0); + + if (!result){ + mp_msg(MSGT_TV,MSGL_ERR, MSGTR_TVI_DS_GraphInitFailure); + uninit(priv); + } + return result; +} + +/** + * \brief driver uninitialization + * + * \param priv driver's private data structure + * + * \return always 1 + */ +static int uninit(priv_t * priv) +{ + int i; + if (!priv) + return (1); + //Debug + if (priv->dwRegister) { + RemoveFromRot(priv->dwRegister); + } +#ifdef HAVE_TV_TELETEXT + teletext_control(priv->priv_vbi,TV_VBI_CONTROL_STOP,(void*)1); +#endif + //stop audio grabber thread + + if (priv->state && priv->pMediaControl) { + OLE_CALL(priv->pMediaControl, Stop); + } + OLE_RELEASE_SAFE(priv->pMediaControl); + priv->state = 0; + + if (priv->pGraph) { + if (priv->pVideoFilter) + OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->pVideoFilter); + if (priv->pAudioFilter) + OLE_CALL_ARGS(priv->pGraph, RemoveFilter, priv->pAudioFilter); + } + OLE_RELEASE_SAFE(priv->pCrossbar); + OLE_RELEASE_SAFE(priv->pVideoStreamConfig); + OLE_RELEASE_SAFE(priv->pAudioStreamConfig); + OLE_RELEASE_SAFE(priv->pVideoProcAmp); + OLE_RELEASE_SAFE(priv->pVideoFilter); + OLE_RELEASE_SAFE(priv->pAudioFilter); + OLE_RELEASE_SAFE(priv->pGraph); + OLE_RELEASE_SAFE(priv->pBuilder); + OLE_RELEASE_SAFE(priv->pCSGCB); + + if (priv->pmtVideo) + DeleteMediaType(priv->pmtVideo); + if (priv->pmtAudio) + DeleteMediaType(priv->pmtAudio); + if (priv->pmtVBI) + DeleteMediaType(priv->pmtVBI); + + if (priv->arpmtVideo) { + for (i = 0; priv->arpmtVideo[i]; i++) { + DeleteMediaType(priv->arpmtVideo[i]); + } + free(priv->arpmtVideo); + } + if (priv->arVideoCaps) { + for (i = 0; priv->arVideoCaps[i]; i++) { + free(priv->arVideoCaps[i]); + } + free(priv->arVideoCaps); + } + if (priv->arpmtAudio) { + for (i = 0; priv->arpmtAudio[i]; i++) { + DeleteMediaType(priv->arpmtAudio[i]); + } + free(priv->arpmtAudio); + } + if (priv->arAudioCaps) { + for (i = 0; priv->arAudioCaps[i]; i++) { + free(priv->arAudioCaps[i]); + } + free(priv->arAudioCaps); + } + if (priv->a_buf) { + destroy_ringbuffer(priv->a_buf); + free(priv->a_buf); + priv->a_buf = NULL; + } + if (priv->v_buf) { + destroy_ringbuffer(priv->v_buf); + free(priv->v_buf); + priv->v_buf = NULL; + } + if (priv->vbi_buf) { + destroy_ringbuffer(priv->vbi_buf); + free(priv->vbi_buf); + priv->vbi_buf = NULL; + } + if(priv->freq_table){ + priv->freq_table_len=-1; + free(priv->freq_table); + priv->freq_table=NULL; + } + CoUninitialize(); + return (1); +} + +/** + * \brief driver pre-initialization + * + * \param device string, containing device name in form "x[.y]", where x is video capture device + * (default: 0, first available); y (if given) sets audio capture device + * + * \return 1 if success,0 - otherwise + */ +static tvi_handle_t *tvi_init_dshow(tv_param_t* tv_param) +{ + tvi_handle_t *h; + priv_t *priv; + int a; + + h = new_handle(); + if (!h) + return (NULL); + + priv = h->priv; + + memset(priv, 0, sizeof(priv_t)); + priv->direct_setfreq_call = 1; //first using direct call. if it fails, workaround will be enabled + priv->direct_getfreq_call = 1; //first using direct call. if it fails, workaround will be enabled + priv->adev_index = -1; + priv->freq_table_len=-1; + priv->tv_param=tv_param; + + if (tv_param->device) { + if (sscanf(tv_param->device, "%d", &a) == 1) { + priv->dev_index = a; + } else { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceParam, tv_param->device); + free_handle(h); + return NULL; + } + if (priv->dev_index < 0) { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongDeviceIndex, a); + free_handle(h); + return NULL; + } + } + if (tv_param->adevice) { + if (sscanf(tv_param->adevice, "%d", &a) == 1) { + priv->adev_index = a; + } else { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceParam, tv_param->adevice); + free_handle(h); + return NULL; + } + if (priv->dev_index < 0) { + mp_msg(MSGT_TV, MSGL_ERR, MSGTR_TVI_DS_WrongADeviceIndex, a); + free_handle(h); + return NULL; + } + } + return h; +} + +/** + * \brief driver's ioctl handler + * + * \param priv driver's private data structure + * \param cmd ioctl command + * \param arg ioct command's parameter + * + * \return TVI_CONTROL_TRUE if success + * \return TVI_CONTROL_FALSE if failure + * \return TVI_CONTROL_UNKNOWN if unknowm cmd called + */ +static int control(priv_t * priv, int cmd, void *arg) +{ + switch (cmd) { +/* need rewrite */ + case TVI_CONTROL_VID_SET_FORMAT: + { + int fcc, i; + if (priv->state) + return TVI_CONTROL_FALSE; + fcc = *(int *) arg; + + if(!priv->arpmtVideo) + return TVI_CONTROL_FALSE; + for (i = 0; priv->arpmtVideo[i]; i++) + if (check_video_format + (priv->arpmtVideo[i], fcc, priv->width, priv->height)) + break; + if (!priv->arpmtVideo[i]) + return TVI_CONTROL_FALSE; + + priv->nVideoFormatUsed = i; + + if (priv->pmtVideo) + DeleteMediaType(priv->pmtVideo); + priv->pmtVideo = + CreateMediaType(priv->arpmtVideo[priv->nVideoFormatUsed]); + DisplayMediaType("VID_SET_FORMAT", priv->pmtVideo); + /* + Setting width & height to preferred by driver values + */ + extract_video_format(priv->arpmtVideo[priv->nVideoFormatUsed], + &(priv->fcc), &(priv->width), + &(priv->height)); + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_VID_GET_FORMAT: + { + if(!priv->pmtVideo) + return TVI_CONTROL_FALSE; + DisplayMediaType("VID_GET_FORMAT", priv->pmtVideo); + if (priv->fcc) { + *(int *) arg = priv->fcc; + return (TVI_CONTROL_TRUE); + } else + return (TVI_CONTROL_FALSE); + } + case TVI_CONTROL_VID_SET_WIDTH: + { + VIDEO_STREAM_CONFIG_CAPS *pCaps; + VIDEOINFOHEADER *Vhdr; + int width = *(int *) arg; + if (priv->state) + return TVI_CONTROL_FALSE; + + pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; + if (!pCaps) + return TVI_CONTROL_FALSE; + if (width < pCaps->MinOutputSize.cx + || width > pCaps->MaxOutputSize.cx) + return TVI_CONTROL_FALSE; + + if (width % pCaps->OutputGranularityX) + return TVI_CONTROL_FALSE; + + if (!priv->pmtVideo || !priv->pmtVideo->pbFormat) + return TVI_CONTROL_FALSE; + Vhdr = (VIDEOINFOHEADER *) priv->pmtVideo->pbFormat; + Vhdr->bmiHeader.biWidth = width; + priv->pmtVideo->lSampleSize = Vhdr->bmiHeader.biSizeImage = + labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth * + Vhdr->bmiHeader.biHeight) >> 3; + + priv->width = width; + + return (TVI_CONTROL_TRUE); + } + case TVI_CONTROL_VID_GET_WIDTH: + { + if (priv->width) { + *(int *) arg = priv->width; + return (TVI_CONTROL_TRUE); + } else + return TVI_CONTROL_FALSE; + } + case TVI_CONTROL_VID_CHK_WIDTH: + { + VIDEO_STREAM_CONFIG_CAPS *pCaps; + int width = *(int *) arg; + pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; + if (!pCaps) + return TVI_CONTROL_FALSE; + if (width < pCaps->MinOutputSize.cx + || width > pCaps->MaxOutputSize.cx) + return TVI_CONTROL_FALSE; + + if (width % pCaps->OutputGranularityX) + return TVI_CONTROL_FALSE; + return (TVI_CONTROL_TRUE); + } + case TVI_CONTROL_VID_SET_HEIGHT: + { + VIDEO_STREAM_CONFIG_CAPS *pCaps; + VIDEOINFOHEADER *Vhdr; + int height = *(int *) arg; + if (priv->state) + return TVI_CONTROL_FALSE; + + pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; + if (!pCaps) + return TVI_CONTROL_FALSE; + if (height < pCaps->MinOutputSize.cy + || height > pCaps->MaxOutputSize.cy) + return TVI_CONTROL_FALSE; + + if (height % pCaps->OutputGranularityY) + return TVI_CONTROL_FALSE; + + if (!priv->pmtVideo || !priv->pmtVideo->pbFormat) + return TVI_CONTROL_FALSE; + Vhdr = (VIDEOINFOHEADER *) priv->pmtVideo->pbFormat; + + if (Vhdr->bmiHeader.biHeight < 0) + Vhdr->bmiHeader.biHeight = -height; + else + Vhdr->bmiHeader.biHeight = height; + priv->pmtVideo->lSampleSize = Vhdr->bmiHeader.biSizeImage = + labs(Vhdr->bmiHeader.biBitCount * Vhdr->bmiHeader.biWidth * + Vhdr->bmiHeader.biHeight) >> 3; + + priv->height = height; + return (TVI_CONTROL_TRUE); + } + case TVI_CONTROL_VID_GET_HEIGHT: + { + if (priv->height) { + *(int *) arg = priv->height; + return (TVI_CONTROL_TRUE); + } else + return TVI_CONTROL_FALSE; + } + case TVI_CONTROL_VID_CHK_HEIGHT: + { + VIDEO_STREAM_CONFIG_CAPS *pCaps; + int height = *(int *) arg; + pCaps = priv->arVideoCaps[priv->nVideoFormatUsed]; + if (!pCaps) + return TVI_CONTROL_FALSE; + if (height < pCaps->MinOutputSize.cy + || height > pCaps->MaxOutputSize.cy) + return TVI_CONTROL_FALSE; + + if (height % pCaps->OutputGranularityY) + return TVI_CONTROL_FALSE; + + return (TVI_CONTROL_TRUE); + } + case TVI_CONTROL_IS_AUDIO: + if (!priv->pmtAudio) + return TVI_CONTROL_FALSE; + else + return TVI_CONTROL_TRUE; + case TVI_CONTROL_IS_VIDEO: + return TVI_CONTROL_TRUE; + case TVI_CONTROL_AUD_GET_FORMAT: + { + *(int *) arg = AF_FORMAT_S16_LE; + if (!priv->pmtAudio) + return TVI_CONTROL_FALSE; + else + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_AUD_GET_CHANNELS: + { + *(int *) arg = priv->channels; + if (!priv->pmtAudio) + return TVI_CONTROL_FALSE; + else + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_AUD_SET_SAMPLERATE: + { + int i, samplerate; + if (priv->state) + return TVI_CONTROL_FALSE; + if (!priv->arpmtAudio[0]) + return TVI_CONTROL_FALSE; + + samplerate = *(int *) arg;; + + for (i = 0; priv->arpmtAudio[i]; i++) + if (check_audio_format + (priv->arpmtAudio[i], samplerate, 16, priv->channels)) + break; + if (!priv->arpmtAudio[i]) { + //request not found. failing back to first available + mp_msg(MSGT_TV, MSGL_WARN, MSGTR_TVI_DS_SamplerateNotsupported, samplerate); + i = 0; + } + if (priv->pmtAudio) + DeleteMediaType(priv->pmtAudio); + priv->pmtAudio = CreateMediaType(priv->arpmtAudio[i]); + extract_audio_format(priv->arpmtAudio[i], &(priv->samplerate), + NULL, &(priv->channels)); + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_AUD_GET_SAMPLERATE: + { + *(int *) arg = priv->samplerate; + if (!priv->samplerate) + return TVI_CONTROL_FALSE; + if (!priv->pmtAudio) + return TVI_CONTROL_FALSE; + else + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_AUD_GET_SAMPLESIZE: + { + WAVEFORMATEX *pWF; + if (!priv->pmtAudio) + return TVI_CONTROL_FALSE; + if (!priv->pmtAudio->pbFormat) + return TVI_CONTROL_FALSE; + pWF = (WAVEFORMATEX *) priv->pmtAudio->pbFormat; + *(int *) arg = pWF->wBitsPerSample / 8; + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_IS_TUNER: + { + if (!priv->pTVTuner) + return TVI_CONTROL_FALSE; + + return (TVI_CONTROL_TRUE); + } + case TVI_CONTROL_TUN_SET_NORM: + { + IAMAnalogVideoDecoder *pVD; + long lAnalogFormat; + int i; + HRESULT hr; + + i = *(int *) arg; + i--; + if (i < 0 || i >= tv_available_norms_count) + return TVI_CONTROL_FALSE; + lAnalogFormat = tv_norms[tv_available_norms[i]].index; + + hr = OLE_QUERYINTERFACE(priv->pVideoFilter,IID_IAMAnalogVideoDecoder, pVD); + if (hr != S_OK) + return TVI_CONTROL_FALSE; + hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat); + OLE_RELEASE_SAFE(pVD); + if (FAILED(hr)) + return (TVI_CONTROL_FALSE); + else + return (TVI_CONTROL_TRUE); + } + case TVI_CONTROL_TUN_GET_NORM: + { + long lAnalogFormat; + int i; + HRESULT hr; + IAMAnalogVideoDecoder *pVD; + + hr = OLE_QUERYINTERFACE(priv->pVideoFilter,IID_IAMAnalogVideoDecoder, pVD); + if (hr == S_OK) { + hr = OLE_CALL_ARGS(pVD, get_TVFormat, &lAnalogFormat); + OLE_RELEASE_SAFE(pVD); + } + + if (FAILED(hr)) { //trying another method + if (!priv->pTVTuner) + return TVI_CONTROL_FALSE; + hr=OLE_CALL_ARGS(priv->pTVTuner, get_TVFormat, &lAnalogFormat); + if (FAILED(hr)) + return TVI_CONTROL_FALSE; + } + for (i = 0; i < tv_available_norms_count; i++) { + if (tv_norms[tv_available_norms[i]].index == lAnalogFormat) { + *(int *) arg = i + 1; + return TVI_CONTROL_TRUE; + } + } + return (TVI_CONTROL_FALSE); + } + case TVI_CONTROL_SPC_GET_NORMID: + { + int i; + if (!priv->pTVTuner) + return TVI_CONTROL_FALSE; + for (i = 0; i < tv_available_norms_count; i++) { + if (!strcasecmp + (tv_norms[tv_available_norms[i]].name, (char *) arg)) { + *(int *) arg = i + 1; + return TVI_CONTROL_TRUE; + } + } + return TVI_CONTROL_FALSE; + } + case TVI_CONTROL_SPC_SET_INPUT: + { + return set_crossbar_input(priv, *(int *) arg); + } + case TVI_CONTROL_TUN_GET_FREQ: + { + unsigned long lFreq; + int ret; + if (!priv->pTVTuner) + return TVI_CONTROL_FALSE; + + ret = get_frequency(priv, &lFreq); + lFreq = lFreq * 16 / 1000000; //convert from Hz to 1/16 MHz units + + *(unsigned long *) arg = lFreq; + return ret; + } + case TVI_CONTROL_TUN_SET_FREQ: + { + unsigned long nFreq = *(unsigned long *) arg; + if (!priv->pTVTuner) + return TVI_CONTROL_FALSE; + //convert to Hz + nFreq = 1000000 * nFreq / 16; //convert from 1/16 MHz units to Hz + return set_frequency(priv, nFreq); + } + case TVI_CONTROL_VID_SET_HUE: + return set_control(priv, VideoProcAmp_Hue, *(int *) arg); + case TVI_CONTROL_VID_GET_HUE: + return get_control(priv, VideoProcAmp_Hue, (int *) arg); + case TVI_CONTROL_VID_SET_CONTRAST: + return set_control(priv, VideoProcAmp_Contrast, *(int *) arg); + case TVI_CONTROL_VID_GET_CONTRAST: + return get_control(priv, VideoProcAmp_Contrast, (int *) arg); + case TVI_CONTROL_VID_SET_SATURATION: + return set_control(priv, VideoProcAmp_Saturation, *(int *) arg); + case TVI_CONTROL_VID_GET_SATURATION: + return get_control(priv, VideoProcAmp_Saturation, (int *) arg); + case TVI_CONTROL_VID_SET_BRIGHTNESS: + return set_control(priv, VideoProcAmp_Brightness, *(int *) arg); + case TVI_CONTROL_VID_GET_BRIGHTNESS: + return get_control(priv, VideoProcAmp_Brightness, (int *) arg); + + case TVI_CONTROL_VID_GET_FPS: + { + VIDEOINFOHEADER *Vhdr; + if (!priv->pmtVideo) + return TVI_CONTROL_FALSE; + if (!priv->pmtVideo->pbFormat) + return TVI_CONTROL_FALSE; + Vhdr = (VIDEOINFOHEADER *) priv->pmtVideo->pbFormat; + *(float *) arg = + (1.0 * Vhdr->dwBitRate) / Vhdr->bmiHeader.biSizeImage; + return TVI_CONTROL_TRUE; + } + case TVI_CONTROL_IMMEDIATE: + priv->immediate_mode = 1; + return TVI_CONTROL_TRUE; +#ifdef HAVE_TV_TELETEXT + case TVI_CONTROL_VBI_INIT: + { + void* ptr; + ptr=&(priv->tsp); + if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==TVI_CONTROL_TRUE) + priv->priv_vbi=ptr; + else + priv->priv_vbi=NULL; + return TVI_CONTROL_TRUE; + } + default: + return teletext_control(priv->priv_vbi,cmd,arg); +#endif + } + return (TVI_CONTROL_UNKNOWN); +} diff -r acb856c03bd8 -r d81eef9beb1b stream/tvi_dshow.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stream/tvi_dshow.h Sat Oct 13 17:14:39 2007 +0000 @@ -0,0 +1,693 @@ +#ifndef TVI_DSHOW_H_ +#define TVI_DSHOW_H_ + +/// \defgroup tvi_dshow TV driver (Directshow) + +#define INITGUID +#include +//#include +#include +#include "loader/dshow/mediatype.h" +#include "loader/dshow/guids.h" + +#define wtoa(strW,strA,lenA) WideCharToMultiByte(0,0,strW,-1,strA,lenA,NULL,NULL) +#define atow(strA,strW,lenW) MultiByteToWideChar(0,0,strA,strlen(strA),strW,lenW) + +typedef struct DISPPARAMS *LPDISPPARAMS; +typedef struct IFileSinkFilter *LPFILESINKFILTER; +typedef struct IAMCopyCaptureFileProgress *LPAMCOPYCAPTUREFILEPROGRESS; +typedef struct IErrorLog *LPERRORLOG; +typedef struct IAMTunerNotification *LPAMTUNERNOTIFICATION; +typedef struct IFilterGraph *LPFILTERGRAPH; +typedef struct IBaseFilter *LPBASEFILTER; +typedef struct IPin *LPPIN; +typedef struct IEnumPins *LPENUMPINS; +typedef struct IEnumFilters *LPENUMFILTERS; +typedef struct IEnumMediaTypes *LPENUMMEDIATYPES; +typedef struct IReferenceClock *LPREFERENCECLOCK; +typedef struct IMediaSample *LPMEDIASAMPLE; +typedef struct IVideoWindow *LPVIDEOWINDOW; + +typedef struct +{ + long cBuffers; + long cbBuffer; + long cbAlign; + long cbPrefix; +}ALLOCATOR_PROPERTIES; + +typedef + enum tagTunerInputType { TunerInputCable = 0, + TunerInputAntenna = TunerInputCable + 1 +} TunerInputType; +typedef enum tagAMTunerModeType { + AMTUNER_MODE_DEFAULT = 0x0000, + AMTUNER_MODE_TV = 0x0001, + AMTUNER_MODE_FM_RADIO = 0x0002, + AMTUNER_MODE_AM_RADIO = 0x0004, + AMTUNER_MODE_DSS = 0x0008 +} AMTunerModeType; +enum tagAMTunerSubChannel { AMTUNER_SUBCHAN_NO_TUNE = -2, + AMTUNER_SUBCHAN_DEFAULT = -1 +} AMTunerSubChannel; +typedef enum tagVideoProcAmpProperty { + VideoProcAmp_Brightness, + VideoProcAmp_Contrast, + VideoProcAmp_Hue, + VideoProcAmp_Saturation, + VideoProcAmp_Sharpness, + VideoProcAmp_Gamma, + VideoProcAmp_ColorEnable, + VideoProcAmp_WhiteBalance, + VideoProcAmp_BacklightCompensation, + VideoProcAmp_Gain +} VideoProcAmpProperty; + +typedef long OAFilterState; +typedef + enum tagAnalogVideoStandard { AnalogVideo_None = 0, + AnalogVideo_NTSC_M = 0x1, + AnalogVideo_NTSC_M_J = 0x2, + AnalogVideo_NTSC_433 = 0x4, + AnalogVideo_PAL_B = 0x10, + AnalogVideo_PAL_D = 0x20, + AnalogVideo_PAL_G = 0x40, + AnalogVideo_PAL_H = 0x80, + AnalogVideo_PAL_I = 0x100, + AnalogVideo_PAL_M = 0x200, + AnalogVideo_PAL_N = 0x400, + AnalogVideo_PAL_60 = 0x800, + AnalogVideo_SECAM_B = 0x1000, + AnalogVideo_SECAM_D = 0x2000, + AnalogVideo_SECAM_G = 0x4000, + AnalogVideo_SECAM_H = 0x8000, + AnalogVideo_SECAM_K = 0x10000, + AnalogVideo_SECAM_K1 = 0x20000, + AnalogVideo_SECAM_L = 0x40000, + AnalogVideo_SECAM_L1 = 0x80000 +} AnalogVideoStandard; + + +typedef LONG_PTR OAHWND; +typedef enum tagPhysicalConnectorType { PhysConn_Video_Tuner = 1, + PhysConn_Video_Composite = PhysConn_Video_Tuner + 1, + PhysConn_Video_SVideo = PhysConn_Video_Composite + 1, + PhysConn_Video_RGB = PhysConn_Video_SVideo + 1, + PhysConn_Video_YRYBY = PhysConn_Video_RGB + 1, + PhysConn_Video_SerialDigital = PhysConn_Video_YRYBY + 1, + PhysConn_Video_ParallelDigital = PhysConn_Video_SerialDigital + 1, + PhysConn_Video_SCSI = PhysConn_Video_ParallelDigital + 1, + PhysConn_Video_AUX = PhysConn_Video_SCSI + 1, + PhysConn_Video_1394 = PhysConn_Video_AUX + 1, + PhysConn_Video_USB = PhysConn_Video_1394 + 1, + PhysConn_Video_VideoDecoder = PhysConn_Video_USB + 1, + PhysConn_Video_VideoEncoder = PhysConn_Video_VideoDecoder + 1, + PhysConn_Video_SCART = PhysConn_Video_VideoEncoder + 1, + PhysConn_Video_Black = PhysConn_Video_SCART + 1, + PhysConn_Audio_Tuner = 0x1000, + PhysConn_Audio_Line = PhysConn_Audio_Tuner + 1, + PhysConn_Audio_Mic = PhysConn_Audio_Line + 1, + PhysConn_Audio_AESDigital = PhysConn_Audio_Mic + 1, + PhysConn_Audio_SPDIFDigital = PhysConn_Audio_AESDigital + 1, + PhysConn_Audio_SCSI = PhysConn_Audio_SPDIFDigital + 1, + PhysConn_Audio_AUX = PhysConn_Audio_SCSI + 1, + PhysConn_Audio_1394 = PhysConn_Audio_AUX + 1, + PhysConn_Audio_USB = PhysConn_Audio_1394 + 1, + PhysConn_Audio_AudioDecoder = PhysConn_Audio_USB + 1 +} PhysicalConnectorType; + +typedef struct _VIDEO_STREAM_CONFIG_CAPS { + GUID guid; // will be MEDIATYPE_Video + ULONG VideoStandard; // logical OR of all AnalogVideoStandards + // supported + SIZE InputSize; // the inherent size of the incoming signal + // (every pixel unique) + SIZE MinCroppingSize; // smallest rcSrc cropping rect allowed + SIZE MaxCroppingSize; // largest rcSrc cropping rect allowed + int CropGranularityX; // granularity of cropping size + int CropGranularityY; + int CropAlignX; // alignment of cropping rect + int CropAlignY; + SIZE MinOutputSize; // smallest bitmap stream can produce + SIZE MaxOutputSize; // largest bitmap stream can produce + int OutputGranularityX; // granularity of output bitmap size + int OutputGranularityY; + int StretchTapsX; // 0, no stretch, 1 pix dup, 2 interp, ... + int StretchTapsY; // Describes quality of hardware scaler + int ShrinkTapsX; // + int ShrinkTapsY; // + LONGLONG MinFrameInterval; // 100 nS units + LONGLONG MaxFrameInterval; + LONG MinBitsPerSecond; + LONG MaxBitsPerSecond; +} VIDEO_STREAM_CONFIG_CAPS, *PVIDEO_STREAM_CONFIG_CAPS; + +typedef struct _AUDIO_STREAM_CONFIG_CAPS { + GUID guid; + ULONG MinimumChannels; + ULONG MaximumChannels; + ULONG ChannelsGranularity; + ULONG MinimumBitsPerSample; + ULONG MaximumBitsPerSample; + ULONG BitsPerSampleGranularity; + ULONG MinimumSampleFrequency; + ULONG MaximumSampleFrequency; + ULONG SampleFrequencyGranularity; +} AUDIO_STREAM_CONFIG_CAPS; + +typedef enum tagVideoProcAmpFlags { + VideoProcAmp_Flags_Auto = 0x0001, + VideoProcAmp_Flags_Manual = 0x0002 +} VideoProcAmpFlags; +typedef enum { + PINDIR_INPUT = 0, + PINDIR_OUTPUT +} PIN_DIRECTION; + +#define KSPROPERTY_SUPPORT_GET 1 +#define KSPROPERTY_SUPPORT_SET 2 +typedef struct { + GUID Set; + ULONG Id; + ULONG Flags; +} KSIDENTIFIER; + +typedef KSIDENTIFIER KSPROPERTY; + + +typedef struct { + KSPROPERTY Property; + ULONG Mode; // IN: KSPROPERTY_TUNER_MODE + ULONG StandardsSupported; // KS_AnalogVideo_* (if TV or DSS) + ULONG MinFrequency; // Hz + ULONG MaxFrequency; // Hz + ULONG TuningGranularity; // Hz + ULONG NumberOfInputs; // count of inputs + ULONG SettlingTime; // milliSeconds + ULONG Strategy; // KS_TUNER_STRATEGY +} KSPROPERTY_TUNER_MODE_CAPS_S, *PKSPROPERTY_TUNER_MODE_CAPS_S; + +typedef struct { + KSPROPERTY Property; + ULONG Mode; // IN: KSPROPERTY_TUNER_MODE +} KSPROPERTY_TUNER_MODE_S, *PKSPROPERTY_TUNER_MODE_S; + +typedef struct { + KSPROPERTY Property; + ULONG Frequency; // Hz + ULONG LastFrequency; // Hz (last known good) + ULONG TuningFlags; // KS_TUNER_TUNING_FLAGS + ULONG VideoSubChannel; // DSS + ULONG AudioSubChannel; // DSS + ULONG Channel; // VBI decoders + ULONG Country; // VBI decoders +} KSPROPERTY_TUNER_FREQUENCY_S, *PKSPROPERTY_TUNER_FREQUENCY_S; +typedef struct { + KSPROPERTY Property; + ULONG CurrentFrequency; + ULONG PLLOffset; + ULONG SignalStrength; + ULONG Busy; +} KSPROPERTY_TUNER_STATUS_S, *PKSPROPERTY_TUNER_STATUS_S; +typedef enum { + KS_TUNER_TUNING_EXACT = 1, // No fine tuning + KS_TUNER_TUNING_FINE, // Fine grained search + KS_TUNER_TUNING_COARSE, // Coarse search +} KS_TUNER_TUNING_FLAGS; + +typedef enum { + KSPROPERTY_TUNER_CAPS, // R -overall device capabilities + KSPROPERTY_TUNER_MODE_CAPS, // R -capabilities in this mode + KSPROPERTY_TUNER_MODE, // RW -set a mode (TV, FM, AM, DSS) + KSPROPERTY_TUNER_STANDARD, // R -get TV standard (only if TV mode) + KSPROPERTY_TUNER_FREQUENCY, // RW -set/get frequency + KSPROPERTY_TUNER_INPUT, // RW -select an input + KSPROPERTY_TUNER_STATUS, // R -tuning status + KSPROPERTY_TUNER_IF_MEDIUM // R O-Medium for IF or Transport Pin +} KSPROPERTY_TUNER; +typedef enum { + KS_TUNER_STRATEGY_PLL = 0X01, // Tune by PLL offset + KS_TUNER_STRATEGY_SIGNAL_STRENGTH = 0X02, // Tune by signal strength + KS_TUNER_STRATEGY_DRIVER_TUNES = 0X04, // Driver does fine tuning +} KS_TUNER_STRATEGY; +typedef enum tagTVAudioMode { + AMTVAUDIO_MODE_MONO = 0x0001, + AMTVAUDIO_MODE_STEREO = 0x0002, + AMTVAUDIO_MODE_LANG_A = 0x0010, + AMTVAUDIO_MODE_LANG_B = 0x0020, + AMTVAUDIO_MODE_LANG_C = 0x0040, +} TVAudioMode; + +typedef struct _FilterInfo { + WCHAR achName[128]; + LPFILTERGRAPH pGraph; +} FILTER_INFO; + +typedef struct _PinInfo { + LPBASEFILTER pFilter; + PIN_DIRECTION dir; + unsigned short achName[128]; +} PIN_INFO; +//----------------------------------- + + +#undef INTERFACE +#define INTERFACE IPin +DECLARE_INTERFACE(IPin) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(Connect) (THIS_ IPin *, AM_MEDIA_TYPE *); + STDMETHOD(ReceiveConnection) (THIS_ IPin *, const AM_MEDIA_TYPE *); + STDMETHOD(Disconnect) (THIS); + STDMETHOD(ConnectedTo) (THIS_ IPin **); + STDMETHOD(ConnectionMediaType) (THIS_ AM_MEDIA_TYPE * pmt); + STDMETHOD(QueryPinInfo) (THIS_ PIN_INFO *); + STDMETHOD(QueryDirection) (THIS_ PIN_DIRECTION *); + STDMETHOD(QueryId) (THIS_ unsigned short **); + STDMETHOD(QueryAccept) (THIS_ const AM_MEDIA_TYPE *); + STDMETHOD(EnumMediaTypes) (THIS_ LPENUMMEDIATYPES *); + STDMETHOD(QueryInternalConnections) (THIS_ IPin **, unsigned long *); + STDMETHOD(EndOfStream) (THIS); + STDMETHOD(BeginFlush) (THIS); + STDMETHOD(EndFlush) (THIS); + STDMETHOD(NewSegment) (THIS_ REFERENCE_TIME, REFERENCE_TIME, double); +}; + +#undef INTERFACE +#define INTERFACE IBaseFilter +DECLARE_INTERFACE(IBaseFilter) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(GetClassID) (THIS_ CLSID * pClassID); + STDMETHOD(Stop) (THIS); + STDMETHOD(Pause) (THIS); + STDMETHOD(Run) (THIS_ REFERENCE_TIME tStart); + STDMETHOD(GetState) (THIS_ unsigned long, void *); + STDMETHOD(SetSyncSource) (THIS_ LPREFERENCECLOCK); + STDMETHOD(GetSyncSource) (THIS_ LPREFERENCECLOCK *); + STDMETHOD(EnumPins) (THIS_ LPENUMPINS *); + STDMETHOD(FindPin) (THIS_ const unsigned short *, LPPIN *); + STDMETHOD(QueryFilterInfo) (THIS_ void *); + STDMETHOD(JoinFilterGraph) (THIS_ LPFILTERGRAPH, + const unsigned short *); + STDMETHOD(QueryVendorInfo) (THIS_ unsigned short **); +}; + +#undef INTERFACE +#define INTERFACE IAMTVTuner +DECLARE_INTERFACE(IAMTVTuner) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(put_Channel) (THIS_ long, long, long); + STDMETHOD(get_Channel) (THIS_ long *, long *, long *); + STDMETHOD(ChannelMinMax) (THIS_ long *, long *); + STDMETHOD(put_CountryCode) (THIS_ long); + STDMETHOD(get_CountryCode) (THIS_ long *); + STDMETHOD(put_TuningSpace) (THIS_ long); + STDMETHOD(get_TuningSpace) (THIS_ long *); + STDMETHOD(Logon) (THIS_ HANDLE); + STDMETHOD(Logout) (IAMTVTuner *); + STDMETHOD(SignalPresen) (THIS_ long *); + STDMETHOD(put_Mode) (THIS_ AMTunerModeType); + STDMETHOD(get_Mode) (THIS_ AMTunerModeType *); + STDMETHOD(GetAvailableModes) (THIS_ long *); + STDMETHOD(RegisterNotificationCallBack) (THIS_ LPAMTUNERNOTIFICATION, + long); + STDMETHOD(UnRegisterNotificationCallBack) (THIS_ + LPAMTUNERNOTIFICATION); + STDMETHOD(get_AvailableTVFormats) (THIS_ long *); + STDMETHOD(get_TVFormat) (THIS_ long *); + STDMETHOD(AutoTune) (THIS_ long, long *); + STDMETHOD(StoreAutoTune) (IAMTVTuner *); + STDMETHOD(get_NumInputConnections) (THIS_ long *); + STDMETHOD(put_InputType) (THIS_ long, TunerInputType); + STDMETHOD(get_InputType) (THIS_ long, TunerInputType *); + STDMETHOD(put_ConnectInput) (THIS_ long); + STDMETHOD(get_ConnectInput) (THIS_ long *); + STDMETHOD(get_VideoFrequency) (THIS_ long *); + STDMETHOD(get_AudioFrequency) (THIS_ long *); +}; + +#undef INTERFACE +#define INTERFACE IMediaControl +DECLARE_INTERFACE(IMediaControl) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(GetTypeInfoCount) (THIS_ UINT *); + STDMETHOD(GetTypeInfo) (THIS_ UINT, LCID, LPTYPEINFO *); + STDMETHOD(GetIDsOfNames) (THIS_ REFIID, LPOLESTR *, UINT, LCID, + DISPID *); + STDMETHOD(Invoke) (THIS_ DISPID, REFIID, LCID, WORD, LPDISPPARAMS, + VARIANT *, EXCEPINFO *, UINT *); + STDMETHOD(Run) (THIS); + STDMETHOD(Pause) (THIS); + STDMETHOD(Stop) (THIS); + STDMETHOD(GetState) (THIS_ LONG, OAFilterState *); + STDMETHOD(RenderFile) (THIS_ BSTR); + STDMETHOD(AddSourceFilter) (THIS_ BSTR, LPDISPATCH *); + STDMETHOD(get_FilterCollection) (THIS_ LPDISPATCH *); + STDMETHOD(get_RegFilterCollection) (THIS_ LPDISPATCH *); + STDMETHOD(StopWhenReady) (IMediaControl *); +}; + +#undef INTERFACE +#define INTERFACE IGraphBuilder +DECLARE_INTERFACE(IGraphBuilder) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(AddFilter) (THIS_ IBaseFilter *, LPCWSTR); + STDMETHOD(RemoveFilter) (THIS_ IBaseFilter *); + STDMETHOD(EnumFilters) (THIS_ LPENUMFILTERS *); + STDMETHOD(FindFilterByName) (THIS_ LPCWSTR, IBaseFilter **); + STDMETHOD(ConnectDirect) (THIS_ IPin *, IPin *, const AM_MEDIA_TYPE *); + STDMETHOD(Reconnect) (THIS_ IPin *); + STDMETHOD(Disconnect) (THIS_ IPin *); + STDMETHOD(SetDefaultSyncSource) (IGraphBuilder *); + STDMETHOD(Connect) (THIS_ IPin *, IPin *); + STDMETHOD(Render) (THIS_ IPin *); + STDMETHOD(RenderFile) (THIS_ LPCWSTR, LPCWSTR); + STDMETHOD(AddSourceFilter) (THIS_ LPCWSTR, LPCWSTR, IBaseFilter **); + STDMETHOD(SetLogFile) (THIS_ DWORD_PTR); + STDMETHOD(Abort) (IGraphBuilder *); + STDMETHOD(ShouldOperationContinue) (IGraphBuilder *); +}; + + +#undef INTERFACE +#define INTERFACE ICaptureGraphBuilder2 +DECLARE_INTERFACE(ICaptureGraphBuilder2) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(SetFiltergraph) (THIS_ IGraphBuilder *); + STDMETHOD(GetFiltergraph) (THIS_ IGraphBuilder **); + STDMETHOD(SetOutputFileName) (THIS_ const GUID *, LPCOLESTR, + IBaseFilter **, LPFILESINKFILTER *); + STDMETHOD(FindInterface) (THIS_ const GUID *, const GUID *, + IBaseFilter *, REFIID, void **); + STDMETHOD(RenderStream) (THIS_ const GUID *, const GUID *, IUnknown *, + IBaseFilter *, IBaseFilter *); + STDMETHOD(ControlStream) (THIS_ const GUID *, const GUID *, + IBaseFilter *, REFERENCE_TIME *, + REFERENCE_TIME *, WORD, WORD); + STDMETHOD(AllocCapFile) (THIS_ LPCOLESTR, DWORDLONG); + STDMETHOD(CopyCaptureFile) (THIS_ LPOLESTR, LPOLESTR, int, + LPAMCOPYCAPTUREFILEPROGRESS); + STDMETHOD(FindPin) (THIS_ IUnknown *, PIN_DIRECTION, const GUID *, + const GUID *, BOOL, int, IPin **); +}; + +#undef INTERFACE +#define INTERFACE ICreateDevEnum +DECLARE_INTERFACE(ICreateDevEnum) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(CreateClassEnumerator) (THIS_ REFCLSID, IEnumMoniker **, + DWORD); +}; + +#undef INTERFACE +#define INTERFACE IAMCrossbar +DECLARE_INTERFACE(IAMCrossbar) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(get_PinCounts) (THIS_ long *, long *); + STDMETHOD(CanRoute) (THIS_ long, long); + STDMETHOD(Route) (THIS_ long, long); + STDMETHOD(get_IsRoutedTo) (THIS_ long, long *); + STDMETHOD(get_CrossbarPinInfo) (THIS_ BOOL, long, long *, long *); +}; + +#undef INTERFACE +#define INTERFACE IPropertyBag +DECLARE_INTERFACE(IPropertyBag) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(Read) (THIS_ LPCOLESTR, LPVARIANT, LPERRORLOG); + STDMETHOD(Write) (THIS_ LPCOLESTR, LPVARIANT); +}; + +#undef INTERFACE +#define INTERFACE IAMStreamConfig +DECLARE_INTERFACE(IAMStreamConfig) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + HRESULT(STDMETHODCALLTYPE * SetFormat) (THIS_ AM_MEDIA_TYPE *); + HRESULT(STDMETHODCALLTYPE * GetFormat) (THIS_ AM_MEDIA_TYPE **); + HRESULT(STDMETHODCALLTYPE * GetNumberOfCapabilities) (THIS_ int *,int *); + HRESULT(STDMETHODCALLTYPE * GetStreamCaps) (THIS_ int,AM_MEDIA_TYPE **, BYTE *); +}; + +#undef INTERFACE +#define INTERFACE IAMVideoProcAmp +DECLARE_INTERFACE(IAMVideoProcAmp) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(GetRange) (THIS_ long, long *, long *, long *, long *,long *); + STDMETHOD(Set) (THIS_ long, long, long); + STDMETHOD(Get) (THIS_ long, long *, long *); +}; + +#undef INTERFACE +#define INTERFACE IKsPropertySet +DECLARE_INTERFACE(IKsPropertySet) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + HRESULT(STDMETHODCALLTYPE * Set) (THIS_ REFGUID, DWORD, LPVOID, DWORD,LPVOID, DWORD); + HRESULT(STDMETHODCALLTYPE * Get) (THIS_ REFGUID, DWORD, LPVOID, DWORD,LPVOID, DWORD, DWORD *); + HRESULT(STDMETHODCALLTYPE * QuerySupported) (THIS_ REFGUID, DWORD,DWORD *); +}; + +#undef INTERFACE +#define INTERFACE IAMAnalogVideoDecoder +DECLARE_INTERFACE(IAMAnalogVideoDecoder) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(get_AvailableTVFormats) (THIS_ long *); + STDMETHOD(put_TVFormat) (THIS_ long); + STDMETHOD(get_TVFormat) (THIS_ long *); + STDMETHOD(get_HorizontalLocked) (THIS_ long *); + STDMETHOD(put_VCRHorizontalLocking) (THIS_ long); + STDMETHOD(get_VCRHorizontalLocking) (THIS_ long *); + STDMETHOD(get_NumberOfLines) (THIS_ long *); + STDMETHOD(put_OutputEnable) (THIS_ long); + STDMETHOD(get_OutputEnable) (THIS_ long *); +}; + +#undef INTERFACE +#define INTERFACE IAMTVAudio +DECLARE_INTERFACE(IAMTVAudio) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(GetHardwareSupportedTVAudioModes) (THIS_ long *); + STDMETHOD(GetAvailableTVAudioModes) (THIS_ long *); + STDMETHOD(get_TVAudioMode) (THIS_ long *); + STDMETHOD(put_TVAudioMode) (THIS_ long); + STDMETHOD(RegisterNotificationCallBack) (THIS_ LPAMTUNERNOTIFICATION, + long); + STDMETHOD(UnRegisterNotificationCallBack) (THIS_ + LPAMTUNERNOTIFICATION); +}; + + +#undef INTERFACE +#define INTERFACE ISampleGrabberCB +DECLARE_INTERFACE(ISampleGrabberCB) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(SampleCB) (THIS_ double, LPMEDIASAMPLE); + STDMETHOD(BufferCB) (THIS_ double, BYTE *, long); +}; + +#undef INTERFACE +#define INTERFACE ISampleGrabber +DECLARE_INTERFACE(ISampleGrabber) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(SetOneShot) (THIS_ BOOL); + STDMETHOD(SetMediaType) (THIS_ const AM_MEDIA_TYPE *); + STDMETHOD(GetConnectedMediaType) (THIS_ AM_MEDIA_TYPE *); + STDMETHOD(SetBufferSamples) (THIS_ BOOL); + STDMETHOD(GetCurrentBuffer) (THIS_ long *, long *); + STDMETHOD(GetCurrentSample) (THIS_ LPMEDIASAMPLE *); + STDMETHOD(SetCallback) (THIS_ ISampleGrabberCB *, long); +}; + +#undef INTERFACE +#define INTERFACE IFilterGraph +DECLARE_INTERFACE(IFilterGraph) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(AddFilter) (THIS_ LPBASEFILTER, LPCWSTR); + STDMETHOD(RemoveFilter) (THIS_ LPBASEFILTER); + STDMETHOD(EnumFilters) (THIS_ LPENUMFILTERS *); + STDMETHOD(FindFilterByName) (THIS_ LPCWSTR, LPBASEFILTER *); + STDMETHOD(ConnectDirect) (THIS_ IPin *, IPin *, const AM_MEDIA_TYPE *); + STDMETHOD(Reconnect) (THIS_ LPPIN); + STDMETHOD(Disconnect) (THIS_ LPPIN); + STDMETHOD(SetDefaultSyncSource) (THIS); +}; + +#undef INTERFACE +#define INTERFACE IAMAudioInputMixer +DECLARE_INTERFACE(IAMAudioInputMixer) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(put_Enable) (THIS_ BOOL); + STDMETHOD(get_Enable) (THIS_ BOOL *); + STDMETHOD(put_Mono) (THIS_ BOOL); + STDMETHOD(get_Mono) (THIS_ BOOL *); + STDMETHOD(put_MixLevel) (THIS_ double); + STDMETHOD(get_MixLevel) (THIS_ double *); + STDMETHOD(put_Pan) (THIS_ double); + STDMETHOD(get_Pan) (THIS_ double *); + STDMETHOD(put_Loudness) (THIS_ BOOL); + STDMETHOD(get_Loudness) (THIS_ BOOL *); + STDMETHOD(put_Treble) (THIS_ double); + STDMETHOD(get_Treble) (THIS_ double *); + STDMETHOD(get_TrebleRange) (THIS_ double *); + STDMETHOD(put_Bass) (THIS_ double); + STDMETHOD(get_Bass) (THIS_ double *); + STDMETHOD(get_BassRange) (THIS_ double *); +}; + + +#undef INTERFACE +#define INTERFACE IMediaSample +DECLARE_INTERFACE(IMediaSample) +{ + STDMETHOD(QueryInterface) (THIS_ const GUID *, void **); + STDMETHOD_(long, AddRef) (THIS); + STDMETHOD_(long, Release) (THIS); + STDMETHOD(GetPointer )(THIS_ unsigned char** ); + STDMETHOD_(LONG,GetSize )(THIS); + STDMETHOD(GetTime )(THIS_ REFERENCE_TIME* ,REFERENCE_TIME* ); + STDMETHOD(SetTime )(THIS_ REFERENCE_TIME* ,REFERENCE_TIME* ); + STDMETHOD(IsSyncPoint )(THIS); + STDMETHOD(SetSyncPoint )(THIS_ long ); + STDMETHOD(IsPreroll )(THIS); + STDMETHOD(SetPreroll )(THIS_ long ); + STDMETHOD_(LONG,GetActualDataLength)(THIS); + STDMETHOD(SetActualDataLength )(THIS_ long ); + STDMETHOD(GetMediaType )(THIS_ AM_MEDIA_TYPE** ); + STDMETHOD(SetMediaType )(THIS_ AM_MEDIA_TYPE* ); + STDMETHOD(IsDiscontinuity )(THIS); + STDMETHOD(SetDiscontinuity )(THIS_ long ); + STDMETHOD(GetMediaTime )(THIS_ long long* ,long long* ); + STDMETHOD(SetMediaTime )(THIS_ long long* ,long long* ); +}; + + +#undef INTERFACE +#define INTERFACE IAMBufferNegotiation +DECLARE_INTERFACE(IAMBufferNegotiation) +{ + STDMETHOD(QueryInterface )(THIS_ REFIID ,void **); + STDMETHOD_(ULONG,AddRef )(THIS); + STDMETHOD_(ULONG,Release )(THIS); + STDMETHOD(SuggestAllocatorProperties )(THIS_ const ALLOCATOR_PROPERTIES *); + STDMETHOD(GetAllocatorProperties )(THIS_ ALLOCATOR_PROPERTIES *); +}; + + +#undef INTERFACE +#define INTERFACE IVideoWindow +DECLARE_INTERFACE(IVideoWindow) +{ + STDMETHOD(QueryInterface )(THIS_ REFIID ,void **); + STDMETHOD_(ULONG,AddRef )(THIS); + STDMETHOD_(ULONG,Release )(THIS); + STDMETHOD(GetTypeInfoCount) (THIS_ UINT * ); + STDMETHOD(GetTypeInfo) (THIS_ UINT ,LCID , ITypeInfo ** ); + STDMETHOD(GetIDsOfNames) (THIS_ REFIID ,LPOLESTR * , UINT ,LCID , DISPID * ); + STDMETHOD(Invoke) (THIS_ DISPID ,REFIID , LCID , WORD ,void *, VARIANT * ,EXCEPINFO * , UINT * ); + STDMETHOD(put_Caption) (THIS_ BSTR ); + STDMETHOD(get_Caption) (THIS_ BSTR * ); + STDMETHOD(put_WindowStyle) (THIS_ long ); + STDMETHOD(get_WindowStyle) (THIS_ long *); + STDMETHOD(put_WindowStyleEx) (THIS_ long ); + STDMETHOD(get_WindowStyleEx) (THIS_ long *); + STDMETHOD(put_AutoShow) (THIS_ long ); + STDMETHOD(get_AutoShow) (THIS_ long *); + STDMETHOD(put_WindowState) (THIS_ long ); + STDMETHOD(get_WindowState) (THIS_ long *); + STDMETHOD(put_BackgroundPalette) (THIS_ long ); + STDMETHOD(get_BackgroundPalette) (THIS_ long *); + STDMETHOD(put_Visible) (THIS_ long ); + STDMETHOD(get_Visible) (THIS_ long *); + STDMETHOD(put_Left) (THIS_ long ); + STDMETHOD(get_Left) (THIS_ long *); + STDMETHOD(put_Width) (THIS_ long ); + STDMETHOD(get_Width) (THIS_ long *); + STDMETHOD(put_Top) (THIS_ long ); + STDMETHOD(get_Top) (THIS_ long *); + STDMETHOD(put_Height) (THIS_ long ); + STDMETHOD(get_Height) (THIS_ long *); + STDMETHOD(put_Owner) (THIS_ OAHWND ); + STDMETHOD(get_Owner) (THIS_ OAHWND * ); + STDMETHOD(put_MessageDrain) (THIS_ OAHWND ); + STDMETHOD(get_MessageDrain) (THIS_ OAHWND * ); + STDMETHOD(get_BorderColor) (THIS_ long *); + STDMETHOD(put_BorderColor) (THIS_ long ); + STDMETHOD(get_FullScreenMode) (THIS_ long *); + STDMETHOD(put_FullScreenMode) (THIS_ long ); + STDMETHOD(SetWindowForeground) (THIS_ long ); + STDMETHOD(NotifyOwnerMessage) (THIS_ OAHWND ,long , LONG_PTR ,LONG_PTR ); + STDMETHOD(SetWindowPosition) (THIS_ long ,long , long ,long ); + STDMETHOD(GetWindowPosition) (THIS_ long *,long *, long *,long *); + STDMETHOD(GetMinIdealImageSize) (THIS_ long *, long *); + STDMETHOD(GetMaxIdealImageSize) (THIS_ long *, long *); + STDMETHOD(GetRestorePosition) (THIS_ long *,long *, long *,long *); + STDMETHOD(HideCursor) (THIS_ long ); + STDMETHOD(IsCursorHidden) (THIS_ long *); +}; + +DECLARE_ENUMERATOR_(IEnumFilters, LPBASEFILTER); +DECLARE_ENUMERATOR_(IEnumPins, LPPIN); +DECLARE_ENUMERATOR_(IEnumMediaTypes, AM_MEDIA_TYPE *); + +#define OLE_CALL(p,method) (p)->lpVtbl->method(p) +#ifdef __GNUC__ +#define OLE_CALL_ARGS(p, method, a1, args...) (p)->lpVtbl->method(p, a1, ##args) +#else +#define OLE_CALL_ARGS(p, method, ...) (p)->lpVtbl->method(p, __VA_ARGS__) +#endif +#define OLE_RELEASE_SAFE(p) if(p){ OLE_CALL((IUnknown*)p,Release); p=NULL;} +#define OLE_QUERYINTERFACE(p,iface,ptr) OLE_CALL_ARGS((IUnknown*)p,QueryInterface,&iface,(void*)&ptr) + +#endif // TVI_DSHOW_H_