| /* | |
| * parsechannels.c - | |
| * Copyright (C) 2008 Zaheer Abbas Merali | |
| * | |
| * Authors: | |
| * 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., 51 Franklin St, Fifth Floor, | |
| * Boston, MA 02110-1301, USA. | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include <glib.h> | |
| #include <glib-object.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <gst/gst.h> | |
| #include <gst/gst-i18n-plugin.h> | |
| #include "parsechannels.h" | |
| #include <linux/dvb/frontend.h> | |
| GST_DEBUG_CATEGORY_EXTERN (dvb_base_bin_debug); | |
| #define GST_CAT_DEFAULT dvb_base_bin_debug | |
| typedef enum | |
| { | |
| CHANNEL_CONF_FORMAT_NONE, | |
| CHANNEL_CONF_FORMAT_DVBV5, | |
| CHANNEL_CONF_FORMAT_ZAP | |
| } GstDvbChannelConfFormat; | |
| typedef gboolean (*GstDvbV5ChannelsConfPropSetFunction) (GstElement * | |
| dvbbasebin, const gchar * property, GKeyFile * kf, | |
| const gchar * channel_name, const gchar * key); | |
| typedef struct | |
| { | |
| const gchar *conf_property; | |
| const gchar *elem_property; | |
| GstDvbV5ChannelsConfPropSetFunction set_func; | |
| } GstDvbV5ChannelsConfToPropertyMap; | |
| static gboolean parse_and_configure_from_v5_conf_file (GstElement * dvbbasebin, | |
| const gchar * filename, const gchar * channel_name, GError ** error); | |
| static gboolean parse_and_configure_from_zap_conf_file (GstElement * dvbbasebin, | |
| const gchar * filename, const gchar * channel_name, GError ** error); | |
| static GstDvbChannelConfFormat detect_file_format (const gchar * filename); | |
| static gboolean gst_dvb_base_bin_conf_set_string (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_uint (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_int (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_inversion (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_guard (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_trans_mode (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_code_rate (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_delsys (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_hierarchy (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static gboolean gst_dvb_base_bin_conf_set_modulation (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key); | |
| static GHashTable *parse_channels_conf_from_zap_file (GstElement * dvbbasebin, | |
| const gchar * filename, GError ** error); | |
| static gboolean remove_channel_from_hash (gpointer key, gpointer value, | |
| gpointer user_data); | |
| static void destroy_channels_hash (GHashTable * channels); | |
| GstDvbV5ChannelsConfToPropertyMap dvbv5_prop_map[] = { | |
| {"SERVICE_ID", "program-numbers", gst_dvb_base_bin_conf_set_string}, | |
| {"FREQUENCY", "frequency", gst_dvb_base_bin_conf_set_uint}, | |
| {"BANDWIDTH_HZ", "bandwidth-hz", gst_dvb_base_bin_conf_set_uint}, | |
| {"INVERSION", "inversion", gst_dvb_base_bin_conf_set_inversion}, | |
| {"GUARD_INTERVAL", "guard", gst_dvb_base_bin_conf_set_guard}, | |
| {"TRANSMISSION_MODE", "trans-mode", gst_dvb_base_bin_conf_set_trans_mode}, | |
| {"HIERARCHY", "hierarchy", gst_dvb_base_bin_conf_set_hierarchy}, | |
| {"MODULATION", "modulation", gst_dvb_base_bin_conf_set_modulation}, | |
| {"CODE_RATE_HP", "code-rate-hp", gst_dvb_base_bin_conf_set_code_rate}, | |
| {"CODE_RATE_LP", "code-rate-lp", gst_dvb_base_bin_conf_set_code_rate}, | |
| {"ISDBT_LAYER_ENABLED", "isdbt-layer-enabled", | |
| gst_dvb_base_bin_conf_set_uint}, | |
| {"ISDBT_PARTIAL_RECEPTION", "isdbt-partial-reception", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_SOUND_BROADCASTING", "isdbt-sound-broadcasting", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_SB_SUBCHANNEL_ID", "isdbt-sb-subchannel-id", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_SB_SEGMENT_IDX", "isdbt-sb-segment-idx", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_SB_SEGMENT_COUNT", "isdbt-sb-segment-count", gst_dvb_base_bin_conf_set_int}, /* Range in files start from 0, property starts from 1 */ | |
| {"ISDBT_LAYERA_FEC", "isdbt-layera-fec", gst_dvb_base_bin_conf_set_code_rate}, | |
| {"ISDBT_LAYERA_MODULATION", "isdbt-layera-modulation", | |
| gst_dvb_base_bin_conf_set_modulation}, | |
| {"ISDBT_LAYERA_SEGMENT_COUNT", "isdbt-layera-segment-count", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_LAYERA_TIME_INTERLEAVING", "isdbt-layera-time-interleaving", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_LAYERB_FEC", "isdbt-layerb-fec", gst_dvb_base_bin_conf_set_code_rate}, | |
| {"ISDBT_LAYERB_MODULATION", "isdbt-layerb-modulation", | |
| gst_dvb_base_bin_conf_set_modulation}, | |
| {"ISDBT_LAYERB_SEGMENT_COUNT", "isdbt-layerb-segment-count", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_LAYERB_TIME_INTERLEAVING", "isdbt-layerb-time-interleaving", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_LAYERC_FEC", "isdbt-layerc-fec", gst_dvb_base_bin_conf_set_code_rate}, | |
| {"ISDBT_LAYERC_MODULATION", "isdbt-layerc-modulation", | |
| gst_dvb_base_bin_conf_set_modulation}, | |
| {"ISDBT_LAYERC_SEGMENT_COUNT", "isdbt-layerc-segment-count", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"ISDBT_LAYERC_TIME_INTERLEAVING", "isdbt-layerc-time-interleaving", | |
| gst_dvb_base_bin_conf_set_int}, | |
| {"DELIVERY_SYSTEM", "delsys", gst_dvb_base_bin_conf_set_delsys}, | |
| {NULL,} | |
| }; | |
| /* TODO: | |
| * Store the channels hash table around instead of constantly parsing it | |
| * Detect when the file changed on disk | |
| */ | |
| static gint | |
| gst_dvb_base_bin_find_string_in_array (const gchar ** array, const gchar * str) | |
| { | |
| gint i = 0; | |
| const gchar *cur; | |
| while ((cur = array[i])) { | |
| if (strcmp (cur, str) == 0) | |
| return i; | |
| i++; | |
| } | |
| return -1; | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_property_from_string_array (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key, const gchar ** strings, gint default_value) | |
| { | |
| gchar *str; | |
| gint v; | |
| str = g_key_file_get_string (kf, channel_name, key, NULL); | |
| v = gst_dvb_base_bin_find_string_in_array (strings, str); | |
| if (v == -1) { | |
| GST_WARNING_OBJECT (dvbbasebin, "Unexpected value '%s' for property " | |
| "'%s', using default: '%d'", str, property, default_value); | |
| v = default_value; | |
| } | |
| g_free (str); | |
| g_object_set (dvbbasebin, property, v, NULL); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_string (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| gchar *str; | |
| str = g_key_file_get_string (kf, channel_name, key, NULL); | |
| if (!str) { | |
| GST_WARNING_OBJECT (dvbbasebin, | |
| "Could not get value for '%s' on channel '%s'", key, channel_name); | |
| return FALSE; | |
| } | |
| g_object_set (dvbbasebin, property, str, NULL); | |
| g_free (str); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_uint (GstElement * dvbbasebin, const gchar * property, | |
| GKeyFile * kf, const gchar * channel_name, const gchar * key) | |
| { | |
| guint64 v; | |
| v = g_key_file_get_uint64 (kf, channel_name, key, NULL); | |
| if (!v) { | |
| GST_WARNING_OBJECT (dvbbasebin, | |
| "Could not get value for '%s' on channel '%s'", key, channel_name); | |
| return FALSE; | |
| } | |
| g_object_set (dvbbasebin, property, (guint) v, NULL); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_int (GstElement * dvbbasebin, const gchar * property, | |
| GKeyFile * kf, const gchar * channel_name, const gchar * key) | |
| { | |
| gint v; | |
| v = g_key_file_get_integer (kf, channel_name, key, NULL); | |
| if (!v) { | |
| GST_WARNING_OBJECT (dvbbasebin, | |
| "Could not get value for '%s' on channel '%s'", key, channel_name); | |
| return FALSE; | |
| } | |
| g_object_set (dvbbasebin, property, v, NULL); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_inversion (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| gchar *str; | |
| gint v; | |
| str = g_key_file_get_string (kf, channel_name, key, NULL); | |
| if (!str) { | |
| GST_WARNING_OBJECT (dvbbasebin, | |
| "Could not get value for '%s' on channel '%s'", key, channel_name); | |
| return FALSE; | |
| } | |
| if (strcmp (str, "AUTO") == 0) | |
| v = 2; | |
| else if (strcmp (str, "ON") == 0) | |
| v = 1; | |
| else | |
| v = 0; /* OFF */ | |
| g_free (str); | |
| g_object_set (dvbbasebin, property, v, NULL); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_guard (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| const gchar *guards[] = { | |
| "1/32", "1/16", "1/8", "1/4", "auto", | |
| "1/128", "19/128", "19/256", | |
| "PN420", "PN595", "PN945", NULL | |
| }; | |
| return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin, | |
| property, kf, channel_name, key, guards, 4); | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_trans_mode (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| const gchar *trans_modes[] = { | |
| "2K", "8K", "AUTO", "4K", "1K", | |
| "16K", "32K", "C1", "C3780", NULL | |
| }; | |
| return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin, | |
| property, kf, channel_name, key, trans_modes, 2); | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_code_rate (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| const gchar *code_rates[] = { | |
| "NONE", "1/2", "2/3", "3/4", "4/5", | |
| "5/6", "6/7", "7/8", "8/9", "AUTO", | |
| "3/5", "9/10", "2/5", NULL | |
| }; | |
| return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin, | |
| property, kf, channel_name, key, code_rates, 9); | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_delsys (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| const gchar *delsys[] = { | |
| "UNDEFINED", "DVBCA", "DVBCB", "DVBT", "DSS", | |
| "DVBS", "DVBS2", "DVBH", "ISDBT", "ISDBS", | |
| "ISDBC", "ATSC", "ATSCMH", "DTMB", "CMMB", | |
| "DAB", "DVBT2", "TURBO", "DVBCC", NULL | |
| }; | |
| return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin, | |
| property, kf, channel_name, key, delsys, 0); | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_hierarchy (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| const gchar *hierarchies[] = { | |
| "NONE", "1", "2", "4", "AUTO", NULL | |
| }; | |
| return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin, | |
| property, kf, channel_name, key, hierarchies, 4); | |
| } | |
| static gboolean | |
| gst_dvb_base_bin_conf_set_modulation (GstElement * dvbbasebin, | |
| const gchar * property, GKeyFile * kf, const gchar * channel_name, | |
| const gchar * key) | |
| { | |
| const gchar *modulations[] = { | |
| "QPSK", "QAM/16", "QAM/32", "QAM/64", | |
| "QAM/128", "QAM/256", "QAM/AUTO", "VSB/8", | |
| "VSB/16", "PSK/8", "APSK/16", "APSK/32", | |
| "DQPSK", "QAM/4_NR", NULL | |
| }; | |
| return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin, | |
| property, kf, channel_name, key, modulations, 6); | |
| } | |
| /* FIXME: is channel_name guaranteed to be ASCII or UTF-8? */ | |
| static gboolean | |
| parse_and_configure_from_v5_conf_file (GstElement * dvbbasebin, | |
| const gchar * filename, const gchar * channel_name, GError ** error) | |
| { | |
| GKeyFile *keyfile; | |
| gchar **keys, **keys_p; | |
| GError *err = NULL; | |
| keyfile = g_key_file_new (); | |
| if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &err)) | |
| goto load_error; | |
| if (!g_key_file_has_group (keyfile, channel_name)) | |
| goto unknown_channel; | |
| keys = g_key_file_get_keys (keyfile, channel_name, NULL, &err); | |
| if (!keys) | |
| goto no_properties; | |
| keys_p = keys; | |
| while (*keys_p) { | |
| const gchar *k = *keys_p; | |
| const GstDvbV5ChannelsConfToPropertyMap *map_entry = dvbv5_prop_map; | |
| gboolean property_found = FALSE; | |
| GST_LOG_OBJECT (dvbbasebin, "Setting property %s", k); | |
| while (map_entry->conf_property) { | |
| if (strcmp (map_entry->conf_property, k) == 0) { | |
| if (!map_entry->set_func (dvbbasebin, map_entry->elem_property, keyfile, | |
| channel_name, k)) | |
| goto property_error; | |
| property_found = TRUE; | |
| break; | |
| } | |
| map_entry++; | |
| } | |
| if (!property_found) | |
| GST_WARNING_OBJECT (dvbbasebin, "Failed to map property '%s'", k); | |
| keys_p++; | |
| } | |
| GST_DEBUG_OBJECT (dvbbasebin, "Successfully parsed channel configuration " | |
| "file '%s'", filename); | |
| g_strfreev (keys); | |
| g_key_file_unref (keyfile); | |
| return TRUE; | |
| load_error: | |
| if ((err->domain == G_FILE_ERROR && err->code == G_FILE_ERROR_NOENT) || | |
| (err->domain == G_KEY_FILE_ERROR | |
| && err->code == G_KEY_FILE_ERROR_NOT_FOUND)) { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND, | |
| _("Couldn't find channel configuration file")); | |
| } else { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, | |
| _("Couldn't load channel configuration file: '%s'"), err->message); | |
| } | |
| g_clear_error (&err); | |
| return FALSE; | |
| unknown_channel: | |
| { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND, | |
| _("Couldn't find details for channel '%s'"), channel_name); | |
| g_key_file_unref (keyfile); | |
| g_clear_error (&err); | |
| return FALSE; | |
| } | |
| no_properties: | |
| { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND, | |
| _("No properties for channel '%s'"), channel_name); | |
| g_key_file_unref (keyfile); | |
| g_clear_error (&err); | |
| return FALSE; | |
| } | |
| property_error: | |
| { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, | |
| _("Failed to set properties for channel '%s'"), channel_name); | |
| g_key_file_unref (keyfile); | |
| g_clear_error (&err); | |
| return FALSE; | |
| } | |
| } | |
| static GHashTable * | |
| parse_channels_conf_from_zap_file (GstElement * dvbbasebin, | |
| const gchar * filename, GError ** error) | |
| { | |
| gchar *contents; | |
| gchar **lines; | |
| gchar *line; | |
| gchar **fields; | |
| int i, parsedchannels = 0; | |
| GHashTable *res; | |
| GError *err = NULL; | |
| const gchar *terrestrial[] = { "inversion", "bandwidth", | |
| "code-rate-hp", "code-rate-lp", "modulation", "transmission-mode", | |
| "guard", "hierarchy" | |
| }; | |
| const gchar *satellite[] = { "polarity", "diseqc-source", | |
| "symbol-rate" | |
| }; | |
| const gchar *cable[] = { "inversion", "symbol-rate", "code-rate-hp", | |
| "modulation" | |
| }; | |
| GST_INFO_OBJECT (dvbbasebin, "parsing '%s'", filename); | |
| if (!g_file_get_contents (filename, &contents, NULL, &err)) | |
| goto open_fail; | |
| lines = g_strsplit (contents, "\n", 0); | |
| res = g_hash_table_new (g_str_hash, g_str_equal); | |
| i = 0; | |
| line = lines[0]; | |
| while (line != NULL) { | |
| GHashTable *params; | |
| int j, numfields; | |
| if (line[0] == '#') | |
| goto next_line; | |
| params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); | |
| fields = g_strsplit (line, ":", 0); | |
| numfields = g_strv_length (fields); | |
| switch (numfields) { | |
| case 13: /* terrestrial */ | |
| g_hash_table_insert (params, g_strdup ("type"), | |
| g_strdup ("terrestrial")); | |
| for (j = 2; j <= 9; j++) { | |
| g_hash_table_insert (params, g_strdup (terrestrial[j - 2]), | |
| g_strdup (fields[j])); | |
| } | |
| g_hash_table_insert (params, g_strdup ("frequency"), | |
| g_strdup (fields[1])); | |
| break; | |
| case 9: /* cable */ | |
| g_hash_table_insert (params, g_strdup ("type"), g_strdup ("cable")); | |
| for (j = 2; j <= 5; j++) { | |
| g_hash_table_insert (params, g_strdup (cable[j - 2]), | |
| g_strdup (fields[j])); | |
| } | |
| g_hash_table_insert (params, g_strdup ("frequency"), | |
| g_strdup (fields[1])); | |
| break; | |
| case 8: /* satellite */ | |
| g_hash_table_insert (params, g_strdup ("type"), g_strdup ("satellite")); | |
| for (j = 2; j <= 4; j++) { | |
| g_hash_table_insert (params, g_strdup (satellite[j - 2]), | |
| g_strdup (fields[j])); | |
| } | |
| /* Some ZAP format variations store freqs in MHz | |
| * but we internally use kHz for DVB-S/S2. */ | |
| if (strlen (fields[1]) < 6) { | |
| g_hash_table_insert (params, g_strdup ("frequency"), | |
| g_strdup_printf ("%d", atoi (fields[1]) * 1000)); | |
| } else { | |
| g_hash_table_insert (params, g_strdup ("frequency"), | |
| g_strdup_printf ("%d", atoi (fields[1]))); | |
| } | |
| break; | |
| case 6: /* atsc (vsb/qam) */ | |
| g_hash_table_insert (params, g_strdup ("type"), g_strdup ("atsc")); | |
| g_hash_table_insert (params, g_strdup ("modulation"), | |
| g_strdup (fields[2])); | |
| g_hash_table_insert (params, g_strdup ("frequency"), | |
| g_strdup (fields[1])); | |
| break; | |
| default: | |
| goto not_parsed; | |
| } | |
| /* parsed */ | |
| g_hash_table_insert (params, g_strdup ("sid"), | |
| g_strdup (fields[numfields - 1])); | |
| g_hash_table_insert (res, g_strdup (fields[0]), params); | |
| parsedchannels++; | |
| not_parsed: | |
| g_strfreev (fields); | |
| next_line: | |
| line = lines[++i]; | |
| } | |
| g_strfreev (lines); | |
| g_free (contents); | |
| if (parsedchannels == 0) | |
| goto no_channels; | |
| return res; | |
| open_fail: | |
| if (err->code == G_FILE_ERROR_NOENT) { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND, | |
| _("Couldn't find channel configuration file: '%s'"), err->message); | |
| } else { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ, | |
| _("Couldn't load channel configuration file: '%s'"), err->message); | |
| } | |
| g_clear_error (&err); | |
| return NULL; | |
| no_channels: | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED, | |
| _("Channel configuration file doesn't contain any channels")); | |
| g_hash_table_unref (res); | |
| return NULL; | |
| } | |
| static gboolean | |
| remove_channel_from_hash (gpointer key, gpointer value, gpointer user_data) | |
| { | |
| g_free (key); | |
| if (value) | |
| g_hash_table_destroy ((GHashTable *) value); | |
| return TRUE; | |
| } | |
| static void | |
| destroy_channels_hash (GHashTable * channels) | |
| { | |
| g_hash_table_foreach_remove (channels, remove_channel_from_hash, NULL); | |
| } | |
| /* FIXME: is channel_name guaranteed to be ASCII or UTF-8? */ | |
| static gboolean | |
| parse_and_configure_from_zap_conf_file (GstElement * dvbbasebin, | |
| const gchar * filename, const gchar * channel_name, GError ** error) | |
| { | |
| gboolean ret = FALSE; | |
| GHashTable *channels, *params; | |
| gchar *type; | |
| /* Assumptions are made here about a format that is loosely | |
| * defined. Particularly, we assume a given delivery system | |
| * out of counting the number of fields per line. dvbsrc has | |
| * smarter code to auto-detect a delivery system based on | |
| * known-correct combinations of parameters so if you ever | |
| * encounter cases where the delivery system is being | |
| * wrongly set here, just remove the offending | |
| * g_object_set line and let dvbsrc work his magic out. */ | |
| channels = parse_channels_conf_from_zap_file (dvbbasebin, filename, error); | |
| if (!channels) | |
| goto beach; | |
| params = g_hash_table_lookup (channels, channel_name); | |
| if (!params) | |
| goto unknown_channel; | |
| g_object_set (dvbbasebin, "program-numbers", | |
| g_hash_table_lookup (params, "sid"), NULL); | |
| /* check if it is terrestrial or satellite */ | |
| g_object_set (dvbbasebin, "frequency", | |
| atoi (g_hash_table_lookup (params, "frequency")), NULL); | |
| type = g_hash_table_lookup (params, "type"); | |
| if (strcmp (type, "terrestrial") == 0) { | |
| gchar *val; | |
| val = g_hash_table_lookup (params, "inversion"); | |
| if (strcmp (val, "INVERSION_OFF") == 0) | |
| g_object_set (dvbbasebin, "inversion", INVERSION_OFF, NULL); | |
| else if (strcmp (val, "INVERSION_ON") == 0) | |
| g_object_set (dvbbasebin, "inversion", INVERSION_ON, NULL); | |
| else | |
| g_object_set (dvbbasebin, "inversion", INVERSION_AUTO, NULL); | |
| val = g_hash_table_lookup (params, "bandwidth"); | |
| if (strcmp (val, "BANDWIDTH_8_MHZ") == 0) | |
| g_object_set (dvbbasebin, "bandwidth", 0, NULL); | |
| else if (strcmp (val, "BANDWIDTH_7_MHZ") == 0) | |
| g_object_set (dvbbasebin, "bandwidth", 1, NULL); | |
| else if (strcmp (val, "BANDWIDTH_6_MHZ") == 0) | |
| g_object_set (dvbbasebin, "bandwidth", 2, NULL); | |
| else if (strcmp (val, "BANDWIDTH_5_MHZ") == 0) | |
| g_object_set (dvbbasebin, "bandwidth", 4, NULL); | |
| else if (strcmp (val, "BANDWIDTH_10_MHZ") == 0) | |
| g_object_set (dvbbasebin, "bandwidth", 5, NULL); | |
| else if (strcmp (val, "BANDWIDTH_1_712_MHZ") == 0) | |
| g_object_set (dvbbasebin, "bandwidth", 6, NULL); | |
| else | |
| g_object_set (dvbbasebin, "bandwidth", 3, NULL); | |
| val = g_hash_table_lookup (params, "code-rate-hp"); | |
| if (strcmp (val, "FEC_NONE") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 0, NULL); | |
| else if (strcmp (val, "FEC_1_2") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 1, NULL); | |
| else if (strcmp (val, "FEC_2_3") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 2, NULL); | |
| else if (strcmp (val, "FEC_3_4") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 3, NULL); | |
| else if (strcmp (val, "FEC_4_5") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 4, NULL); | |
| else if (strcmp (val, "FEC_5_6") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 5, NULL); | |
| else if (strcmp (val, "FEC_6_7") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 6, NULL); | |
| else if (strcmp (val, "FEC_7_8") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 7, NULL); | |
| else if (strcmp (val, "FEC_8_9") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 8, NULL); | |
| else | |
| g_object_set (dvbbasebin, "code-rate-hp", 9, NULL); | |
| val = g_hash_table_lookup (params, "code-rate-lp"); | |
| if (strcmp (val, "FEC_NONE") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 0, NULL); | |
| else if (strcmp (val, "FEC_1_2") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 1, NULL); | |
| else if (strcmp (val, "FEC_2_3") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 2, NULL); | |
| else if (strcmp (val, "FEC_3_4") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 3, NULL); | |
| else if (strcmp (val, "FEC_4_5") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 4, NULL); | |
| else if (strcmp (val, "FEC_5_6") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 5, NULL); | |
| else if (strcmp (val, "FEC_6_7") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 6, NULL); | |
| else if (strcmp (val, "FEC_7_8") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 7, NULL); | |
| else if (strcmp (val, "FEC_8_9") == 0) | |
| g_object_set (dvbbasebin, "code-rate-lp", 8, NULL); | |
| else | |
| g_object_set (dvbbasebin, "code-rate-lp", 9, NULL); | |
| val = g_hash_table_lookup (params, "modulation"); | |
| if (strcmp (val, "QPSK") == 0) | |
| g_object_set (dvbbasebin, "modulation", 0, NULL); | |
| else if (strcmp (val, "QAM_16") == 0) | |
| g_object_set (dvbbasebin, "modulation", 1, NULL); | |
| else if (strcmp (val, "QAM_32") == 0) | |
| g_object_set (dvbbasebin, "modulation", 2, NULL); | |
| else if (strcmp (val, "QAM_64") == 0) | |
| g_object_set (dvbbasebin, "modulation", 3, NULL); | |
| else if (strcmp (val, "QAM_128") == 0) | |
| g_object_set (dvbbasebin, "modulation", 4, NULL); | |
| else if (strcmp (val, "QAM_256") == 0) | |
| g_object_set (dvbbasebin, "modulation", 5, NULL); | |
| else | |
| g_object_set (dvbbasebin, "modulation", 6, NULL); | |
| val = g_hash_table_lookup (params, "transmission-mode"); | |
| if (strcmp (val, "TRANSMISSION_MODE_2K") == 0) | |
| g_object_set (dvbbasebin, "trans-mode", 0, NULL); | |
| else if (strcmp (val, "TRANSMISSION_MODE_8K") == 0) | |
| g_object_set (dvbbasebin, "trans-mode", 1, NULL); | |
| else | |
| g_object_set (dvbbasebin, "trans-mode", 2, NULL); | |
| val = g_hash_table_lookup (params, "guard"); | |
| if (strcmp (val, "GUARD_INTERVAL_1_32") == 0) | |
| g_object_set (dvbbasebin, "guard", 0, NULL); | |
| else if (strcmp (val, "GUARD_INTERVAL_1_16") == 0) | |
| g_object_set (dvbbasebin, "guard", 1, NULL); | |
| else if (strcmp (val, "GUARD_INTERVAL_1_8") == 0) | |
| g_object_set (dvbbasebin, "guard", 2, NULL); | |
| else if (strcmp (val, "GUARD_INTERVAL_1_4") == 0) | |
| g_object_set (dvbbasebin, "guard", 3, NULL); | |
| else | |
| g_object_set (dvbbasebin, "guard", 4, NULL); | |
| val = g_hash_table_lookup (params, "hierarchy"); | |
| if (strcmp (val, "HIERARCHY_NONE") == 0) | |
| g_object_set (dvbbasebin, "hierarchy", 0, NULL); | |
| else if (strcmp (val, "HIERARCHY_1") == 0) | |
| g_object_set (dvbbasebin, "hierarchy", 1, NULL); | |
| else if (strcmp (val, "HIERARCHY_2") == 0) | |
| g_object_set (dvbbasebin, "hierarchy", 2, NULL); | |
| else if (strcmp (val, "HIERARCHY_4") == 0) | |
| g_object_set (dvbbasebin, "hierarchy", 3, NULL); | |
| else | |
| g_object_set (dvbbasebin, "hierarchy", 4, NULL); | |
| ret = TRUE; | |
| } else if (strcmp (type, "satellite") == 0) { | |
| gchar *val; | |
| ret = TRUE; | |
| g_object_set (dvbbasebin, "delsys", SYS_DVBS, NULL); | |
| val = g_hash_table_lookup (params, "polarity"); | |
| if (val) | |
| g_object_set (dvbbasebin, "polarity", val, NULL); | |
| else | |
| ret = FALSE; | |
| val = g_hash_table_lookup (params, "diseqc-source"); | |
| if (val) | |
| g_object_set (dvbbasebin, "diseqc-source", atoi (val), NULL); | |
| val = g_hash_table_lookup (params, "symbol-rate"); | |
| if (val) | |
| g_object_set (dvbbasebin, "symbol-rate", atoi (val), NULL); | |
| else | |
| ret = FALSE; | |
| } else if (strcmp (type, "cable") == 0) { | |
| gchar *val; | |
| g_object_set (dvbbasebin, "delsys", SYS_DVBC_ANNEX_A, NULL); | |
| ret = TRUE; | |
| val = g_hash_table_lookup (params, "symbol-rate"); | |
| if (val) | |
| g_object_set (dvbbasebin, "symbol-rate", atoi (val) / 1000, NULL); | |
| val = g_hash_table_lookup (params, "modulation"); | |
| if (strcmp (val, "QPSK") == 0) | |
| g_object_set (dvbbasebin, "modulation", 0, NULL); | |
| else if (strcmp (val, "QAM_16") == 0) | |
| g_object_set (dvbbasebin, "modulation", 1, NULL); | |
| else if (strcmp (val, "QAM_32") == 0) | |
| g_object_set (dvbbasebin, "modulation", 2, NULL); | |
| else if (strcmp (val, "QAM_64") == 0) | |
| g_object_set (dvbbasebin, "modulation", 3, NULL); | |
| else if (strcmp (val, "QAM_128") == 0) | |
| g_object_set (dvbbasebin, "modulation", 4, NULL); | |
| else if (strcmp (val, "QAM_256") == 0) | |
| g_object_set (dvbbasebin, "modulation", 5, NULL); | |
| else | |
| g_object_set (dvbbasebin, "modulation", 6, NULL); | |
| val = g_hash_table_lookup (params, "code-rate-hp"); | |
| if (strcmp (val, "FEC_NONE") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 0, NULL); | |
| else if (strcmp (val, "FEC_1_2") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 1, NULL); | |
| else if (strcmp (val, "FEC_2_3") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 2, NULL); | |
| else if (strcmp (val, "FEC_3_4") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 3, NULL); | |
| else if (strcmp (val, "FEC_4_5") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 4, NULL); | |
| else if (strcmp (val, "FEC_5_6") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 5, NULL); | |
| else if (strcmp (val, "FEC_6_7") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 6, NULL); | |
| else if (strcmp (val, "FEC_7_8") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 7, NULL); | |
| else if (strcmp (val, "FEC_8_9") == 0) | |
| g_object_set (dvbbasebin, "code-rate-hp", 8, NULL); | |
| else | |
| g_object_set (dvbbasebin, "code-rate-hp", 9, NULL); | |
| val = g_hash_table_lookup (params, "inversion"); | |
| if (strcmp (val, "INVERSION_OFF") == 0) | |
| g_object_set (dvbbasebin, "inversion", 0, NULL); | |
| else if (strcmp (val, "INVERSION_ON") == 0) | |
| g_object_set (dvbbasebin, "inversion", 1, NULL); | |
| else | |
| g_object_set (dvbbasebin, "inversion", 2, NULL); | |
| } else if (strcmp (type, "atsc") == 0) { | |
| gchar *val; | |
| ret = TRUE; | |
| g_object_set (dvbbasebin, "delsys", SYS_ATSC, NULL); | |
| val = g_hash_table_lookup (params, "modulation"); | |
| if (strcmp (val, "QAM_64") == 0) | |
| g_object_set (dvbbasebin, "modulation", 3, NULL); | |
| else if (strcmp (val, "QAM_256") == 0) | |
| g_object_set (dvbbasebin, "modulation", 5, NULL); | |
| else if (strcmp (val, "8VSB") == 0) | |
| g_object_set (dvbbasebin, "modulation", 7, NULL); | |
| else if (strcmp (val, "16VSB") == 0) | |
| g_object_set (dvbbasebin, "modulation", 8, NULL); | |
| else | |
| ret = FALSE; | |
| } | |
| destroy_channels_hash (channels); | |
| beach: | |
| return ret; | |
| unknown_channel: | |
| { | |
| g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND, | |
| _("Couldn't find details for channel '%s'"), channel_name); | |
| destroy_channels_hash (channels); | |
| return FALSE; | |
| } | |
| } | |
| static GstDvbChannelConfFormat | |
| detect_file_format (const gchar * filename) | |
| { | |
| gchar *contents; | |
| gchar **lines; | |
| gchar **line; | |
| GstDvbChannelConfFormat ret = CHANNEL_CONF_FORMAT_NONE; | |
| if (!g_file_get_contents (filename, &contents, NULL, NULL)) | |
| return ret; | |
| lines = g_strsplit (contents, "\n", 0); | |
| line = lines; | |
| while (*line) { | |
| if (g_str_has_prefix (*line, "[") && g_str_has_suffix (*line, "]")) { | |
| ret = CHANNEL_CONF_FORMAT_DVBV5; | |
| break; | |
| } else if (g_strrstr (*line, ":")) { | |
| ret = CHANNEL_CONF_FORMAT_ZAP; | |
| break; | |
| } | |
| line++; | |
| } | |
| g_strfreev (lines); | |
| g_free (contents); | |
| return ret; | |
| } | |
| gboolean | |
| set_properties_for_channel (GstElement * dvbbasebin, | |
| const gchar * channel_name, GError ** error) | |
| { | |
| gboolean ret = FALSE; | |
| gchar *filename; | |
| filename = g_strdup (g_getenv ("GST_DVB_CHANNELS_CONF")); | |
| if (filename == NULL) { | |
| filename = g_build_filename (g_get_user_config_dir (), | |
| "gstreamer-" GST_API_VERSION, "dvb-channels.conf", NULL); | |
| } | |
| switch (detect_file_format (filename)) { | |
| case CHANNEL_CONF_FORMAT_DVBV5: | |
| if (!parse_and_configure_from_v5_conf_file (dvbbasebin, filename, | |
| channel_name, error)) { | |
| GST_WARNING_OBJECT (dvbbasebin, "Problem finding information for " | |
| "channel '%s' in configuration file '%s'", channel_name, filename); | |
| } else { | |
| GST_INFO_OBJECT (dvbbasebin, "Parsed libdvbv5 channel configuration " | |
| "file"); | |
| ret = TRUE; | |
| } | |
| break; | |
| case CHANNEL_CONF_FORMAT_ZAP: | |
| if (!parse_and_configure_from_zap_conf_file (dvbbasebin, filename, | |
| channel_name, error)) { | |
| GST_WARNING_OBJECT (dvbbasebin, "Problem finding information for " | |
| "channel '%s' in configuration file '%s'", channel_name, filename); | |
| } else { | |
| GST_INFO_OBJECT (dvbbasebin, "Parsed ZAP channel configuration file"); | |
| ret = TRUE; | |
| } | |
| break; | |
| default: | |
| GST_WARNING_OBJECT (dvbbasebin, "Unknown configuration file format. " | |
| "Can not get parameters for channel"); | |
| } | |
| g_free (filename); | |
| return ret; | |
| } |