>From 45148641d9c743e1c591527f7cd1da6d9ef24c2f Mon Sep 17 00:00:00 2001
From: Manol Manolov <man40dev@gmail.com>
Date: Wed, 4 Aug 2010 10:59:44 -0400
Subject: [PATCH] Add frequency and video standard in dshow module
 The new options are dshow-tuner-frequency and dshow-tuner-standard

---
 modules/access/dshow/common.h  |  113 +++++++++++++++++++++++++++
 modules/access/dshow/dshow.cpp |  164 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 272 insertions(+), 5 deletions(-)

diff --git a/modules/access/dshow/common.h b/modules/access/dshow/common.h
index 6a3f2b5..d71fcbf 100644
--- a/modules/access/dshow/common.h
+++ b/modules/access/dshow/common.h
@@ -97,3 +97,116 @@ struct access_sys_t
     int            i_chroma;
     bool           b_chroma; /* Force a specific chroma on the dshow input */
 };
+
+
+#define INSTANCEDATA_OF_PROPERTY_PTR(x) ((PKSPROPERTY((x))) + 1)
+#define INSTANCEDATA_OF_PROPERTY_SIZE(x) (sizeof((x)) - sizeof(KSPROPERTY))
+
+/****************************************************************************
+ * The following should be in ks.h and ksmedia.h,
+ * but since they are not in the current version of Mingw32,
+ * they will be defined here. They are exact copy from Windows SDK.
+ ****************************************************************************/
+typedef enum  {
+        KS_AnalogVideo_None          = 0x00000000,
+        KS_AnalogVideo_NTSC_M        = 0x00000001,
+        KS_AnalogVideo_NTSC_M_J      = 0x00000002,
+        KS_AnalogVideo_NTSC_433      = 0x00000004,
+        KS_AnalogVideo_PAL_B         = 0x00000010,
+        KS_AnalogVideo_PAL_D         = 0x00000020,
+        KS_AnalogVideo_PAL_G         = 0x00000040,
+        KS_AnalogVideo_PAL_H         = 0x00000080,
+        KS_AnalogVideo_PAL_I         = 0x00000100,
+        KS_AnalogVideo_PAL_M         = 0x00000200,
+        KS_AnalogVideo_PAL_N         = 0x00000400,
+        KS_AnalogVideo_PAL_60        = 0x00000800,
+        KS_AnalogVideo_SECAM_B       = 0x00001000,
+        KS_AnalogVideo_SECAM_D       = 0x00002000,
+        KS_AnalogVideo_SECAM_G       = 0x00004000,
+        KS_AnalogVideo_SECAM_H       = 0x00008000,
+        KS_AnalogVideo_SECAM_K       = 0x00010000,
+        KS_AnalogVideo_SECAM_K1      = 0x00020000,
+        KS_AnalogVideo_SECAM_L       = 0x00040000,
+        KS_AnalogVideo_SECAM_L1      = 0x00080000,
+        KS_AnalogVideo_PAL_N_COMBO   = 0x00100000
+} KS_AnalogVideoStandard;
+
+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_SCAN_CAPS,         // R  -overall device capabilities for scanning
+    KSPROPERTY_TUNER_SCAN_STATUS,       // R  -status of scan
+    KSPROPERTY_TUNER_STANDARD_MODE,     // RW -autodetect mode for signal standard
+    KSPROPERTY_TUNER_NETWORKTYPE_SCAN_CAPS // R -network type specific tuner capabilities
+} KSPROPERTY_TUNER;
+
+// Describes how the device tunes.  Only one of these flags may be set
+// in KSPROPERTY_TUNER_MODE_CAPS_S.Strategy
+
+// Describe how the driver should attempt to tune:
+// EXACT:   just go to the frequency specified (no fine tuning)
+// FINE:    (slow) do an exhaustive search for the best signal
+// COARSE:  (fast) use larger frequency jumps to just determine if any signal
+
+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 {
+    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 struct {
+    union {
+#if defined( _KS_NO_ANONYMOUS_STRUCTURES_ )
+        struct _IDENTIFIER {
+#else
+        struct {
+#endif
+            GUID    Set;
+            ULONG   Id;
+            ULONG   Flags;
+        };
+        LONGLONG    Alignment;
+    };
+} KSIDENTIFIER, *PKSIDENTIFIER;
+
+typedef KSIDENTIFIER KSPROPERTY, *PKSPROPERTY;
+
+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  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  Standard;                    // KS_AnalogVideo_*
+} KSPROPERTY_TUNER_STANDARD_S, *PKSPROPERTY_TUNER_STANDARD_S;
diff --git a/modules/access/dshow/dshow.cpp b/modules/access/dshow/dshow.cpp
index d2ad8cf..2ebb720 100644
--- a/modules/access/dshow/dshow.cpp
+++ b/modules/access/dshow/dshow.cpp
@@ -136,6 +136,13 @@ static const char *const ppsz_amtuner_mode_text[] = { N_("Default"),
 #define CHANNEL_LONGTEXT N_( \
     "Set the TV channel the tuner will set to " \
     "(0 means default)." )
+#define TVFREQ_TEXT N_("Tuner Frequency")
+#define TVFREQ_LONGTEXT N_( \
+    "This overrides the channel. " \
+    "Measured in Hz." )
+#define STANDARD_TEXT N_( "Standard" )
+#define STANDARD_LONGTEXT N_( \
+    "Video standard (Default, SECAM_D, PAL_B, NTSC_M, etc...)." )
 #define COUNTRY_TEXT N_("Tuner country code")
 #define COUNTRY_LONGTEXT N_( \
     "Set the tuner country code that establishes the current " \
@@ -176,6 +183,31 @@ static const char *const ppsz_amtuner_mode_text[] = { N_("Default"),
 #define AUDIO_BITSPERSAMPLE_LONGTEXT N_( \
     "Select audio input format with the given bits/sample (if non 0)" )
 
+static const int i_standards_list[] =
+    {
+        KS_AnalogVideo_None,
+        KS_AnalogVideo_NTSC_M, KS_AnalogVideo_NTSC_M_J, KS_AnalogVideo_NTSC_433,
+        KS_AnalogVideo_PAL_B, KS_AnalogVideo_PAL_D, KS_AnalogVideo_PAL_G,
+        KS_AnalogVideo_PAL_H, KS_AnalogVideo_PAL_I, KS_AnalogVideo_PAL_M,
+        KS_AnalogVideo_PAL_N, KS_AnalogVideo_PAL_60,
+        KS_AnalogVideo_SECAM_B, KS_AnalogVideo_SECAM_D, KS_AnalogVideo_SECAM_G,
+        KS_AnalogVideo_SECAM_H, KS_AnalogVideo_SECAM_K, KS_AnalogVideo_SECAM_K1,
+        KS_AnalogVideo_SECAM_L, KS_AnalogVideo_SECAM_L1,
+        KS_AnalogVideo_PAL_N_COMBO
+    };
+static const char *const ppsz_standards_list_text[] =
+    {
+        N_("Default"),
+        "NTSC_M", "NTSC_M_J", "NTSC_443",
+        "PAL_B", "PAL_D", "PAL_G",
+        "PAL_H", "PAL_I", "PAL_M",
+        "PAL_N", "PAL_60",
+        "SECAM_B", "SECAM_D", "SECAM_G",
+        "SECAM_H", "SECAM_K", "SECAM_K1",
+        "SECAM_L", "SECAM_L1",
+        "PAL_N_COMBO"
+    };
+
 static int  CommonOpen ( vlc_object_t *, access_sys_t *, bool );
 static void CommonClose( vlc_object_t *, access_sys_t * );
 
@@ -218,9 +250,16 @@ vlc_module_begin ()
     add_integer( "dshow-tuner-channel", 0, NULL, CHANNEL_TEXT, CHANNEL_LONGTEXT,
                 true )
 
+    add_integer( "dshow-tuner-frequency", 0, NULL, TVFREQ_TEXT, TVFREQ_LONGTEXT,
+                true )
+
     add_integer( "dshow-tuner-country", 0, NULL, COUNTRY_TEXT, COUNTRY_LONGTEXT,
                 true )
 
+    add_integer( "dshow-tuner-standard", 0, NULL, STANDARD_TEXT, STANDARD_LONGTEXT,
+                false )
+        change_integer_list( i_standards_list, ppsz_standards_list_text, NULL )
+
     add_integer( "dshow-tuner-input", 0, NULL, TUNER_INPUT_TEXT,
                  TUNER_INPUT_LONGTEXT, true )
         change_integer_list( pi_tuner_input, ppsz_tuner_input_text, NULL )
@@ -260,6 +299,13 @@ vlc_module_begin ()
 
 vlc_module_end ()
 
+
+/*****************************************************************************
+ * DirectShow GUIDs.
+ * Easier to define them here as mingw doesn't provide them all.
+ *****************************************************************************/
+const GUID PROPSETID_TUNER = {0x6a2e0605, 0x28e4, 0x11d0, {0xa1, 0x8c, 0x00, 0xa0, 0xc9, 0x11, 0x89, 0x56}};
+
 /*****************************************************************************
  * DirectShow elementary stream descriptor
  *****************************************************************************/
@@ -426,6 +472,10 @@ static int CommonOpen( vlc_object_t *p_this, access_sys_t *p_sys,
     var_Create( p_this, "dshow-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
     var_Create( p_this, "dshow-tuner-channel",
                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Create( p_this, "dshow-tuner-frequency",
+                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+    var_Create( p_this, "dshow-tuner-standard",
+                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
     var_Create( p_this, "dshow-tuner-country",
                 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
     var_Create( p_this, "dshow-tuner-input",
@@ -2216,7 +2266,7 @@ static void ShowTunerProperties( vlc_object_t *p_this,
 static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph,
                          IBaseFilter *p_device_filter )
 {
-    int i_channel, i_country, i_input, i_amtuner_mode;
+    int i_channel, i_country, i_input, i_amtuner_mode, i_standard, i_frequency;
     long l_modes = 0;
     IAMTVTuner *p_TV;
     HRESULT hr;
@@ -2227,11 +2277,15 @@ static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph,
     i_country = var_GetInteger( p_this, "dshow-tuner-country" );
     i_input = var_GetInteger( p_this, "dshow-tuner-input" );
     i_amtuner_mode = var_GetInteger( p_this, "dshow-amtuner-mode" );
+    i_frequency = var_GetInteger( p_this, "dshow-tuner-frequency" );
+    i_standard =
+        i_standards_list[var_CreateGetInteger( p_this, "dshow-tuner-standard" )];
+
+    if( !i_channel && !i_frequency && !i_country && !i_input ) return; /* Nothing to do */
 
-    if( !i_channel && !i_country && !i_input ) return; /* Nothing to do */
+    msg_Dbg( p_this, "tuner config: channel %i, frequency %i, country %i, input type %i, standard %s",
+             i_channel, i_frequency, i_country, i_input, ppsz_standards_list_text[i_standard] );
 
-    msg_Dbg( p_this, "tuner config: channel %i, country %i, input type %i",
-             i_channel, i_country, i_input );
 
     hr = p_graph->FindInterface( &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved,
                                  p_device_filter, IID_IAMTVTuner,
@@ -2266,7 +2320,107 @@ static void ConfigTuner( vlc_object_t *p_this, ICaptureGraphBuilder2 *p_graph,
     else if( i_input == 2 ) p_TV->put_InputType( 0, TunerInputAntenna );
 
     p_TV->put_CountryCode( i_country );
-    p_TV->put_Channel( i_channel, AMTUNER_SUBCHAN_NO_TUNE,
+
+    if( i_frequency <= 0 ) p_TV->put_Channel( i_channel, AMTUNER_SUBCHAN_NO_TUNE,
                        AMTUNER_SUBCHAN_NO_TUNE );
+
+    if( i_frequency > 0 || i_standard > 0) {
+        IKsPropertySet *pKs = NULL;
+        DWORD dw_supported = 0;
+        KSPROPERTY_TUNER_MODE_CAPS_S ModeCaps;
+        KSPROPERTY_TUNER_FREQUENCY_S Frequency;
+        KSPROPERTY_TUNER_STANDARD_S Standard;
+
+        hr = p_TV->QueryInterface(IID_IKsPropertySet,(void **)&pKs);
+        if (FAILED(hr))
+        {
+            msg_Dbg( p_this, "Couldn't QI for IKsPropertySet" );
+            return;
+        }
+
+        memset(&ModeCaps,0,sizeof(KSPROPERTY_TUNER_MODE_CAPS_S));
+        memset(&Frequency,0,sizeof(KSPROPERTY_TUNER_FREQUENCY_S));
+        memset(&Standard,0,sizeof(KSPROPERTY_TUNER_STANDARD_S));
+        ModeCaps.Mode = AMTUNER_MODE_TV;
+
+        hr = pKs->QuerySupported(PROPSETID_TUNER,
+                KSPROPERTY_TUNER_MODE_CAPS,&dw_supported);
+        if(SUCCEEDED(hr) && dw_supported&KSPROPERTY_SUPPORT_GET)
+        {
+            DWORD cbBytes=0;
+            hr = pKs->Get(PROPSETID_TUNER,KSPROPERTY_TUNER_MODE_CAPS,
+                INSTANCEDATA_OF_PROPERTY_PTR(&ModeCaps),
+                INSTANCEDATA_OF_PROPERTY_SIZE(ModeCaps),
+                &ModeCaps,
+                sizeof(ModeCaps),
+                &cbBytes);
+        }
+        else
+        {
+            msg_Dbg( p_this, "KSPROPERTY_TUNER_MODE_CAPS not supported!" );
+            return;
+        }
+
+        msg_Dbg( p_this, "Frequency range supproted from %d to %d.", ModeCaps.MinFrequency, ModeCaps.MaxFrequency);
+        msg_Dbg( p_this, "Video standards supproted by the tuner: ");
+        for(int i = 0 ; i < ARRAY_SIZE(ppsz_standards_list_text); i++) {
+            if(ModeCaps.StandardsSupported & i_standards_list[i])
+                msg_Dbg( p_this, "%s, ", ppsz_standards_list_text[i]);
+        }
+
+        if(i_frequency > 0) {
+            Frequency.Frequency=i_frequency;
+            if(ModeCaps.Strategy==KS_TUNER_STRATEGY_DRIVER_TUNES)
+                Frequency.TuningFlags=KS_TUNER_TUNING_FINE;
+            else
+                Frequency.TuningFlags=KS_TUNER_TUNING_EXACT;
+
+            if(i_frequency>=ModeCaps.MinFrequency && i_frequency<=ModeCaps.MaxFrequency)
+            {
+
+                hr = pKs->Set(PROPSETID_TUNER,
+                    KSPROPERTY_TUNER_FREQUENCY,
+                    INSTANCEDATA_OF_PROPERTY_PTR(&Frequency),
+                    INSTANCEDATA_OF_PROPERTY_SIZE(Frequency),
+                    &Frequency,
+                    sizeof(Frequency));
+                if(FAILED(hr))
+                {
+                    msg_Dbg( p_this, "Couldn't set KSPROPERTY_TUNER_FREQUENCY!" );
+                    return;
+                }
+            }
+            else
+            {
+                msg_Dbg( p_this, "Requested frequency exceeds the supported range!" );
+                return;
+            }
+        }
+
+        if(i_standard > 0) {
+            if(i_standard & ModeCaps.StandardsSupported )
+            {
+                Standard.Standard = i_standard;
+                hr = pKs->Set(PROPSETID_TUNER,
+                    KSPROPERTY_TUNER_STANDARD,
+                    INSTANCEDATA_OF_PROPERTY_PTR(&Standard),
+                    INSTANCEDATA_OF_PROPERTY_SIZE(Standard),
+                    &Standard,
+                    sizeof(Standard));
+                if(FAILED(hr))
+                {
+                    msg_Dbg( p_this, "Couldn't set KSPROPERTY_TUNER_STANDARD!" );
+                    return;
+                }
+            }
+            else
+            {
+                msg_Dbg( p_this, "Requested video standard is not supported by the tuner!" );
+                return;
+            }
+        }
+        pKs->Release();
+    }
+
     p_TV->Release();
 }
-- 
1.7.0.2.msysgit.0

