/* GStreamer DVB source * Copyright (C) 2006 Zaheer Abbas Merali * * 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. * * * Example launch line * |[ * 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 ! mpegtsdemux name=demux ! queue max-size-buffers=0 max-size-time=0 ! mpeg2dec ! xvimagesink demux. ! queue max-size-buffers=0 max-size-time=0 ! mad ! alsasink * ]| 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 renders 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=100:256:257 ! mpegtsdemux name=demux ! queue max-size-buffers=0 max-size-time=0 ! mpeg2dec ! xvimagesink demux. ! queue max-size-buffers=0 max-size-time=0 ! mad ! alsasink * ]| Captures and renders a transport stream from dvb card 0 that is a DVB-T card for a program at tuned frequency 514000000 with PMT pid 100 and elementary stream pids of 256, 257 with other parameters as seen in the pipeline. * |[ * gst-launch dvbsrc polarity="h" frequency=11302000 symbol-rate=27500 diseqc-src=0 pids=50:102:103 ! mpegtsdemux name=demux ! queue max-size-buffers=0 max-size-time=0 ! mpeg2dec ! xvimagesink demux. ! queue max-size-buffers=0 max-size-time=0 ! mad ! alsasink * ]| Captures and renders a 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 with PMT pid of 50 and elementary stream pids of 102 and 103. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstdvbsrc.h" #include #include #include #include #include #include #include #include #include #include "_stdint.h" #define _XOPEN_SOURCE 500 #include #include #include #include #include GST_DEBUG_CATEGORY_STATIC (gstdvbsrc_debug); #define GST_CAT_DEFAULT (gstdvbsrc_debug) #define SLOF (11700*1000UL) #define LOF1 (9750*1000UL) #define LOF2 (10600*1000UL) /* 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, ARG_DVBSRC_TIMEOUT, }; #define DEFAULT_ADAPTER 0 #define DEFAULT_FRONTEND 0 #define DEFAULT_DISEQC_SRC -1 /* disabled */ #define DEFAULT_FREQUENCY 0 #define DEFAULT_POLARITY "H" #define DEFAULT_PIDS "8192" #define DEFAULT_SYMBOL_RATE 0 #define DEFAULT_BANDWIDTH BANDWIDTH_7_MHZ #define DEFAULT_CODE_RATE_HP FEC_AUTO #define DEFAULT_CODE_RATE_LP FEC_1_2 #define DEFAULT_GUARD GUARD_INTERVAL_1_16 #define DEFAULT_MODULATION QAM_16 #define DEFAULT_TRANSMISSION_MODE TRANSMISSION_MODE_8K #define DEFAULT_HIERARCHY HIERARCHY_1 #define DEFAULT_INVERSION INVERSION_ON #define DEFAULT_STATS_REPORTING_INTERVAL 100 #define DEFAULT_TIMEOUT 1000000 /* 1 second */ #define DEFAULT_BUFFER_SIZE 8192 /* not a property */ 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", "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"}, {PSK_8, "8PSK", "8psk"}, {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_unlock_stop (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 * * * * * ****************************** */ #define gst_dvbsrc_parent_class parent_class G_DEFINE_TYPE (GstDvbSrc, gst_dvbsrc, GST_TYPE_PUSH_SRC); /* 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); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&ts_src_factory)); gst_element_class_set_details_simple (gstelement_class, "DVB Source", "Source/Video", "Digital Video Broadcast Source", "P2P-VCR, C-Lab, University of Paderborn," "Zaheer Abbas Merali "); 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->unlock_stop = GST_DEBUG_FUNCPTR (gst_dvbsrc_unlock_stop); 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_DEBUG_FUNCPTR (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, DEFAULT_ADAPTER, 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, DEFAULT_FRONTEND, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, ARG_DVBSRC_FREQUENCY, g_param_spec_uint ("frequency", "frequency", "Frequency", 0, G_MAXUINT, DEFAULT_FREQUENCY, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, ARG_DVBSRC_POLARITY, g_param_spec_string ("polarity", "polarity", "Polarity [vhHV] (DVB-S)", DEFAULT_POLARITY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, ARG_DVBSRC_PIDS, g_param_spec_string ("pids", "pids", "Colon seperated list of pids (eg. 110:120)", DEFAULT_PIDS, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, ARG_DVBSRC_SYM_RATE, g_param_spec_uint ("symbol-rate", "symbol rate", "Symbol Rate (DVB-S, DVB-C)", 0, G_MAXUINT, 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, DEFAULT_BANDWIDTH, 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, DEFAULT_CODE_RATE_HP, 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, DEFAULT_CODE_RATE_LP, G_PARAM_READWRITE)); /* FIXME: should the property be called 'guard-interval' then? */ g_object_class_install_property (gobject_class, ARG_DVBSRC_GUARD, g_param_spec_enum ("guard", "guard", "Guard Interval (DVB-T)", GST_TYPE_DVBSRC_GUARD, DEFAULT_GUARD, 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, DEFAULT_MODULATION, G_PARAM_READWRITE)); /* FIXME: property should be named 'transmission-mode' */ 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, DEFAULT_TRANSMISSION_MODE, 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, DEFAULT_HIERARCHY, 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, DEFAULT_INVERSION, 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, DEFAULT_STATS_REPORTING_INTERVAL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, ARG_DVBSRC_TIMEOUT, g_param_spec_uint64 ("timeout", "Timeout", "Post a message after timeout microseconds (0 = disabled)", 0, G_MAXUINT64, DEFAULT_TIMEOUT, 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) { 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); /* And we wanted timestamped output */ gst_base_src_set_do_timestamp (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 = DEFAULT_ADAPTER; object->frontend_number = DEFAULT_FRONTEND; object->diseqc_src = DEFAULT_DISEQC_SRC; object->send_diseqc = (DEFAULT_DISEQC_SRC != -1); /* object->pol = DVB_POL_H; *//* set via G_PARAM_CONSTRUCT */ object->sym_rate = DEFAULT_SYMBOL_RATE; object->bandwidth = DEFAULT_BANDWIDTH; object->code_rate_hp = DEFAULT_CODE_RATE_HP; object->code_rate_lp = DEFAULT_CODE_RATE_LP; object->guard_interval = DEFAULT_GUARD; object->modulation = DEFAULT_MODULATION; object->transmission_mode = DEFAULT_TRANSMISSION_MODE; object->hierarchy_information = DEFAULT_HIERARCHY; object->inversion = DEFAULT_INVERSION; object->stats_interval = DEFAULT_STATS_REPORTING_INTERVAL; g_mutex_init (&object->tune_mutex); object->timeout = DEFAULT_TIMEOUT; } 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_uint (value); GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_FREQUENCY (%d Hz)", object->freq); 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"); GST_INFO_OBJECT (object, "\t%s", (s[0] == 'h' || s[0] == 'H') ? "DVB_POL_H" : "DVB_POL_V"); } break; } case ARG_DVBSRC_PIDS: { gchar *pid_string; pid_string = g_value_dup_string (value); GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_PIDS %s", pid_string); if (!strcmp (pid_string, "8192")) { /* get the whole ts */ int pid_count = 1; object->pids[0] = 8192; while (pid_count < MAX_FILTERS) { object->pids[pid_count++] = G_MAXUINT16; } } else { int pid = 0; int pid_count; gchar **pids; char **tmp; tmp = pids = g_strsplit (pid_string, ":", MAX_FILTERS); if (pid_string) g_free (pid_string); /* 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, "\tParsed Pid: %d", pid); object->pids[pid_count] = pid; pid_count++; } pids++; } while (pid_count < MAX_FILTERS) { object->pids[pid_count++] = G_MAXUINT16; } g_strfreev (tmp); } /* if we are in playing or paused, 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_ELEMENT (object)->current_state == GST_STATE_PAUSED) { 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_uint (value); GST_INFO_OBJECT (object, "Set Property: ARG_DVBSRC_SYM_RATE to value %d", object->sym_rate); 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_STATE (object) > 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; case ARG_DVBSRC_TIMEOUT: object->timeout = g_value_get_uint64 (value); 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_uint (value, object->freq); break; case ARG_DVBSRC_POLARITY: if (object->pol == DVB_POL_H) g_value_set_static_string (value, "H"); else g_value_set_static_string (value, "V"); break; case ARG_DVBSRC_SYM_RATE: g_value_set_uint (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; case ARG_DVBSRC_TIMEOUT: g_value_set_uint64 (value, object->timeout); 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, gboolean writable) { struct dvb_frontend_info fe_info; const 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, writable ? O_RDWR : O_RDONLY)) < 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; } GST_DEBUG_OBJECT (object, "Device opened, querying information"); 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; } GST_DEBUG_OBJECT (object, "Got information about adapter : %s", fe_info.name); 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_clear (&object->tune_mutex); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (_object); } /* ****************************** * * * Plugin Realization * * * ****************************** */ /* 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 GstFlowReturn gst_dvbsrc_read_device (GstDvbSrc * object, int size, GstBuffer ** buffer) { gint count = 0; gint ret_val = 0; GstBuffer *buf = gst_buffer_new_and_alloc (size); GstClockTime timeout = object->timeout * GST_USECOND; GstMapInfo map; g_return_val_if_fail (GST_IS_BUFFER (buf), GST_FLOW_ERROR); if (object->fd_dvr < 0) return GST_FLOW_ERROR; gst_buffer_map (buf, &map, GST_MAP_WRITE); while (count < size) { ret_val = gst_poll_wait (object->poll, timeout); GST_LOG_OBJECT (object, "select returned %d", ret_val); if (G_UNLIKELY (ret_val < 0)) { if (errno == EBUSY) goto stopped; else goto select_error; } else if (G_UNLIKELY (ret_val == 0)) { /* timeout, post element message */ gst_element_post_message (GST_ELEMENT_CAST (object), gst_message_new_element (GST_OBJECT (object), gst_structure_new_empty ("dvb-read-failure"))); } else { int nread = read (object->fd_dvr, map.data + count, size - count); if (G_UNLIKELY (nread < 0)) { GST_WARNING_OBJECT (object, "Unable to read from device: /dev/dvb/adapter%d/dvr%d (%d)", object->adapter_number, object->frontend_number, errno); gst_element_post_message (GST_ELEMENT_CAST (object), gst_message_new_element (GST_OBJECT (object), gst_structure_new_empty ("dvb-read-failure"))); } else count = count + nread; } } gst_buffer_unmap (buf, &map); gst_buffer_resize (buf, 0, count); *buffer = buf; return GST_FLOW_OK; stopped: { GST_DEBUG_OBJECT (object, "stop called"); gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); return GST_FLOW_FLUSHING; } select_error: { GST_ELEMENT_ERROR (object, RESOURCE, READ, (NULL), ("select error %d: %s (%d)", ret_val, g_strerror (errno), errno)); gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); return GST_FLOW_ERROR; } } 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"); retval = gst_dvbsrc_read_device (object, buffer_size, buf); 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, FALSE); 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, TRUE); 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; } if (!(src->poll = gst_poll_new (TRUE))) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ_WRITE, (NULL), ("could not create an fdset: %s (%d)", g_strerror (errno), errno)); /* unset filters also */ gst_dvbsrc_unset_pes_filters (src); close (src->fd_frontend); return FALSE; } else { gst_poll_fd_init (&src->poll_fd_dvr); src->poll_fd_dvr.fd = src->fd_dvr; gst_poll_add_fd (src->poll, &src->poll_fd_dvr); gst_poll_fd_ctl_read (src->poll, &src->poll_fd_dvr, TRUE); } return TRUE; } static gboolean gst_dvbsrc_stop (GstBaseSrc * bsrc) { GstDvbSrc *src = GST_DVBSRC (bsrc); gst_dvbsrc_close_devices (src); if (src->poll) { gst_poll_free (src->poll); src->poll = NULL; } return TRUE; } static gboolean gst_dvbsrc_unlock (GstBaseSrc * bsrc) { GstDvbSrc *src = GST_DVBSRC (bsrc); gst_poll_set_flushing (src->poll, TRUE); return TRUE; } static gboolean gst_dvbsrc_unlock_stop (GstBaseSrc * bsrc) { GstDvbSrc *src = GST_DVBSRC (bsrc); gst_poll_set_flushing (src->poll, FALSE); 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"); 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."); 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", 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 feparams.delsys.dvbs.fec = object->code_rate_hp; feparams.delsys.dvbs.modulation = object->modulation; feparams.delsys.dvbs.symbol_rate = sym_rate; #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", 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; #if DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR == 3 feparams.delsys.dvbs.fec = object->code_rate_hp; feparams.delsys.dvbs.modulation = object->modulation; feparams.delsys.dvbs.symbol_rate = sym_rate; #else feparams.u.qam.fec_inner = object->code_rate_hp; feparams.u.qam.modulation = object->modulation; feparams.u.qam.symbol_rate = sym_rate; #endif break; case FE_ATSC: GST_INFO_OBJECT (object, "Tuning ATSC to %d", freq); feparams.frequency = freq; #if DVB_API_VERSION == 3 && DVB_API_VERSION_MINOR == 3 feparams.delsys.atsc.modulation = object->modulation; #else feparams.u.vsb.modulation = object->modulation; #endif 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 < 50; 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) 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); }