[MPlayer-dev-eng] PVR Channel Navigation (3rd rev)

Sven Gothel sgothel at jausoft.com
Sun Apr 29 13:23:27 CEST 2007


Thanks to Reimar 
for his review efforts.

Index: mplayer/stream/stream_pvr.c
===================================================================
--- mplayer/stream/stream_pvr.c	(revision 23154)
+++ mplayer/stream/stream_pvr.c	(working copy)
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -40,8 +41,12 @@
 #include "help_mp.h"
 
 #include "stream.h"
-#include "tv.h"
+#include "pvr.h"
 
+#include "frequencies.h"
+
+#include "libavutil/common.h"
+
 #define PVR_DEFAULT_DEVICE "/dev/video0"
 #define PVR_MAX_CONTROLS 10
 
@@ -79,6 +84,23 @@
 int pvr_param_bitrate_peak = 0;
 char *pvr_param_stream_type = NULL;
 
+#define STATIONNAMESZ   256
+
+struct STATIONELEM {
+    char  name[8];
+    int   freq;
+    char  station[STATIONNAMESZ];
+    int   enabled;
+};
+
+struct STATIONLIST {
+    char                name[STATIONNAMESZ];
+    struct STATIONELEM *list;
+    int                 total;   /* total   number */
+    int                 used;    /* used    number */
+    int                 enabled; /* enabled number */
+};
+
 struct pvr_t {
   int dev_fd;
   char *video_dev;
@@ -93,7 +115,10 @@
   int saturation;
   int width;
   int height;
-  char *freq;
+  int freq;
+  int chanidx;
+  int chanidx_last;
+  struct STATIONLIST stationlist;
 
   /* encoder params */
   int aspect;
@@ -126,7 +151,14 @@
   pvr->saturation = 0;
   pvr->width = -1;
   pvr->height = -1;
-  pvr->freq = NULL;
+  pvr->freq = -1;
+  pvr->chanidx  = -1;
+  pvr->chanidx_last  = -1;
+  pvr->stationlist.name[0] = '\0';
+  pvr->stationlist.list    = NULL;
+  pvr->stationlist.total   = 0;
+  pvr->stationlist.used    = 0;
+  pvr->stationlist.enabled = 0;
 
   /* set default encoding settings
    * may be overlapped by user parameters
@@ -158,8 +190,10 @@
   
   if (pvr->video_dev)
     free (pvr->video_dev);
-  if (pvr->freq)
-    free (pvr->freq);
+
+  if ( NULL!= pvr->stationlist.list )
+    free(pvr->stationlist.list);
+
   free (pvr);
 }
 
@@ -489,14 +523,643 @@
   return 0;
 }
 
+/* Channel/Frequency layer */
+
+/**
+ * Copy Constructor for stationlist
+ *
+ * @see parse_setup_stationlist
+ */
+static int
+copycreate_stationlist(struct pvr_t *pvr, int num)
+{
+  int i;
+
+  if ( chantab<0 || !pvr )
+    return -1;
+
+  num = FFMAX(num, chanlists[chantab].count);
+
+  if ( pvr->stationlist.list != NULL )
+  {
+    free ( pvr->stationlist.list );
+    pvr->stationlist.list = NULL;
+  }
+  pvr->stationlist.total= 0;
+  pvr->stationlist.enabled=0;
+  pvr->stationlist.used=0;
+  pvr->stationlist.list = calloc( num, sizeof(struct STATIONELEM) );
+
+  if ( pvr->stationlist.list == NULL )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR,
+            "%s No memory allocated for station list, giving up\n", LOG_LEVEL_V4L2);
+    return -1;
+  }
+
+  /* transport the channel list data 
+   * to our extented struct
+   */
+  pvr->stationlist.total = num;
+  strlcpy(pvr->stationlist.name, chanlists[chantab].name, STATIONNAMESZ);
+
+  for ( i=0; i<chanlists[chantab].count; i++ )
+  {
+    pvr->stationlist.list[i].station[0]= '\0' ; /* no station name yet */
+    strlcpy(pvr->stationlist.list[i].name, chanlists[chantab].list[i].name, STATIONNAMESZ);
+    pvr->stationlist.list[i].freq = chanlists[chantab].list[i].freq;
+    pvr->stationlist.list[i].enabled= 1 ; /* default enabled */
+    pvr->stationlist.enabled++;
+    pvr->stationlist.used++;
+  }
+
+  return 0;
+}
+
+static int
+print_all_stations(struct pvr_t *pvr)
+{
+  int i;
+
+  if (!pvr || !pvr->stationlist.list) 
+    return -1;
+
+  for (i = 0; i<pvr->stationlist.total ; i++)
+  {
+      mp_msg (MSGT_OPEN, MSGL_INFO,
+              "%s %3d: [%c] channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+              i, (pvr->stationlist.list[i].enabled)?'X':' ',
+              pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+              pvr->stationlist.list[i].station);
+  } 
+  return 0;
+}
+
+/**
+ * Disables all stations
+ *
+ * @see parse_setup_stationlist
+ */
+static int
+disable_all_stations(struct pvr_t *pvr)
+{
+  int i;
+
+  if (!pvr || !pvr->stationlist.list) 
+    return -1;
+
+  for (i = 0; i<pvr->stationlist.total ; i++)
+  {
+    pvr->stationlist.list[i].enabled= 0;
+  }
+  pvr->stationlist.enabled=0;
+  return 0;
+}
+
+/**
+ * Update or add a station
+ *
+ * @see parse_setup_stationlist
+ */
+static int
+set_station(struct pvr_t *pvr, const char *station, const char *channel, int freq)
+{
+  int i;
+
+  if (!pvr || !pvr->stationlist.list) 
+    return -1;
+
+  if ( 0>=pvr->stationlist.total || ( !channel && !freq ) )
+    return -1;
+
+  /* select channel */
+  for (i = 0; i<pvr->stationlist.used ; i++)
+  {
+    if (channel && !strcasecmp(pvr->stationlist.list[i].name, channel))
+    {
+        break; /* found existing channel entry */
+    }
+    if (freq>0 && pvr->stationlist.list[i].freq==freq)
+    {
+        break; /* found existing frequency entry */
+    }
+  }
+
+  if (i < pvr->stationlist.used)
+  {
+    /**
+     * found an existing entry,
+     * which is about to change with the user data.
+     * it is also enabled ..
+     */
+    if ( ! pvr->stationlist.list[i].enabled )
+    {
+        pvr->stationlist.list[i].enabled = 1;
+        pvr->stationlist.enabled++;
+    }
+    if ( station )
+    {
+        strlcpy(pvr->stationlist.list[i].station, station, STATIONNAMESZ);
+    } else if ( channel ) {
+        strlcpy(pvr->stationlist.list[i].station, channel, STATIONNAMESZ);
+    } else {
+        snprintf(pvr->stationlist.list[i].station, STATIONNAMESZ, "F %d", freq);
+    }
+    mp_msg (MSGT_OPEN, MSGL_DBG2,
+            "%s Set user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+            pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+            pvr->stationlist.list[i].station);
+    return 0;
+  }
+
+  /* from here on, we have to create a new entry,
+   * frequency is mandatory
+   **/
+  if ( 0>=freq )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR,
+          "%s Cannot add new station/channel without frequency\n", LOG_LEVEL_V4L2);
+    return -1;
+  }
+
+  if (i >= pvr->stationlist.total)
+  {
+      assert(i == pvr->stationlist.used);
+      assert(i == pvr->stationlist.total);
+
+      /**
+       * we have to extend the stationlist about 
+       * an arbitrary size, even though this path is not performance critical
+       */
+      pvr->stationlist.total += 10;
+      pvr->stationlist.list   = realloc( pvr->stationlist.list, pvr->stationlist.total * sizeof(struct STATIONELEM) );
+
+      if ( !pvr->stationlist.list )
+      {
+        mp_msg (MSGT_OPEN, MSGL_ERR,
+                "%s No memory allocated for station list, giving up\n", LOG_LEVEL_V4L2);
+        return -1;
+      }
+      /* clear the new space ..*/
+      memset( &(pvr->stationlist.list[pvr->stationlist.used]), 0, (pvr->stationlist.total-pvr->stationlist.used)*sizeof(struct STATIONELEM));
+  }
+
+  /**
+   * here we go, our actual new entry
+   */
+  pvr->stationlist.used++;
+  pvr->stationlist.list[i].enabled = 1;
+  pvr->stationlist.enabled++;
+
+  if ( station )
+  {
+      strlcpy(pvr->stationlist.list[i].station, station, STATIONNAMESZ);
+  } 
+  if ( channel ) {
+      strlcpy(pvr->stationlist.list[i].name, channel, STATIONNAMESZ);
+  } else {
+      snprintf(pvr->stationlist.list[i].name, STATIONNAMESZ, "F %d", freq);
+  }
+  pvr->stationlist.list[i].freq = freq;
+
+  mp_msg (MSGT_OPEN, MSGL_DBG2,
+        "%s Add user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+        pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+        pvr->stationlist.list[i].station);
+
+  return 0;
+}
+
+/**
+ * Here we set our stationlist, as follow
+ *  - choose the frequency channel table, e.g. ntsc-cable
+ *  - create our stationlist, same element size as the channellist
+ *  - copy the channellist content to our stationlist
+ *  - IF the user provides his channel-mapping, THEN:
+ *    - disable all stations
+ *    - update and/or create entries in the stationlist and enable them
+ */
+static int
+parse_setup_stationlist(struct pvr_t *pvr)
+{
+  int i;
+
+  if (!pvr) 
+    return -1;
+
+  /**
+   * Create our station/channel list
+   */
+  if ( tv_param_chanlist )
+  {
+    /* select channel list */
+    for (i = 0; chanlists[i].name != NULL; i++)
+    {
+      if (!strcasecmp(chanlists[i].name, tv_param_chanlist))
+      {
+          chantab = i;
+          break;
+      }
+    }
+    if ( chanlists[i].name == NULL )
+    {
+        mp_msg (MSGT_OPEN, MSGL_ERR,
+                "%s unable to find channel list %s, using default %s\n", LOG_LEVEL_V4L2, 
+                tv_param_chanlist, chanlists[chantab].name);
+    } else {
+        mp_msg (MSGT_OPEN, MSGL_INFO,
+                "%s select channel list %s, entries %d\n", LOG_LEVEL_V4L2, 
+                chanlists[chantab].name, chanlists[chantab].count);
+    }
+  }
+
+  if(0>chantab)
+  {
+    mp_msg (MSGT_OPEN, MSGL_FATAL,
+            "%s No channel list selected, giving up\n", LOG_LEVEL_V4L2);
+    return -1;
+  }
+
+  if ( copycreate_stationlist(pvr, -1) < 0 )
+  {
+    mp_msg (MSGT_OPEN, MSGL_FATAL,
+            "%s No memory allocated for station list, giving up\n", LOG_LEVEL_V4L2);
+    return -1;
+  }
+
+  /* Handle user channel mappings */
+  if (tv_param_channels) 
+  {
+    char channel[STATIONNAMESZ];
+    char station[STATIONNAMESZ];
+    char** channels = tv_param_channels;
+
+    (void) disable_all_stations(pvr);
+
+    while (*channels) 
+    {
+        char* tmp = *(channels++);
+        char* sep = strchr(tmp,'-');
+        int  freq = -1;
+        int i;
+
+        if (!sep) continue; // Wrong syntax, but mplayer should not crash
+
+        strlcpy(station, sep + 1, STATIONNAMESZ);
+
+        sep[0] = '\0';
+        strlcpy(channel, tmp, STATIONNAMESZ);
+
+        while ((sep=strchr(station, '_')))
+        {
+            sep[0] = ' ';
+        }
+
+        // if channel number is a number and larger than 1000 threat it as frequency
+        // tmp still contain pointer to null-terminated string with channel number here
+        if (atoi(channel)>1000)
+        { 
+            freq=atoi(channel);
+        }
+        if ( set_station(pvr, station, (freq<=0)?channel:NULL, freq) < 0 )
+        {
+            mp_msg (MSGT_OPEN, MSGL_ERR,
+                    "%s Unable to set user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+                    channel, freq, station);
+        }
+    }
+  }
+  (void) print_all_stations(pvr);
+}
+
+static int
+get_v4l2_freq (struct pvr_t *pvr)
+{
+  int freq;
+  struct v4l2_frequency vf;
+  struct v4l2_tuner vt;
+
+  if (!pvr)
+    return -1;
+  
+  if (pvr->dev_fd < 0)
+    return -1;
+
+  memset(&vt, 0, sizeof(vt));
+  memset(&vf, 0, sizeof(vf));
+
+  if ( ioctl(pvr->dev_fd, VIDIOC_G_TUNER, &vt) < 0 )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't set tuner (%s).\n",
+            LOG_LEVEL_V4L2, strerror (errno));
+    return -1;
+  }
+
+  vf.type = 0;
+  if ( ioctl(pvr->dev_fd, VIDIOC_G_FREQUENCY, &vf) < 0 )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't get frequency %d.\n",
+            LOG_LEVEL_V4L2, errno);
+    return -1;
+  }
+  freq = vf.frequency;
+  if ( !(vt.capability & V4L2_TUNER_CAP_LOW) )
+      freq *= 1000;
+  freq /= 16;
+
+  return freq;
+}
+
+static int
+set_v4l2_freq (struct pvr_t *pvr)
+{
+  struct v4l2_frequency vf;
+  struct v4l2_tuner vt;
+
+  if (!pvr)
+    return -1;
+  
+  if (pvr->freq>0)
+  {
+    if ( get_v4l2_freq (pvr) == pvr->freq )
+    {
+        mp_msg (MSGT_OPEN, MSGL_STATUS,
+                "%s Frequency %d allready set.\n", LOG_LEVEL_V4L2, pvr->freq);
+        return 0;
+    }
+  }
+
+  if (pvr->dev_fd < 0)
+    return -1;
+
+  if (0>=pvr->freq)
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR,
+            "%s Frequency invalid %d !!!\n", LOG_LEVEL_V4L2, pvr->freq);
+    return -1;
+  }
+
+  memset(&vf, 0, sizeof(vf));
+  memset(&vt, 0, sizeof(vt));
+
+  if ( ioctl(pvr->dev_fd, VIDIOC_G_TUNER, &vt) < 0 )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't get tuner (%s).\n",
+            LOG_LEVEL_V4L2, strerror (errno));
+    return -1;
+  }
+
+  vf.tuner = 0;
+  vf.type = vt.type;
+  vf.frequency = pvr->freq * 16;
+  if ( !(vt.capability & V4L2_TUNER_CAP_LOW) )
+      vf.frequency /= 1000;
+
+  if ( ioctl(pvr->dev_fd, VIDIOC_S_FREQUENCY, &vf) < 0 )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't set frequency (%s).\n",
+            LOG_LEVEL_V4L2, strerror (errno));
+    return -1;
+  }
+
+  memset(&vt, 0, sizeof(vt));
+  if ( ioctl(pvr->dev_fd, VIDIOC_G_TUNER, &vt) < 0 )
+  {
+    mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't set tuner (%s).\n",
+            LOG_LEVEL_V4L2, strerror (errno));
+    return -1;
+  }
+
+  if (!vt.signal)
+  {
+      mp_msg (MSGT_OPEN, MSGL_ERR,
+              "%s NO SIGNAL at frequency %d (%d)\n", LOG_LEVEL_V4L2, pvr->freq, vf.frequency);
+      /* still return 0, since this is 'just informal' */
+  } else {
+      mp_msg (MSGT_OPEN, MSGL_STATUS,
+              "%s Got signal at frequency %d (%d)\n", LOG_LEVEL_V4L2, pvr->freq, vf.frequency);
+  }
+
+  return 0;
+}
+
+static int
+set_station_by_channeloffset(struct pvr_t *pvr, int offset, int v4lAction) 
+{
+  if (!pvr || !pvr->stationlist.list) 
+    return -1;
+
+  if ( pvr->stationlist.enabled>=abs(offset) )
+  {
+      int gotcha=0;
+      int chidx=pvr->chanidx + offset;
+
+      while(!gotcha)
+      {
+          if ( chidx  < 0 ) chidx += pvr->stationlist.used;
+          if ( chidx >= pvr->stationlist.used ) chidx -= pvr->stationlist.used;
+
+          mp_msg (MSGT_OPEN, MSGL_DBG2,
+                "%s Offset switch: current %d, enabled %d, offset %d -> %d\n", LOG_LEVEL_V4L2, 
+                pvr->chanidx, pvr->stationlist.enabled, offset, chidx);
+
+          if ( ! pvr->stationlist.list[chidx].enabled )
+          {
+              mp_msg (MSGT_OPEN, MSGL_DBG2,
+                    "%s Switch disabled to user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+                    pvr->stationlist.list[chidx].name, pvr->stationlist.list[chidx].freq,
+                    pvr->stationlist.list[chidx].station);
+            chidx += (offset>0)?1:-1;
+          } else {
+            gotcha=1;
+          }
+      }
+
+      pvr->freq     = pvr->stationlist.list[chidx].freq;
+      pvr->chanidx_last  = pvr->chanidx;
+      pvr->chanidx  = chidx;
+
+      mp_msg (MSGT_OPEN, MSGL_INFO,
+            "%s Switch to user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+            pvr->stationlist.list[chidx].name, pvr->stationlist.list[chidx].freq,
+            pvr->stationlist.list[chidx].station);
+
+      if(v4lAction)
+      {
+        return set_v4l2_freq (pvr);
+      }
+      return (pvr->freq>0)?0:-1;
+  }
+
+  mp_msg (MSGT_OPEN, MSGL_ERR,
+          "%s Ooops couldn't set freq by channel entry offset %d to current %d, enabled %d\n", LOG_LEVEL_V4L2, 
+          offset, pvr->chanidx, pvr->stationlist.enabled);
+  return -1;
+}
+
+static int
+set_station_by_channelname(struct pvr_t *pvr, const char *channel, int v4lAction)
+{
+  if (!pvr || !pvr->stationlist.list) 
+    return -1;
+
+  if ( pvr->stationlist.enabled>0 && channel )
+  {
+      int i;
+
+      /* select channel */
+      for (i = 0; i<pvr->stationlist.used ; i++)
+      {
+        if ( !strcasecmp(pvr->stationlist.list[i].name, channel))
+        {
+            if ( !pvr->stationlist.list[i].enabled )
+            {
+                mp_msg (MSGT_OPEN, MSGL_WARN,
+                    "%s Switch disabled to user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+                    pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+                    pvr->stationlist.list[i].station);
+
+                return -1;
+            }
+            pvr->freq     = pvr->stationlist.list[i].freq;
+            pvr->chanidx_last  = pvr->chanidx;
+            pvr->chanidx  = i;
+            break;
+        }
+      }
+
+      if (i >= pvr->stationlist.used)
+      {
+        mp_msg (MSGT_OPEN, MSGL_WARN,
+                "%s unable to find channel %s\n", LOG_LEVEL_V4L2, channel);
+      } else {
+        mp_msg (MSGT_OPEN, MSGL_INFO,
+            "%s Switch to user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+            pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+            pvr->stationlist.list[i].station);
+
+        if(v4lAction)
+        {
+            return set_v4l2_freq (pvr);
+        }
+        return (pvr->freq>0)?0:-1;
+      }
+  } 
+  return -1;
+}
+
+static int
+set_station_by_freq(struct pvr_t *pvr, int freq, int v4lAction)
+{
+  if (!pvr || !pvr->stationlist.list) 
+    return -1;
+
+  if ( pvr->stationlist.enabled>0 && freq )
+  {
+      int i;
+
+      /* select channel */
+      for (i = 0; i<pvr->stationlist.used ; i++)
+      {
+        if ( pvr->stationlist.list[i].freq == freq )
+        {
+            if ( !pvr->stationlist.list[i].enabled )
+            {
+                mp_msg (MSGT_OPEN, MSGL_WARN,
+                    "%s Switch disabled to user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+                    pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+                    pvr->stationlist.list[i].station);
+
+                return -1;
+            }
+            pvr->freq     = pvr->stationlist.list[i].freq;
+            pvr->chanidx_last  = pvr->chanidx;
+            pvr->chanidx  = i;
+            break;
+        }
+      }
+
+      if (i >= pvr->stationlist.used)
+      {
+        mp_msg (MSGT_OPEN, MSGL_ERR,
+                "%s unable to find frequency %d from list\n", LOG_LEVEL_V4L2, 
+                freq);
+      } else {
+        mp_msg (MSGT_OPEN, MSGL_INFO,
+            "%s Switch to user station channel: %8s - freq: %8d - station: %s\n", LOG_LEVEL_V4L2, 
+            pvr->stationlist.list[i].name, pvr->stationlist.list[i].freq,
+            pvr->stationlist.list[i].station);
+
+        if(v4lAction)
+        {
+            return set_v4l2_freq (pvr);
+        }
+        return (pvr->freq>0)?0:-1;
+      }
+  } 
+  return -1;
+  
+}
+
+static int 
+force_freq_offset (struct pvr_t *pvr, int offset)
+{
+  int freq;
+
+  if (!pvr) 
+    return -1;
+
+  freq = pvr->freq+offset;
+
+  if ( freq )
+  {
+     mp_msg (MSGT_OPEN, MSGL_INFO,
+            "%s Force Frequency %d + %d = %d \n", LOG_LEVEL_V4L2, 
+            pvr->freq, offset, freq);
+
+     pvr->freq=freq;
+
+     return set_v4l2_freq (pvr);
+  } 
+  return -1;
+  
+}
+
 /* V4L2 layer */
 
 static void
 parse_v4l2_tv_options (struct pvr_t *pvr)
 {
+  int i;
+
   if (!pvr)
     return;
   
+  /**
+   * Create our station/channel list
+   */
+  parse_setup_stationlist(pvr);
+
+  if (tv_param_channel)
+  {
+      if ( set_station_by_channelname(pvr, tv_param_channel, 0) >= 0 )
+      {
+          if ( tv_param_freq )
+          {
+            mp_msg (MSGT_OPEN, MSGL_HINT,
+                    "%s tv param freq %s is overwritten by channel setting freq %d\n", LOG_LEVEL_V4L2, 
+                    tv_param_freq, pvr->freq);
+          }
+      }
+  } 
+  
+  if (pvr->freq<0 && tv_param_freq )
+  {
+      mp_msg (MSGT_OPEN, MSGL_HINT,
+              "%s tv param freq %s is used directly\n", LOG_LEVEL_V4L2, tv_param_freq);
+      (void) set_station_by_freq(pvr, atoi(tv_param_freq),0);
+  }
+
   if (tv_param_device)
   {
     if (pvr->video_dev)
@@ -531,8 +1194,6 @@
   if (tv_param_height)
     pvr->height = tv_param_height;
 
-  if (tv_param_freq)
-    pvr->freq = strdup (tv_param_freq);
 }
 
 static int
@@ -694,27 +1355,23 @@
     }
   }
 
-  /* -tv freq=x */
-  if (pvr->freq)
+  if (pvr->freq<0 )
   {
-    struct v4l2_frequency vf;
-    vf.tuner = 0;
-    vf.type = 0;
-    vf.frequency = strtol (pvr->freq, 0L, 0);
-    mp_msg (MSGT_OPEN, MSGL_INFO,
-            "%s setting frequency to %d\n", LOG_LEVEL_V4L2, vf.frequency);
-    
-    if (ioctl (pvr->dev_fd, VIDIOC_S_FREQUENCY, &vf) < 0)
-    {
-      mp_msg (MSGT_OPEN, MSGL_ERR, "%s can't set frequency (%s).\n",
-              LOG_LEVEL_V4L2, strerror (errno));
-      return -1;
-    }
+      int freq = get_v4l2_freq (pvr);
+      if ( freq>0 )
+      {
+            (void) set_station_by_freq(pvr, freq, 0);
+      }
   }
 
+  if (pvr->freq>0)
+  {
+      return set_v4l2_freq (pvr) ;
+  }
   return 0;
 }
 
+
 static int
 v4l2_list_capabilities (struct pvr_t *pvr)
 {
@@ -916,13 +1573,14 @@
               "%s read (%d) bytes\n", LOG_LEVEL_PVR, pos);
     }
   }
-		
+        
   if (!pos)
     mp_msg (MSGT_OPEN, MSGL_ERR, "%s read %d bytes\n", LOG_LEVEL_PVR, pos);
 
   return pos;
 }
 
+
 static int
 pvr_stream_open (stream_t *stream, int mode, void *opts, int *file_format)
 {
@@ -933,9 +1591,13 @@
   if (mode != STREAM_READ)
     return STREAM_UNSUPORTED;
   
+  if (strlen (stream->url) > 6 && stream->url[6] != '\0')
+    tv_param_channel = strdup (stream->url + 6);
+  
   pvr = pvr_init ();
 
   parse_v4l2_tv_options (pvr);
+
   parse_encoder_options (pvr);
   
   /* open device */
@@ -1029,12 +1691,129 @@
   return STREAM_OK;
 }
 
+/* Public access */
+
+const char *
+pvr_get_current_stationname (stream_t *stream)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return NULL;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  if (NULL!=pvr->stationlist.list && pvr->stationlist.used>pvr->chanidx && pvr->chanidx>=0)
+  {
+        return pvr->stationlist.list[pvr->chanidx].station;
+  }
+  return NULL;
+}
+
+const char *
+pvr_get_current_channelname (stream_t *stream)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return NULL;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  if (NULL!=pvr->stationlist.list && pvr->stationlist.used>pvr->chanidx && pvr->chanidx>=0)
+  {
+        return pvr->stationlist.list[pvr->chanidx].name;
+  }
+  return NULL;
+}
+
+int pvr_get_current_frequency (stream_t *stream)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return -1;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  return pvr->freq;
+}
+
+int
+pvr_set_channel (stream_t *stream, const char * channel)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return -1;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  return set_station_by_channelname(pvr, channel, 1);
+}
+
+int
+pvr_set_lastchannel (stream_t *stream)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return -1;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  if (NULL!=pvr->stationlist.list && pvr->stationlist.used>pvr->chanidx_last && pvr->chanidx_last>=0)
+  {
+      return set_station_by_channelname(pvr, pvr->stationlist.list[pvr->chanidx_last].name, 1);
+  }
+  return -1;
+}
+
+int
+pvr_set_freq (stream_t *stream, int freq)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return -1;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  return set_station_by_freq(pvr, freq, 1);
+}
+
+int
+pvr_set_channel_offset(stream_t *stream, int offset)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return -1;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  return set_station_by_channeloffset(pvr, offset, 1);
+}
+
+int
+pvr_force_freq_offset(stream_t *stream, int offset)
+{
+  struct pvr_t *pvr;
+
+  if (!stream || stream->type != STREAMTYPE_PVR)
+    return -1;
+  
+  pvr = (struct pvr_t *) stream->priv;
+
+  return force_freq_offset(pvr, offset);
+}
+
 stream_info_t stream_info_pvr = {
   "V4L2 MPEG Input (a.k.a PVR)",
   "pvr",
   "Benjamin Zores",
   "",
-  pvr_stream_open, 			
+  pvr_stream_open,             
   { "pvr", NULL },
   NULL,
   1
Index: mplayer/stream/pvr.h
===================================================================
--- mplayer/stream/pvr.h	(revision 0)
+++ mplayer/stream/pvr.h	(revision 0)
@@ -0,0 +1,20 @@
+#ifndef PVR_H
+#define PVR_H
+
+#include "tv.h"
+
+int          pvr_get_current_frequency (stream_t *stream);
+const char * pvr_get_current_stationname (stream_t *stream);
+const char * pvr_get_current_channelname (stream_t *stream);
+
+int pvr_set_channel (stream_t *stream, const char * channel);
+
+int pvr_set_lastchannel (stream_t *stream);
+
+int pvr_set_freq (stream_t *stream, int freq);
+
+int pvr_set_channel_offset(stream_t *stream, int offset);
+
+int pvr_force_freq_offset(stream_t *stream, int offset);
+
+#endif /* PVR_H */
Index: mplayer/command.c
===================================================================
--- mplayer/command.c	(revision 23154)
+++ mplayer/command.c	(working copy)
@@ -32,6 +32,9 @@
 #ifdef USE_RADIO
 #include "stream/stream_radio.h"
 #endif
+#ifdef HAVE_PVR
+#include "stream/pvr.h"
+#endif
 #ifdef HAS_DVBIN_SUPPORT
 #include "stream/dvbin.h"
 #endif
@@ -1937,14 +1940,37 @@
 #ifdef USE_TV
 	case MP_CMD_TV_SET_FREQ:
 	    if (mpctx->file_format == DEMUXER_TYPE_TV)
-		tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
-			    cmd->args[0].v.f * 16.0);
+        {
+            tv_set_freq((tvi_handle_t *) (mpctx->demuxer->priv),
+                    cmd->args[0].v.f * 16.0);
+        } 
+#ifdef HAVE_PVR
+        else if ( mpctx->stream!=NULL && STREAMTYPE_PVR == mpctx->stream->type )
+        {
+            pvr_set_freq (mpctx->stream, ROUND(cmd->args[0].v.f));
+			set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
+                pvr_get_current_channelname(mpctx->stream),
+                pvr_get_current_stationname(mpctx->stream));
+        }
+#endif /* HAVE_PVR */
 	    break;
 
 	case MP_CMD_TV_STEP_FREQ:
 	    if (mpctx->file_format == DEMUXER_TYPE_TV)
-		tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
-			    cmd->args[0].v.f * 16.0);
+        {
+            tv_step_freq((tvi_handle_t *) (mpctx->demuxer->priv),
+                    cmd->args[0].v.f * 16.0);
+        }
+#ifdef HAVE_PVR
+        else if ( mpctx->stream!=NULL && STREAMTYPE_PVR == mpctx->stream->type )
+        {
+            pvr_force_freq_offset(mpctx->stream, 
+                ROUND(cmd->args[0].v.f));
+			set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: f %d",
+                pvr_get_current_channelname(mpctx->stream),
+                pvr_get_current_frequency(mpctx->stream));
+        }
+#endif /* HAVE_PVR */
 	    break;
 
 	case MP_CMD_TV_SET_NORM:
@@ -1971,6 +1997,15 @@
 			//vo_osd_changed(OSDTYPE_SUBTITLE);
 		    }
 		}
+#ifdef HAVE_PVR
+        else if ( mpctx->stream!=NULL && STREAMTYPE_PVR == mpctx->stream->type )
+        {
+            pvr_set_channel_offset(mpctx->stream, cmd->args[0].v.i);
+			set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
+                pvr_get_current_channelname(mpctx->stream),
+                pvr_get_current_stationname(mpctx->stream));
+        }
+#endif /* HAVE_PVR */
 	    }
 #ifdef HAS_DVBIN_SUPPORT
 	    if ((mpctx->stream->type == STREAMTYPE_DVB)
@@ -2004,6 +2039,15 @@
 		    //vo_osd_changed(OSDTYPE_SUBTITLE);
 		}
 	    }
+#ifdef HAVE_PVR
+        else if ( mpctx->stream!=NULL && STREAMTYPE_PVR == mpctx->stream->type )
+        {
+            pvr_set_channel(mpctx->stream, cmd->args[0].v.s);
+			set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
+                pvr_get_current_channelname(mpctx->stream),
+                pvr_get_current_stationname(mpctx->stream));
+        }
+#endif /* HAVE_PVR */
 	    break;
 
 #ifdef HAS_DVBIN_SUPPORT
@@ -2034,6 +2078,15 @@
 		    //vo_osd_changed(OSDTYPE_SUBTITLE);
 		}
 	    }
+#ifdef HAVE_PVR
+        else if ( mpctx->stream!=NULL && STREAMTYPE_PVR == mpctx->stream->type )
+        {
+            pvr_set_lastchannel(mpctx->stream);
+			set_osd_msg(OSD_MSG_TV_CHANNEL, 1, osd_duration, "%s: %s",
+                pvr_get_current_channelname(mpctx->stream),
+                pvr_get_current_stationname(mpctx->stream));
+        }
+#endif /* HAVE_PVR */
 	    break;
 
 	case MP_CMD_TV_STEP_NORM:
-------------- next part --------------
A non-text attachment was scrubbed...
Name: MPlayer-PVR_ChannelFeature-v03.diff
Type: text/x-diff
Size: 27949 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/attachments/20070429/9b2fd638/attachment.diff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/attachments/20070429/9b2fd638/attachment.pgp>


More information about the MPlayer-dev-eng mailing list