/* GStreamer DVB source
 * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali
 *                                         dot org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/**
 * SECTION:element-dvbsrc
 *
 * dvbsrc can be used to capture video from DVB cards, DVB-T, DVB-S or DVB-T.
 * 
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch dvbsrc modulation="QAM 64" trans-mode=8k bandwidth=8MHz frequency=514000000 code-rate-lp=AUTO code-rate-hp=2/3 guard=4  hierarchy=0 ! flutsdemux crc-check=false name=demux ! queue max-size-buffers=0 max-size-time=0 ! flumpeg2vdec ! xvimagesink sync demux. ! queue max-size-buffers=0 max-size-time=0 ! flump3dec ! alsasink sync
 * ]| Captures a full transport stream from dvb card 0 that is a DVB-T card at tuned frequency 514000000 with other parameters as seen in the 
 * pipeline and outputs the first tv program on the transport stream.
 * |[
 * gst-launch dvbsrc modulation="QAM 64" trans-mode=8k bandwidth=8 frequency=514000000 code-rate-lp=AUTO code-rate-hp=2/3 guard=4  hierarchy=0 pids=256:257 ! flutsdemux crc-check=false name=demux es-pids=256:257 ! queue max-size-buffers=0 max-size-time=0 ! flumpeg2vdec ! xvimagesink demux. ! queue max-size-buffers=0 max-size-time=0 ! flump3dec ! alsasink
 * ]| Captures a partial transport stream from dvb card 0 that is a DVB-T card for a program at tuned frequency 514000000 and pids of 256:257 with other parameters as seen in the pipeline and outputs the program with the pids 256 and 257.  
 * |[
 * gst-launch dvbsrc polarity="h" frequency=11302000 srate=27500 diseqc-src=0 pids=102:103 ! queue max-size-buffers=0 max-size-time=0 ! flumpeg2vdec ! xvimagesink demux. ! queue max-size-buffers=0 max-size-time=0 ! flump3dec ! alsasink
 * ]| Captures a partial transport stream from dvb card 0 that is a DVB-S card for a program at tuned frequency 11302000 Hz, symbol rate of 27500 kHz and pids of 256:257 and outputs the program with the pids 256 and 257.
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gstdvbsrc.h"
#include <gst/gst.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <error.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "_stdint.h"

#define _XOPEN_SOURCE 500
#include <unistd.h>

#include <linux/dvb/version.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>

#include <gst/gst-i18n-plugin.h>

GST_DEBUG_CATEGORY_STATIC (gstdvbsrc_debug);
#define GST_CAT_DEFAULT (gstdvbsrc_debug)

#define SLOF (11700*1000UL)
#define LOF1 (9750*1000UL)
#define LOF2 (10600*1000UL)


static GstElementDetails dvbsrc_details = {
  "DVB Source",
  "Source/Video",
  "Digital Video Broadcast Source",
  "P2P-VCR, C-Lab, University of Paderborn\n"
      "Zaheer Abbas Merali <zaheerabbas at merali dot org>"
};

/* Arguments */
enum
{
  ARG_0,
  ARG_DVBSRC_ADAPTER,
  ARG_DVBSRC_FRONTEND,
  ARG_DVBSRC_DISEQC_SRC,
  ARG_DVBSRC_FREQUENCY,
  ARG_DVBSRC_POLARITY,
  ARG_DVBSRC_PIDS,
  ARG_DVBSRC_SYM_RATE,
  ARG_DVBSRC_BANDWIDTH,
  ARG_DVBSRC_CODE_RATE_HP,
  ARG_DVBSRC_CODE_RATE_LP,
  ARG_DVBSRC_GUARD,
  ARG_DVBSRC_MODULATION,
  ARG_DVBSRC_TRANSMISSION_MODE,
  ARG_DVBSRC_HIERARCHY_INF,
  ARG_DVBSRC_TUNE,
  ARG_DVBSRC_INVERSION,
  ARG_DVBSRC_STATS_REPORTING_INTERVAL
};

static void gst_dvbsrc_output_frontend_stats (GstDvbSrc * src);

#define GST_TYPE_DVBSRC_CODE_RATE (gst_dvbsrc_code_rate_get_type ())
static GType
gst_dvbsrc_code_rate_get_type (void)
{
  static GType dvbsrc_code_rate_type = 0;
  static GEnumValue code_rate_types[] = {
    {FEC_NONE, "NONE", "NONE"},
    {FEC_1_2, "1/2", "1/2"},
    {FEC_2_3, "2/3", "2/3"},
    {FEC_3_4, "3/4", "3/4"},
    {FEC_4_5, "4/5", "4/5"},
    {FEC_5_6, "5/6", "5/6"},
    {FEC_6_7, "6/7", "6/7"},
    {FEC_7_8, "7/8", "7/8"},
    {FEC_8_9, "8/9", "8/9"},
    {FEC_AUTO, "AUTO", ""},
    {0, NULL, NULL},
  };

  if (!dvbsrc_code_rate_type) {
    dvbsrc_code_rate_type =
        g_enum_register_static ("GstDvbSrcCode_Rate", code_rate_types);
  }
  return dvbsrc_code_rate_type;
}

#define GST_TYPE_DVBSRC_MODULATION (gst_dvbsrc_modulation_get_type ())
static GType
gst_dvbsrc_modulation_get_type (void)
{
  static GType dvbsrc_modulation_type = 0;
  static GEnumValue modulation_types[] = {
    {QPSK, "QPSK", "QPSK"},
    {QAM_16, "QAM 16", "QAM 16"},
    {QAM_32, "QAM 32", "QAM 32"},
    {QAM_64, "QAM 64", "QAM 64"},
    {QAM_128, "QAM 128", "QAM 128"},
    {QAM_256, "QAM 256", "QAM 256"},
    {QAM_AUTO, "AUTO", "AUTO"},
    {VSB_8, "8VSB", "8VSB"},
    {VSB_16, "16VSB", "16VSB"},
    {0, NULL, NULL},
  };

  if (!dvbsrc_modulation_type) {
    dvbsrc_modulation_type =
        g_enum_register_static ("GstDvbSrcModulation", modulation_types);
  }
  return dvbsrc_modulation_type;
}

#define GST_TYPE_DVBSRC_TRANSMISSION_MODE (gst_dvbsrc_transmission_mode_get_type ())
static GType
gst_dvbsrc_transmission_mode_get_type (void)
{
  static GType dvbsrc_transmission_mode_type = 0;
  static GEnumValue transmission_mode_types[] = {
    {TRANSMISSION_MODE_2K, "2k", "2k"},
    {TRANSMISSION_MODE_8K, "8k", "8k"},
    {TRANSMISSION_MODE_AUTO, "AUTO", "AUTO"},
    {0, NULL, NULL},
  };

  if (!dvbsrc_transmission_mode_type) {
    dvbsrc_transmission_mode_type =
        g_enum_register_static ("GstDvbSrcTransmission_Mode",
        transmission_mode_types);
  }
  return dvbsrc_transmission_mode_type;
}

#define GST_TYPE_DVBSRC_BANDWIDTH (gst_dvbsrc_bandwidth_get_type ())
static GType
gst_dvbsrc_bandwidth_get_type (void)
{
  static GType dvbsrc_bandwidth_type = 0;
  static GEnumValue bandwidth_types[] = {
    {BANDWIDTH_8_MHZ, "8", "8"},
    {BANDWIDTH_7_MHZ, "7", "7"},
    {BANDWIDTH_6_MHZ, "6", "6"},
    {BANDWIDTH_AUTO, "AUTO", "AUTO"},
    {0, NULL, NULL},
  };

  if (!dvbsrc_bandwidth_type) {
    dvbsrc_bandwidth_type =
        g_enum_register_static ("GstDvbSrcBandwidth", bandwidth_types);
  }
  return dvbsrc_bandwidth_type;
}

#define GST_TYPE_DVBSRC_GUARD (gst_dvbsrc_guard_get_type ())
static GType
gst_dvbsrc_guard_get_type (void)
{
  static GType dvbsrc_guard_type = 0;
  static GEnumValue guard_types[] = {
    {GUARD_INTERVAL_1_32, "32", "32"},
    {GUARD_INTERVAL_1_16, "16", "16"},
    {GUARD_INTERVAL_1_8, "8", "8"},
    {GUARD_INTERVAL_1_4, "4", "4"},
    {GUARD_INTERVAL_AUTO, "AUTO", "AUTO"},
    {0, NULL, NULL},
  };

  if (!dvbsrc_guard_type) {
    dvbsrc_guard_type = g_enum_register_static ("GstDvbSrcGuard", guard_types);
  }
  return dvbsrc_guard_type;
}

#define GST_TYPE_DVBSRC_HIERARCHY (gst_dvbsrc_hierarchy_get_type ())
static GType
gst_dvbsrc_hierarchy_get_type (void)
{
  static GType dvbsrc_hierarchy_type = 0;
  static GEnumValue hierarchy_types[] = {
    {HIERARCHY_NONE, "NONE", "NONE"},
    {HIERARCHY_1, "1", "1"},
    {HIERARCHY_2, "2", "2"},
    {HIERARCHY_4, "4", "4"},
    {HIERARCHY_AUTO, "AUTO", "AUTO"},
    {0, NULL, NULL},
  };

  if (!dvbsrc_hierarchy_type) {
    dvbsrc_hierarchy_type =
        g_enum_register_static ("GstDvbSrcHierarchy", hierarchy_types);
  }
  return dvbsrc_hierarchy_type;
}

#define GST_TYPE_DVBSRC_INVERSION (gst_dvbsrc_inversion_get_type ())
static GType
gst_dvbsrc_inversion_get_type (void)
{
  static GType dvbsrc_inversion_type = 0;
  static GEnumValue inversion_types[] = {
    {INVERSION_OFF, "OFF", "OFF"},
    {INVERSION_ON, "ON", "ON"},
    {INVERSION_AUTO, "AUTO", "AUTO"},
    {0, NULL, NULL},
  };

  if (!dvbsrc_inversion_type) {
    dvbsrc_inversion_type =
        g_enum_register_static ("GstDvbSrcInversion", inversion_types);
  }
  return dvbsrc_inversion_type;
}

static void gst_dvbsrc_finalize (GObject * object);
static void gst_dvbsrc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_dvbsrc_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

static GstFlowReturn gst_dvbsrc_create (GstPushSrc * element,
    GstBuffer ** buffer);

static gboolean gst_dvbsrc_start (GstBaseSrc * bsrc);
static gboolean gst_dvbsrc_stop (GstBaseSrc * bsrc);
static GstStateChangeReturn gst_dvbsrc_change_state (GstElement * element,
    GstStateChange transition);

static gboolean gst_dvbsrc_unlock (GstBaseSrc * bsrc);
static gboolean gst_dvbsrc_is_seekable (GstBaseSrc * bsrc);
static gboolean gst_dvbsrc_get_size (GstBaseSrc * src, guint64 * size);

static gboolean gst_dvbsrc_tune (GstDvbSrc * object);
static void gst_dvbsrc_set_pes_filters (GstDvbSrc * object);
static void gst_dvbsrc_unset_pes_filters (GstDvbSrc * object);

static gboolean gst_dvbsrc_frontend_status (GstDvbSrc * object);

static GstStaticPadTemplate ts_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS
    ("video/mpegts, "
        "mpegversion = (int) 2," "systemstream = (boolean) TRUE"));

/*
 ******************************
 *                            *
 *      GObject Related       *
 *            	              *
 *                            *
 ******************************
 */

GST_BOILERPLATE (GstDvbSrc, gst_dvbsrc, GstPushSrc, GST_TYPE_PUSH_SRC);

static void
gst_dvbsrc_base_init (gpointer gclass)
{
  GstDvbSrcClass *klass = (GstDvbSrcClass *) gclass;
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&ts_src_factory));

  gst_element_class_set_details (element_class, &dvbsrc_details);
}


/* initialize the plugin's class */
static void
gst_dvbsrc_class_init (GstDvbSrcClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
  GstBaseSrcClass *gstbasesrc_class;
  GstPushSrcClass *gstpushsrc_class;

  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
  gstbasesrc_class = (GstBaseSrcClass *) klass;
  gstpushsrc_class = (GstPushSrcClass *) klass;

  gobject_class->set_property = gst_dvbsrc_set_property;
  gobject_class->get_property = gst_dvbsrc_get_property;
  gobject_class->finalize = gst_dvbsrc_finalize;

  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_dvbsrc_change_state);
  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dvbsrc_start);
  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dvbsrc_stop);
  gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbsrc_unlock);
  gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_dvbsrc_is_seekable);
  gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_dvbsrc_get_size);

  gstpushsrc_class->create = gst_dvbsrc_create;
  g_object_class_install_property (gobject_class, ARG_DVBSRC_ADAPTER,
      g_param_spec_int ("adapter",
          "The adapter device number",
          "The adapter device number (eg. 0 for adapter0)", 0, 16, 0,
          G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_FRONTEND,
      g_param_spec_int ("frontend",
          "The frontend device number",
          "The frontend device number (eg. 0 for frontend0)", 0, 16, 0,
          G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_FREQUENCY,
      g_param_spec_int ("frequency",
          "frequency", "Frequency", 0, G_MAXINT, 0, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_POLARITY,
      g_param_spec_string ("polarity",
          "polarity", "Polarity [vhHV] (DVB-S)", "h", G_PARAM_READWRITE));


  g_object_class_install_property (gobject_class, ARG_DVBSRC_PIDS,
      g_param_spec_string ("pids",
          "pids",
          "Colon seperated list of pids (eg. 110:120)",
          "8192", G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_SYM_RATE,
      g_param_spec_int ("symbol-rate",
          "symbol rate",
          "Symbol Rate (DVB-S, DVB-C)",
          0, G_MAXINT, DEFAULT_SYMBOL_RATE, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_TUNE,
      g_param_spec_pointer ("tune",
          "tune", "Atomically tune to channel. (For Apps)", G_PARAM_WRITABLE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_DISEQC_SRC,
      g_param_spec_int ("diseqc-source",
          "diseqc source",
          "DISEqC selected source (-1 disabled) (DVB-S)",
          -1, 7, DEFAULT_DISEQC_SRC, G_PARAM_READWRITE));

  /* DVB-T, additional properties */

  g_object_class_install_property (gobject_class, ARG_DVBSRC_BANDWIDTH,
      g_param_spec_enum ("bandwidth",
          "bandwidth",
          "Bandwidth (DVB-T)", GST_TYPE_DVBSRC_BANDWIDTH, 1,
          G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_CODE_RATE_HP,
      g_param_spec_enum ("code-rate-hp",
          "code-rate-hp",
          "High Priority Code Rate (DVB-T, DVB-S and DVB-C)",
          GST_TYPE_DVBSRC_CODE_RATE, FEC_AUTO, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_CODE_RATE_LP,
      g_param_spec_enum ("code-rate-lp",
          "code-rate-lp",
          "Low Priority Code Rate (DVB-T)",
          GST_TYPE_DVBSRC_CODE_RATE, 1, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_GUARD,
      g_param_spec_enum ("guard",
          "guard",
          "Guard Interval (DVB-T)",
          GST_TYPE_DVBSRC_GUARD, 1, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_MODULATION,
      g_param_spec_enum ("modulation",
          "modulation",
          "Modulation (DVB-T and DVB-C)",
          GST_TYPE_DVBSRC_MODULATION, 1, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class,
      ARG_DVBSRC_TRANSMISSION_MODE,
      g_param_spec_enum ("trans-mode",
          "trans-mode",
          "Transmission Mode (DVB-T)",
          GST_TYPE_DVBSRC_TRANSMISSION_MODE, 1, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class, ARG_DVBSRC_HIERARCHY_INF,
      g_param_spec_enum ("hierarchy",
          "hierarchy",
          "Hierarchy Information (DVB-T)",
          GST_TYPE_DVBSRC_HIERARCHY, 1, G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class, ARG_DVBSRC_INVERSION,
      g_param_spec_enum ("inversion",
          "inversion",
          "Inversion Information (DVB-T and DVB-C)",
          GST_TYPE_DVBSRC_INVERSION, 1, G_PARAM_READWRITE));

  g_object_class_install_property (gobject_class,
      ARG_DVBSRC_STATS_REPORTING_INTERVAL,
      g_param_spec_uint ("stats-reporting-interval",
          "stats-reporting-interval",
          "The number of reads before reporting frontend stats",
          0, G_MAXUINT, 100, G_PARAM_READWRITE));
}

/* initialize the new element
 * instantiate pads and add them to element
 * set functions
 * initialize structure
 */
static void
gst_dvbsrc_init (GstDvbSrc * object, GstDvbSrcClass * klass)
{
  int i = 0;

  GST_INFO_OBJECT (object, "gst_dvbsrc_init");

  /* We are a live source */
  gst_base_src_set_live (GST_BASE_SRC (object), TRUE);

  object->fd_frontend = -1;
  object->fd_dvr = -1;

  for (i = 0; i < MAX_FILTERS; i++) {
    object->pids[i] = G_MAXUINT16;
    object->fd_filters[i] = -1;
  }
  /* Pid 8192 on DVB gets the whole transport stream */
  object->pids[0] = 8192;

  object->adapter_number = 0;
  object->frontend_number = 0;
  object->sym_rate = DEFAULT_SYMBOL_RATE;
  object->diseqc_src = DEFAULT_DISEQC_SRC;
  object->send_diseqc = FALSE;
  object->code_rate_hp = FEC_AUTO;
  object->tune_mutex = g_mutex_new ();
}


static void
gst_dvbsrc_set_property (GObject * _object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstDvbSrc *object;

  g_return_if_fail (GST_IS_DVBSRC (_object));
  object = GST_DVBSRC (_object);

  switch (prop_id) {
    case ARG_DVBSRC_ADAPTER:
      object->adapter_number = g_value_get_int (value);
      break;
    case ARG_DVBSRC_FRONTEND:
      object->frontend_number = g_value_get_int (value);
      break;
    case ARG_DVBSRC_DISEQC_SRC:
      if (object->diseqc_src != g_value_get_int (value)) {
        object->diseqc_src = g_value_get_int (value);
        object->send_diseqc = TRUE;
      }
      GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_DISEQC_ID");
      break;
    case ARG_DVBSRC_FREQUENCY:
      object->freq = g_value_get_int (value);
      GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_FREQUENCY");
      break;
    case ARG_DVBSRC_POLARITY:
    {
      const char *s = NULL;

      s = g_value_get_string (value);
      if (s != NULL)
        object->pol = (s[0] == 'h' || s[0] == 'H') ? DVB_POL_H : DVB_POL_V;
    }
      GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_POLARITY");
      break;
    case ARG_DVBSRC_PIDS:
    {
      gchar *pid_string;

      GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_PIDS");
      pid_string = g_value_dup_string (value);
      if (!strcmp (pid_string, "8192")) {
        /* get the whole ts */
        object->pids[0] = 8192;
      } else {
        int pid = 0;
        int pid_count;
        gchar **pids;
        char **tmp;

        tmp = pids = g_strsplit (pid_string, ":", MAX_FILTERS);

        /* always add the PAT and CAT pids */
        object->pids[0] = 0;
        object->pids[1] = 1;

        pid_count = 2;
        while (*pids != NULL && pid_count < MAX_FILTERS) {
          pid = strtol (*pids, NULL, 0);
          if (pid > 1 && pid <= 8192) {
            GST_INFO_OBJECT (object, "Parsed Pid: %d\n", pid);
            object->pids[pid_count] = pid;
            pid_count++;
          }
          pids++;
        }

        g_strfreev (tmp);
      }
      /* if we are in playing, then set filters now */
      GST_INFO_OBJECT (object, "checking if playing for setting pes filters");
      if (GST_ELEMENT (object)->current_state == GST_STATE_PLAYING) {
        GST_INFO_OBJECT (object, "Setting pes filters now");
        gst_dvbsrc_set_pes_filters (object);
      }
    }
      break;
    case ARG_DVBSRC_SYM_RATE:
      object->sym_rate = g_value_get_int (value);
      GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_SYM_RATE to value %d",
          g_value_get_int (value));
      break;

    case ARG_DVBSRC_BANDWIDTH:
      object->bandwidth = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_CODE_RATE_HP:
      object->code_rate_hp = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_CODE_RATE_LP:
      object->code_rate_lp = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_GUARD:
      object->guard_interval = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_MODULATION:
      object->modulation = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_TRANSMISSION_MODE:
      object->transmission_mode = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_HIERARCHY_INF:
      object->hierarchy_information = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_INVERSION:
      object->inversion = g_value_get_enum (value);
      break;
    case ARG_DVBSRC_TUNE:
      GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_TUNE");
      /* if we are in paused/playing state tune now, otherwise in ready to paused state change */
      if (gst_element_get_state
          (GST_ELEMENT (object), NULL, NULL,
              GST_CLOCK_TIME_NONE) > GST_STATE_READY) {
        g_mutex_lock (object->tune_mutex);
        gst_dvbsrc_tune (object);
        g_mutex_unlock (object->tune_mutex);
      }
      break;
    case ARG_DVBSRC_STATS_REPORTING_INTERVAL:
      object->stats_interval = g_value_get_uint (value);
      object->stats_counter = 0;
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  }

}

static void
gst_dvbsrc_get_property (GObject * _object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstDvbSrc *object;

  g_return_if_fail (GST_IS_DVBSRC (_object));
  object = GST_DVBSRC (_object);

  switch (prop_id) {
    case ARG_DVBSRC_ADAPTER:
      g_value_set_int (value, object->adapter_number);
      break;
    case ARG_DVBSRC_FRONTEND:
      g_value_set_int (value, object->frontend_number);
      break;
    case ARG_DVBSRC_FREQUENCY:
      g_value_set_int (value, object->freq);
      break;
    case ARG_DVBSRC_POLARITY:
      if (object->pol == DVB_POL_H)
        g_value_set_string (value, "H");
      else
        g_value_set_string (value, "V");
      break;
    case ARG_DVBSRC_SYM_RATE:
      g_value_set_int (value, object->sym_rate);
      break;
    case ARG_DVBSRC_DISEQC_SRC:
      g_value_set_int (value, object->diseqc_src);
      break;
    case ARG_DVBSRC_BANDWIDTH:
      g_value_set_enum (value, object->bandwidth);
      break;
    case ARG_DVBSRC_CODE_RATE_HP:
      g_value_set_enum (value, object->code_rate_hp);
      break;
    case ARG_DVBSRC_CODE_RATE_LP:
      g_value_set_enum (value, object->code_rate_lp);
      break;
    case ARG_DVBSRC_GUARD:
      g_value_set_enum (value, object->guard_interval);
      break;
    case ARG_DVBSRC_MODULATION:
      g_value_set_enum (value, object->modulation);
      break;
    case ARG_DVBSRC_TRANSMISSION_MODE:
      g_value_set_enum (value, object->transmission_mode);
      break;
    case ARG_DVBSRC_HIERARCHY_INF:
      g_value_set_enum (value, object->hierarchy_information);
      break;
    case ARG_DVBSRC_INVERSION:
      g_value_set_enum (value, object->inversion);
      break;
    case ARG_DVBSRC_STATS_REPORTING_INTERVAL:
      g_value_set_uint (value, object->stats_interval);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  }
}

static gboolean
gst_dvbsrc_close_devices (GstDvbSrc * object)
{
  gst_dvbsrc_unset_pes_filters (object);

  close (object->fd_dvr);
  object->fd_dvr = -1;
  close (object->fd_frontend);
  object->fd_frontend = -1;

  return TRUE;
}

static gboolean
gst_dvbsrc_open_frontend (GstDvbSrc * object)
{
  struct dvb_frontend_info fe_info;
  char *adapter_desc = NULL;
  gchar *frontend_dev;
  GstStructure *adapter_structure;
  char *adapter_name = NULL;

  frontend_dev = g_strdup_printf ("/dev/dvb/adapter%d/frontend%d",
      object->adapter_number, object->frontend_number);
  GST_INFO_OBJECT (object, "Using frontend device: %s", frontend_dev);

  /* open frontend */
  if ((object->fd_frontend = open (frontend_dev, O_RDWR)) < 0) {
    switch (errno) {
      case ENOENT:
        GST_ELEMENT_ERROR (object, RESOURCE, NOT_FOUND,
            (_("Device \"%s\" does not exist."), frontend_dev), (NULL));
        break;
      default:
        GST_ELEMENT_ERROR (object, RESOURCE, OPEN_READ_WRITE,
            (_("Could not open frontend device \"%s\"."), frontend_dev),
            GST_ERROR_SYSTEM);
        break;
    }

    close (object->fd_frontend);
    g_free (frontend_dev);
    return FALSE;
  }

  if (ioctl (object->fd_frontend, FE_GET_INFO, &fe_info) < 0) {
    GST_ELEMENT_ERROR (object, RESOURCE, SETTINGS,
        (_("Could not get settings from frontend device \"%s\"."),
            frontend_dev), GST_ERROR_SYSTEM);

    close (object->fd_frontend);
    g_free (frontend_dev);
    return FALSE;
  }

  adapter_name = g_strdup (fe_info.name);

  object->adapter_type = fe_info.type;
  switch (object->adapter_type) {
    case FE_QPSK:
      adapter_desc = "DVB-S";
      adapter_structure = gst_structure_new ("dvb-adapter",
          "type", G_TYPE_STRING, adapter_desc,
          "name", G_TYPE_STRING, adapter_name,
          "auto-fec", G_TYPE_BOOLEAN, fe_info.caps & FE_CAN_FEC_AUTO, NULL);
      break;
    case FE_QAM:
      adapter_desc = "DVB-C";
      adapter_structure = gst_structure_new ("dvb-adapter",
          "type", G_TYPE_STRING, adapter_desc,
          "name", G_TYPE_STRING, adapter_name,
          "auto-inversion", G_TYPE_BOOLEAN,
          fe_info.caps & FE_CAN_INVERSION_AUTO, "auto-qam", G_TYPE_BOOLEAN,
          fe_info.caps & FE_CAN_QAM_AUTO, "auto-fec", G_TYPE_BOOLEAN,
          fe_info.caps & FE_CAN_FEC_AUTO, NULL);
      break;
    case FE_OFDM:
      adapter_desc = "DVB-T";
      adapter_structure = gst_structure_new ("dvb-adapter",
          "type", G_TYPE_STRING, adapter_desc,
          "name", G_TYPE_STRING, adapter_name,
          "auto-inversion", G_TYPE_BOOLEAN,
          fe_info.caps & FE_CAN_INVERSION_AUTO, "auto-qam", G_TYPE_BOOLEAN,
          fe_info.caps & FE_CAN_QAM_AUTO, "auto-transmission-mode",
          G_TYPE_BOOLEAN, fe_info.caps & FE_CAN_TRANSMISSION_MODE_AUTO,
          "auto-guard-interval", G_TYPE_BOOLEAN,
          fe_info.caps & FE_CAN_GUARD_INTERVAL_AUTO, "auto-hierarchy",
          G_TYPE_BOOLEAN, fe_info.caps % FE_CAN_HIERARCHY_AUTO, "auto-fec",
          G_TYPE_BOOLEAN, fe_info.caps & FE_CAN_FEC_AUTO, NULL);
      break;
    case FE_ATSC:
      adapter_desc = "ATSC";
      adapter_structure = gst_structure_new ("dvb-adapter",
          "type", G_TYPE_STRING, adapter_desc,
          "name", G_TYPE_STRING, adapter_name, NULL);
      break;
    default:
      g_error ("Unknown frontend type: %d", object->adapter_type);
      adapter_structure = gst_structure_new ("dvb-adapter",
          "type", G_TYPE_STRING, "unknown", NULL);
  }

  GST_INFO_OBJECT (object, "DVB card: %s ", adapter_name);
  gst_element_post_message (GST_ELEMENT_CAST (object), gst_message_new_element
      (GST_OBJECT (object), adapter_structure));
  g_free (frontend_dev);
  g_free (adapter_name);
  return TRUE;
}

static gboolean
gst_dvbsrc_open_dvr (GstDvbSrc * object)
{
  gchar *dvr_dev;

  dvr_dev = g_strdup_printf ("/dev/dvb/adapter%d/dvr%d",
      object->adapter_number, object->frontend_number);
  GST_INFO_OBJECT (object, "Using dvr device: %s", dvr_dev);

  /* open DVR */
  if ((object->fd_dvr = open (dvr_dev, O_RDONLY | O_NONBLOCK)) < 0) {
    switch (errno) {
      case ENOENT:
        GST_ELEMENT_ERROR (object, RESOURCE, NOT_FOUND,
            (_("Device \"%s\" does not exist."), dvr_dev), (NULL));
        break;
      default:
        GST_ELEMENT_ERROR (object, RESOURCE, OPEN_READ,
            (_("Could not open file \"%s\" for reading."), dvr_dev),
            GST_ERROR_SYSTEM);
        break;
    }
    g_free (dvr_dev);
    return FALSE;
  }
  g_free (dvr_dev);
  GST_INFO_OBJECT (object, "Setting buffer size");
  if (ioctl (object->fd_dvr, DMX_SET_BUFFER_SIZE, 1024 * 1024) < 0) {
    GST_INFO_OBJECT (object, "DMX_SET_BUFFER_SIZE failed");
    return FALSE;
  }
  return TRUE;
}

static void
gst_dvbsrc_finalize (GObject * _object)
{
  GstDvbSrc *object;

  GST_DEBUG_OBJECT (_object, "gst_dvbsrc_finalize");

  g_return_if_fail (GST_IS_DVBSRC (_object));
  object = GST_DVBSRC (_object);

  /* freeing the mutex segfaults somehow */
  g_mutex_free (object->tune_mutex);

  if (G_OBJECT_CLASS (parent_class)->finalize)
    G_OBJECT_CLASS (parent_class)->finalize (_object);
}


/*
 ******************************
 *                            *
 *      Plugin Realisation    *
 *                            *
 ******************************
 */



/* entry point to initialize the plug-in
 * initialize the plug-in itself
 * register the element factories and pad templates
 * register the features
 */
gboolean
gst_dvbsrc_plugin_init (GstPlugin * plugin)
{
  GST_DEBUG_CATEGORY_INIT (gstdvbsrc_debug, "dvbsrc", 0, "DVB Source Element");

#ifdef ENABLE_NLS
  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
      LOCALEDIR);
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */

  return gst_element_register (plugin, "dvbsrc", GST_RANK_NONE,
      GST_TYPE_DVBSRC);
}

static GstBuffer *
read_device (int fd, int adapter_number, int frontend_number, int size)
{
  int count = 0;
  struct pollfd pfd[1];
  int ret_val = 0;
  guint attempts = 0;
  const int TIMEOUT = 100;

  GstBuffer *buf = gst_buffer_new_and_alloc (size);

  g_return_val_if_fail (GST_IS_BUFFER (buf), NULL);

  if (fd < 0) {
    return NULL;
  }

  pfd[0].fd = fd;
  pfd[0].events = POLLIN;

  while (count < size) {
    ret_val = poll (pfd, 1, TIMEOUT);
    if (ret_val > 0) {
      if (pfd[0].revents & POLLIN) {
        int tmp = 0;

        tmp = read (fd, GST_BUFFER_DATA (buf) + count, size - count);
        if (tmp < 0) {
          GST_WARNING
              ("Unable to read from device: /dev/dvb/adapter%d/dvr%d (%d)",
              adapter_number, frontend_number, errno);
          attempts += 1;
          if (attempts % 10 == 0) {
            GST_WARNING
                ("Unable to read from device after %u attempts: /dev/dvb/adapter%d/dvr%d",
                attempts, adapter_number, frontend_number);
          }

        } else
          count = count + tmp;
      } else {
        GST_LOG ("revents = %d\n", pfd[0].revents);
      }
    } else if (ret_val == 0) {  // poll timeout
      attempts += 1;
      GST_INFO ("Reading from device /dev/dvb/adapter%d/dvr%d timedout (%d)",
          adapter_number, frontend_number, attempts);

      if (attempts % 10 == 0) {
        GST_WARNING
            ("Unable to read after %u attempts from device: /dev/dvb/adapter%d/dvr%d (%d)",
            attempts, adapter_number, frontend_number, errno);
      }
    } else if (errno == -EINTR) {       // poll interrupted
      ;
    }

  }

  GST_BUFFER_SIZE (buf) = count;
  GST_BUFFER_TIMESTAMP (buf) = GST_CLOCK_TIME_NONE;
  return buf;
}

static GstFlowReturn
gst_dvbsrc_create (GstPushSrc * element, GstBuffer ** buf)
{
  gint buffer_size;
  GstFlowReturn retval = GST_FLOW_ERROR;
  GstDvbSrc *object;

  object = GST_DVBSRC (element);
  GST_LOG ("fd_dvr: %d", object->fd_dvr);

  //g_object_get(G_OBJECT(object), "blocksize", &buffer_size, NULL);
  buffer_size = DEFAULT_BUFFER_SIZE;

  /* device can not be tuned during read */
  g_mutex_lock (object->tune_mutex);


  if (object->fd_dvr > -1) {
    /* --- Read TS from DVR device --- */
    GST_DEBUG_OBJECT (object, "Reading from DVR device");
    *buf = read_device (object->fd_dvr, object->adapter_number,
        object->frontend_number, buffer_size);
    if (*buf != NULL) {
      GstCaps *caps;

      retval = GST_FLOW_OK;

      caps = gst_pad_get_caps (GST_BASE_SRC_PAD (object));
      gst_buffer_set_caps (*buf, caps);
      gst_caps_unref (caps);
    } else {
      GST_DEBUG_OBJECT (object, "Failed to read from device");
      gst_element_post_message (GST_ELEMENT_CAST (object),
          gst_message_new_element (GST_OBJECT (object),
              gst_structure_empty_new ("dvb-read-failure")));
    }

    if (object->stats_interval != 0 &&
        ++object->stats_counter == object->stats_interval) {
      gst_dvbsrc_output_frontend_stats (object);
      object->stats_counter = 0;
    }
  }

  g_mutex_unlock (object->tune_mutex);
  return retval;

}

static GstStateChangeReturn
gst_dvbsrc_change_state (GstElement * element, GstStateChange transition)
{
  GstDvbSrc *src;
  GstStateChangeReturn ret;

  src = GST_DVBSRC (element);
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      /* open frontend then close it again, just so caps sent */
      gst_dvbsrc_open_frontend (src);
      if (src->fd_frontend) {
        close (src->fd_frontend);
      }
      break;
    default:
      break;
  }

  return ret;
}


static gboolean
gst_dvbsrc_start (GstBaseSrc * bsrc)
{
  GstDvbSrc *src = GST_DVBSRC (bsrc);

  gst_dvbsrc_open_frontend (src);
  if (!gst_dvbsrc_tune (src)) {
    GST_ERROR_OBJECT (src, "Not able to lock on to the dvb channel");
    close (src->fd_frontend);
    return FALSE;
  }
  if (!gst_dvbsrc_frontend_status (src)) {
    /* unset filters also */
    gst_dvbsrc_unset_pes_filters (src);
    close (src->fd_frontend);
    return FALSE;
  }
  if (!gst_dvbsrc_open_dvr (src)) {
    GST_ERROR_OBJECT (src, "Not able to open dvr_device");
    /* unset filters also */
    gst_dvbsrc_unset_pes_filters (src);
    close (src->fd_frontend);
    return FALSE;
  }

  return TRUE;
}

static gboolean
gst_dvbsrc_stop (GstBaseSrc * bsrc)
{
  GstDvbSrc *src = GST_DVBSRC (bsrc);

  gst_dvbsrc_close_devices (src);
  return TRUE;
}

static gboolean
gst_dvbsrc_unlock (GstBaseSrc * bsrc)
{
  return TRUE;
}

static gboolean
gst_dvbsrc_is_seekable (GstBaseSrc * bsrc)
{
  return FALSE;
}

static gboolean
gst_dvbsrc_get_size (GstBaseSrc * src, guint64 * size)
{
  return FALSE;
}

static void
gst_dvbsrc_output_frontend_stats (GstDvbSrc * src)
{
  fe_status_t status;
  uint16_t snr, _signal;
  uint32_t ber, uncorrected_blocks;
  GstMessage *message;
  GstStructure *structure;
  int fe_fd = src->fd_frontend;

  ioctl (fe_fd, FE_READ_STATUS, &status);
  ioctl (fe_fd, FE_READ_SIGNAL_STRENGTH, &_signal);
  ioctl (fe_fd, FE_READ_SNR, &snr);
  ioctl (fe_fd, FE_READ_BER, &ber);
  ioctl (fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks);

  structure = gst_structure_new ("dvb-frontend-stats", "status", G_TYPE_INT,
      status, "signal", G_TYPE_INT, _signal, "snr", G_TYPE_INT, snr,
      "ber", G_TYPE_INT, ber, "unc", G_TYPE_INT, uncorrected_blocks,
      "lock", G_TYPE_BOOLEAN, status & FE_HAS_LOCK, NULL);
  message = gst_message_new_element (GST_OBJECT (src), structure);
  gst_element_post_message (GST_ELEMENT (src), message);
}

static gboolean
gst_dvbsrc_frontend_status (GstDvbSrc * object)
{
  fe_status_t status = 0;
  gint i;

  GST_INFO_OBJECT (object, "gst_dvbsrc_frontend_status\n");

  if (object->fd_frontend < 0) {
    GST_ERROR_OBJECT (object,
        "Trying to get frontend status from not opened device!");
    return FALSE;
  } else
    GST_INFO_OBJECT (object, "fd-frontend: %d", object->fd_frontend);

  for (i = 0; i < 15; i++) {
    usleep (1000000);
    GST_INFO_OBJECT (object, ".");
    if (ioctl (object->fd_frontend, FE_READ_STATUS, &status) == -1) {
      GST_ERROR_OBJECT (object, "Failed reading frontend status.");
      return FALSE;
    }
    gst_dvbsrc_output_frontend_stats (object);
    if (status & FE_HAS_LOCK) {
      break;
    }
  }

  if (!(status & FE_HAS_LOCK)) {
    GST_INFO_OBJECT (object,
        "Not able to lock to the signal on the given frequency.\n");
    return FALSE;
  } else
    return TRUE;
}

struct diseqc_cmd
{
  struct dvb_diseqc_master_cmd cmd;
  uint32_t wait;
};

static void
diseqc_send_msg (int fd, fe_sec_voltage_t v, struct diseqc_cmd *cmd,
    fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b)
{
  if (ioctl (fd, FE_SET_TONE, SEC_TONE_OFF) == -1) {
    GST_ERROR ("Setting tone to off failed");
    return;
  }

  if (ioctl (fd, FE_SET_VOLTAGE, v) == -1) {
    GST_ERROR ("Setting voltage failed");
    return;
  }

  usleep (15 * 1000);
  GST_LOG ("diseqc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", cmd->cmd.msg[0],
      cmd->cmd.msg[1], cmd->cmd.msg[2], cmd->cmd.msg[3], cmd->cmd.msg[4],
      cmd->cmd.msg[5]);
  if (ioctl (fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) == -1) {
    GST_ERROR ("Sending diseqc command failed");
    return;
  }

  usleep (cmd->wait * 1000);
  usleep (15 * 1000);

  if (ioctl (fd, FE_DISEQC_SEND_BURST, b) == -1) {
    GST_ERROR ("Sending burst failed");
    return;
  }

  usleep (15 * 1000);

  if (ioctl (fd, FE_SET_TONE, t) == -1) {
    GST_ERROR ("Setting tone failed");
    return;
  }
}


/* digital satellite equipment control,
 * specification is available from http://www.eutelsat.com/
 */
static void
diseqc (int secfd, int sat_no, int voltage, int tone)
{
  struct diseqc_cmd cmd = { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 };

  /* param: high nibble: reset bits, low nibble set bits,
   * bits are: option, position, polarizaion, band
   */
  cmd.cmd.msg[3] =
      0xf0 | (((sat_no * 4) & 0x0f) | (tone == SEC_TONE_ON ? 1 : 0) |
      (voltage == SEC_VOLTAGE_13 ? 0 : 2));
  /* send twice because some diseqc switches do not respond correctly the
   * first time */
  diseqc_send_msg (secfd, voltage, &cmd, tone,
      sat_no % 2 ? SEC_MINI_B : SEC_MINI_A);
  diseqc_send_msg (secfd, voltage, &cmd, tone,
      sat_no % 2 ? SEC_MINI_B : SEC_MINI_A);

}


static gboolean
gst_dvbsrc_tune (GstDvbSrc * object)
{
#if DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR == 3
  struct dvbfe_params feparams;
#else
  struct dvb_frontend_parameters feparams;
#endif
  fe_sec_voltage_t voltage;
  fe_status_t status;
  int i;
  int j;
  unsigned int freq = object->freq;
  unsigned int sym_rate = object->sym_rate * 1000;

  /* found in mail archive on linuxtv.org
   * What works well for us is:
   * - first establish a TS feed (i.e. tune the frontend and check for success)
   * - then set filters (PES/sections)
   * - then tell the MPEG decoder to start
   * - before tuning: first stop the MPEG decoder, then stop all filters  
   */
  GST_INFO_OBJECT (object, "gst_dvbsrc_tune");

  if (object->fd_frontend < 0) {
    /* frontend not opened yet, tune later */
    GST_INFO_OBJECT (object, "Frontend not open: tuning later");
    return FALSE;
  }

  gst_dvbsrc_unset_pes_filters (object);
  for (j = 0; j < 5; j++) {
    switch (object->adapter_type) {
      case FE_QPSK:
        object->tone = SEC_TONE_OFF;
        if (freq > 2200000) {
          // this must be an absolute frequency
          if (freq < SLOF) {
            feparams.frequency = (freq - LOF1);
          } else {
            feparams.frequency = (freq - LOF2);
            object->tone = SEC_TONE_ON;
          }
        } else {
          // this is an L-Band frequency
          feparams.frequency = freq;
        }
        feparams.inversion = INVERSION_AUTO;
        GST_DEBUG_OBJECT (object, "api version %d.%d", DVB_API_VERSION,
            DVB_API_VERSION_MINOR);
#if DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR == 3
        GST_DEBUG_OBJECT (object, "using multiproto driver");
        feparams.delsys.dvbs.symbol_rate = sym_rate;
        feparams.delsys.dvbs.fec = object->code_rate_hp;
#else
        feparams.u.qpsk.symbol_rate = sym_rate;
        feparams.u.qpsk.fec_inner = object->code_rate_hp;
#endif
        GST_INFO_OBJECT (object,
            "tuning DVB-S to L-Band:%u, Pol:%d, srate=%u, 22kHz=%s",
            feparams.frequency, object->pol, sym_rate,
            object->tone == SEC_TONE_ON ? "on" : "off");

        if (object->pol == DVB_POL_H)
          voltage = SEC_VOLTAGE_18;
        else
          voltage = SEC_VOLTAGE_13;

        if (object->diseqc_src == -1 || object->send_diseqc == FALSE) {
          if (ioctl (object->fd_frontend, FE_SET_VOLTAGE, voltage) < 0) {
            g_warning ("Unable to set voltage on dvb frontend device");
          }

          if (ioctl (object->fd_frontend, FE_SET_TONE, object->tone) < 0) {
            g_warning ("Error setting tone: %s", strerror (errno));
          }
        } else {
          GST_DEBUG_OBJECT (object, "Sending DISEqC");
          diseqc (object->fd_frontend, object->diseqc_src, voltage,
              object->tone);
          /* Once diseqc source is set, do not set it again until
           * app decides to change it */
          //object->send_diseqc = FALSE;
        }

        break;
      case FE_OFDM:

        feparams.frequency = freq;
#if DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR == 3
#else
        feparams.u.ofdm.bandwidth = object->bandwidth;
        feparams.u.ofdm.code_rate_HP = object->code_rate_hp;
        feparams.u.ofdm.code_rate_LP = object->code_rate_lp;
        feparams.u.ofdm.constellation = object->modulation;
        feparams.u.ofdm.transmission_mode = object->transmission_mode;
        feparams.u.ofdm.guard_interval = object->guard_interval;
        feparams.u.ofdm.hierarchy_information = object->hierarchy_information;
#endif
        feparams.inversion = object->inversion;

        GST_INFO_OBJECT (object, "tuning DVB-T to %d Hz\n", freq);
        break;
      case FE_QAM:
        GST_INFO_OBJECT (object, "Tuning DVB-C to %d, srate=%d", freq,
            sym_rate);
        feparams.frequency = freq;
        feparams.inversion = object->inversion;
        feparams.u.qam.fec_inner = object->code_rate_hp;
        feparams.u.qam.modulation = object->modulation;
        feparams.u.qam.symbol_rate = sym_rate;
        break;
      case FE_ATSC:
        GST_INFO_OBJECT (object, "Tuning ATSC to %d", freq);
        feparams.frequency = freq;
        feparams.u.vsb.modulation = object->modulation;
        break;
      default:
        g_error ("Unknown frontend type: %d", object->adapter_type);

    }
    usleep (100000);
    /* now tune the frontend */
#if DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR == 3
    if (ioctl (object->fd_frontend, DVBFE_SET_PARAMS, &feparams) < 0) {
#else
    if (ioctl (object->fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
#endif
      g_warning ("Error tuning channel: %s", strerror (errno));
    }
    for (i = 0; i < 5; i++) {
      usleep (100000);
      if (ioctl (object->fd_frontend, FE_READ_STATUS, &status) == -1) {
        perror ("FE_READ_STATUS");
        break;
      }
      GST_LOG_OBJECT (object, "status == 0x%02x", status);
    }
    if (status & FE_HAS_LOCK)
      break;
  }
  if (!(status & FE_HAS_LOCK))
    return FALSE;
  /* set pid filters */
  gst_dvbsrc_set_pes_filters (object);

  return TRUE;
}


static void
gst_dvbsrc_unset_pes_filters (GstDvbSrc * object)
{
  int i = 0;

  GST_INFO_OBJECT (object, "clearing PES filter");

  for (i = 0; i < MAX_FILTERS; i++) {
    if (object->fd_filters[i] == -1)
      continue;
    close (object->fd_filters[i]);
    object->fd_filters[i] = -1;
  }
}

static void
gst_dvbsrc_set_pes_filters (GstDvbSrc * object)
{
  int *fd;
  int pid, i;
  struct dmx_pes_filter_params pes_filter;
  gchar *demux_dev = g_strdup_printf ("/dev/dvb/adapter%d/demux%d",
      object->adapter_number, object->frontend_number);

  GST_INFO_OBJECT (object, "Setting PES filter");

  for (i = 0; i < MAX_FILTERS; i++) {
    if (object->pids[i] == G_MAXUINT16)
      break;

    fd = &object->fd_filters[i];
    pid = object->pids[i];

    close (*fd);
    if ((*fd = open (demux_dev, O_RDWR)) < 0) {
      g_error ("Error opening demuxer: %s (%s)", strerror (errno), demux_dev);
      g_free (demux_dev);
    }
    g_return_if_fail (*fd != -1);

    pes_filter.pid = pid;
    pes_filter.input = DMX_IN_FRONTEND;
    pes_filter.output = DMX_OUT_TS_TAP;
    pes_filter.pes_type = DMX_PES_OTHER;
    pes_filter.flags = DMX_IMMEDIATE_START;

    GST_INFO_OBJECT (object, "Setting pes-filter, pid = %d, type = %d",
        pes_filter.pid, pes_filter.pes_type);

    if (ioctl (*fd, DMX_SET_PES_FILTER, &pes_filter) < 0)
      GST_WARNING_OBJECT (object, "Error setting PES filter on %s: %s",
          demux_dev, strerror (errno));
  }

  g_free (demux_dev);
}