1 /*
2  * parsechannels.c -
3  * Copyright (C) 2008 Zaheer Abbas Merali
4  *
5  * Authors:
6  *   Zaheer Abbas Merali <zaheerabbas at merali dot org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <glib.h>
29 #include <glib-object.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <gst/gst.h>
33 
34 #include <gst/gst-i18n-plugin.h>
35 
36 #include "parsechannels.h"
37 
38 #include <linux/dvb/frontend.h>
39 
40 GST_DEBUG_CATEGORY_EXTERN (dvb_base_bin_debug);
41 #define GST_CAT_DEFAULT dvb_base_bin_debug
42 
43 typedef enum
44 {
45   CHANNEL_CONF_FORMAT_NONE,
46   CHANNEL_CONF_FORMAT_DVBV5,
47   CHANNEL_CONF_FORMAT_ZAP
48 } GstDvbChannelConfFormat;
49 
50 typedef gboolean (*GstDvbV5ChannelsConfPropSetFunction) (GstElement *
51     dvbbasebin, const gchar * property, GKeyFile * kf,
52     const gchar * channel_name, const gchar * key);
53 
54 typedef struct
55 {
56   const gchar *conf_property;
57   const gchar *elem_property;
58   GstDvbV5ChannelsConfPropSetFunction set_func;
59 } GstDvbV5ChannelsConfToPropertyMap;
60 
61 static gboolean parse_and_configure_from_v5_conf_file (GstElement * dvbbasebin,
62     const gchar * filename, const gchar * channel_name, GError ** error);
63 static gboolean parse_and_configure_from_zap_conf_file (GstElement * dvbbasebin,
64     const gchar * filename, const gchar * channel_name, GError ** error);
65 static GstDvbChannelConfFormat detect_file_format (const gchar * filename);
66 
67 static gboolean gst_dvb_base_bin_conf_set_string (GstElement * dvbbasebin,
68     const gchar * property, GKeyFile * kf, const gchar * channel_name,
69     const gchar * key);
70 static gboolean gst_dvb_base_bin_conf_set_uint (GstElement * dvbbasebin,
71     const gchar * property, GKeyFile * kf, const gchar * channel_name,
72     const gchar * key);
73 static gboolean gst_dvb_base_bin_conf_set_int (GstElement * dvbbasebin,
74     const gchar * property, GKeyFile * kf, const gchar * channel_name,
75     const gchar * key);
76 static gboolean gst_dvb_base_bin_conf_set_inversion (GstElement * dvbbasebin,
77     const gchar * property, GKeyFile * kf, const gchar * channel_name,
78     const gchar * key);
79 static gboolean gst_dvb_base_bin_conf_set_guard (GstElement * dvbbasebin,
80     const gchar * property, GKeyFile * kf, const gchar * channel_name,
81     const gchar * key);
82 static gboolean gst_dvb_base_bin_conf_set_trans_mode (GstElement * dvbbasebin,
83     const gchar * property, GKeyFile * kf, const gchar * channel_name,
84     const gchar * key);
85 static gboolean gst_dvb_base_bin_conf_set_code_rate (GstElement * dvbbasebin,
86     const gchar * property, GKeyFile * kf, const gchar * channel_name,
87     const gchar * key);
88 static gboolean gst_dvb_base_bin_conf_set_delsys (GstElement * dvbbasebin,
89     const gchar * property, GKeyFile * kf, const gchar * channel_name,
90     const gchar * key);
91 static gboolean gst_dvb_base_bin_conf_set_hierarchy (GstElement * dvbbasebin,
92     const gchar * property, GKeyFile * kf, const gchar * channel_name,
93     const gchar * key);
94 static gboolean gst_dvb_base_bin_conf_set_modulation (GstElement * dvbbasebin,
95     const gchar * property, GKeyFile * kf, const gchar * channel_name,
96     const gchar * key);
97 static GHashTable *parse_channels_conf_from_zap_file (GstElement * dvbbasebin,
98     const gchar * filename, GError ** error);
99 static gboolean remove_channel_from_hash (gpointer key, gpointer value,
100     gpointer user_data);
101 static void destroy_channels_hash (GHashTable * channels);
102 
103 GstDvbV5ChannelsConfToPropertyMap dvbv5_prop_map[] = {
104   {"SERVICE_ID", "program-numbers", gst_dvb_base_bin_conf_set_string},
105   {"FREQUENCY", "frequency", gst_dvb_base_bin_conf_set_uint},
106   {"BANDWIDTH_HZ", "bandwidth-hz", gst_dvb_base_bin_conf_set_uint},
107   {"INVERSION", "inversion", gst_dvb_base_bin_conf_set_inversion},
108   {"GUARD_INTERVAL", "guard", gst_dvb_base_bin_conf_set_guard},
109   {"TRANSMISSION_MODE", "trans-mode", gst_dvb_base_bin_conf_set_trans_mode},
110   {"HIERARCHY", "hierarchy", gst_dvb_base_bin_conf_set_hierarchy},
111   {"MODULATION", "modulation", gst_dvb_base_bin_conf_set_modulation},
112   {"CODE_RATE_HP", "code-rate-hp", gst_dvb_base_bin_conf_set_code_rate},
113   {"CODE_RATE_LP", "code-rate-lp", gst_dvb_base_bin_conf_set_code_rate},
114   {"ISDBT_LAYER_ENABLED", "isdbt-layer-enabled",
115       gst_dvb_base_bin_conf_set_uint},
116   {"ISDBT_PARTIAL_RECEPTION", "isdbt-partial-reception",
117       gst_dvb_base_bin_conf_set_int},
118   {"ISDBT_SOUND_BROADCASTING", "isdbt-sound-broadcasting",
119       gst_dvb_base_bin_conf_set_int},
120   {"ISDBT_SB_SUBCHANNEL_ID", "isdbt-sb-subchannel-id",
121       gst_dvb_base_bin_conf_set_int},
122   {"ISDBT_SB_SEGMENT_IDX", "isdbt-sb-segment-idx",
123       gst_dvb_base_bin_conf_set_int},
124   {"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 */
125   {"ISDBT_LAYERA_FEC", "isdbt-layera-fec", gst_dvb_base_bin_conf_set_code_rate},
126   {"ISDBT_LAYERA_MODULATION", "isdbt-layera-modulation",
127       gst_dvb_base_bin_conf_set_modulation},
128   {"ISDBT_LAYERA_SEGMENT_COUNT", "isdbt-layera-segment-count",
129       gst_dvb_base_bin_conf_set_int},
130   {"ISDBT_LAYERA_TIME_INTERLEAVING", "isdbt-layera-time-interleaving",
131       gst_dvb_base_bin_conf_set_int},
132   {"ISDBT_LAYERB_FEC", "isdbt-layerb-fec", gst_dvb_base_bin_conf_set_code_rate},
133   {"ISDBT_LAYERB_MODULATION", "isdbt-layerb-modulation",
134       gst_dvb_base_bin_conf_set_modulation},
135   {"ISDBT_LAYERB_SEGMENT_COUNT", "isdbt-layerb-segment-count",
136       gst_dvb_base_bin_conf_set_int},
137   {"ISDBT_LAYERB_TIME_INTERLEAVING", "isdbt-layerb-time-interleaving",
138       gst_dvb_base_bin_conf_set_int},
139   {"ISDBT_LAYERC_FEC", "isdbt-layerc-fec", gst_dvb_base_bin_conf_set_code_rate},
140   {"ISDBT_LAYERC_MODULATION", "isdbt-layerc-modulation",
141       gst_dvb_base_bin_conf_set_modulation},
142   {"ISDBT_LAYERC_SEGMENT_COUNT", "isdbt-layerc-segment-count",
143       gst_dvb_base_bin_conf_set_int},
144   {"ISDBT_LAYERC_TIME_INTERLEAVING", "isdbt-layerc-time-interleaving",
145       gst_dvb_base_bin_conf_set_int},
146   {"DELIVERY_SYSTEM", "delsys", gst_dvb_base_bin_conf_set_delsys},
147   {NULL,}
148 };
149 
150 /* TODO:
151  * Store the channels hash table around instead of constantly parsing it
152  * Detect when the file changed on disk
153  */
154 
155 static gint
gst_dvb_base_bin_find_string_in_array(const gchar ** array,const gchar * str)156 gst_dvb_base_bin_find_string_in_array (const gchar ** array, const gchar * str)
157 {
158   gint i = 0;
159   const gchar *cur;
160   while ((cur = array[i])) {
161     if (strcmp (cur, str) == 0)
162       return i;
163 
164     i++;
165   }
166 
167   return -1;
168 }
169 
170 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)171 gst_dvb_base_bin_conf_set_property_from_string_array (GstElement * dvbbasebin,
172     const gchar * property, GKeyFile * kf, const gchar * channel_name,
173     const gchar * key, const gchar ** strings, gint default_value)
174 {
175   gchar *str;
176   gint v;
177 
178   str = g_key_file_get_string (kf, channel_name, key, NULL);
179   v = gst_dvb_base_bin_find_string_in_array (strings, str);
180   if (v == -1) {
181     GST_WARNING_OBJECT (dvbbasebin, "Unexpected value '%s' for property "
182         "'%s', using default: '%d'", str, property, default_value);
183     v = default_value;
184   }
185 
186   g_free (str);
187   g_object_set (dvbbasebin, property, v, NULL);
188   return TRUE;
189 }
190 
191 static gboolean
gst_dvb_base_bin_conf_set_string(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)192 gst_dvb_base_bin_conf_set_string (GstElement * dvbbasebin,
193     const gchar * property, GKeyFile * kf, const gchar * channel_name,
194     const gchar * key)
195 {
196   gchar *str;
197 
198   str = g_key_file_get_string (kf, channel_name, key, NULL);
199   if (!str) {
200     GST_WARNING_OBJECT (dvbbasebin,
201         "Could not get value for '%s' on channel '%s'", key, channel_name);
202     return FALSE;
203   }
204 
205   g_object_set (dvbbasebin, property, str, NULL);
206   g_free (str);
207   return TRUE;
208 }
209 
210 static gboolean
gst_dvb_base_bin_conf_set_uint(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)211 gst_dvb_base_bin_conf_set_uint (GstElement * dvbbasebin, const gchar * property,
212     GKeyFile * kf, const gchar * channel_name, const gchar * key)
213 {
214   guint64 v;
215 
216   v = g_key_file_get_uint64 (kf, channel_name, key, NULL);
217   if (!v) {
218     GST_WARNING_OBJECT (dvbbasebin,
219         "Could not get value for '%s' on channel '%s'", key, channel_name);
220     return FALSE;
221   }
222 
223   g_object_set (dvbbasebin, property, (guint) v, NULL);
224   return TRUE;
225 }
226 
227 static gboolean
gst_dvb_base_bin_conf_set_int(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)228 gst_dvb_base_bin_conf_set_int (GstElement * dvbbasebin, const gchar * property,
229     GKeyFile * kf, const gchar * channel_name, const gchar * key)
230 {
231   gint v;
232 
233   v = g_key_file_get_integer (kf, channel_name, key, NULL);
234   if (!v) {
235     GST_WARNING_OBJECT (dvbbasebin,
236         "Could not get value for '%s' on channel '%s'", key, channel_name);
237     return FALSE;
238   }
239 
240   g_object_set (dvbbasebin, property, v, NULL);
241   return TRUE;
242 }
243 
244 static gboolean
gst_dvb_base_bin_conf_set_inversion(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)245 gst_dvb_base_bin_conf_set_inversion (GstElement * dvbbasebin,
246     const gchar * property, GKeyFile * kf, const gchar * channel_name,
247     const gchar * key)
248 {
249   gchar *str;
250   gint v;
251 
252   str = g_key_file_get_string (kf, channel_name, key, NULL);
253   if (!str) {
254     GST_WARNING_OBJECT (dvbbasebin,
255         "Could not get value for '%s' on channel '%s'", key, channel_name);
256     return FALSE;
257   }
258 
259   if (strcmp (str, "AUTO") == 0)
260     v = 2;
261   else if (strcmp (str, "ON") == 0)
262     v = 1;
263   else
264     v = 0;                      /* OFF */
265 
266   g_free (str);
267   g_object_set (dvbbasebin, property, v, NULL);
268   return TRUE;
269 }
270 
271 static gboolean
gst_dvb_base_bin_conf_set_guard(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)272 gst_dvb_base_bin_conf_set_guard (GstElement * dvbbasebin,
273     const gchar * property, GKeyFile * kf, const gchar * channel_name,
274     const gchar * key)
275 {
276   const gchar *guards[] = {
277     "1/32", "1/16", "1/8", "1/4", "auto",
278     "1/128", "19/128", "19/256",
279     "PN420", "PN595", "PN945", NULL
280   };
281   return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin,
282       property, kf, channel_name, key, guards, 4);
283 }
284 
285 static gboolean
gst_dvb_base_bin_conf_set_trans_mode(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)286 gst_dvb_base_bin_conf_set_trans_mode (GstElement * dvbbasebin,
287     const gchar * property, GKeyFile * kf, const gchar * channel_name,
288     const gchar * key)
289 {
290   const gchar *trans_modes[] = {
291     "2K", "8K", "AUTO", "4K", "1K",
292     "16K", "32K", "C1", "C3780", NULL
293   };
294   return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin,
295       property, kf, channel_name, key, trans_modes, 2);
296 }
297 
298 static gboolean
gst_dvb_base_bin_conf_set_code_rate(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)299 gst_dvb_base_bin_conf_set_code_rate (GstElement * dvbbasebin,
300     const gchar * property, GKeyFile * kf, const gchar * channel_name,
301     const gchar * key)
302 {
303   const gchar *code_rates[] = {
304     "NONE", "1/2", "2/3", "3/4", "4/5",
305     "5/6", "6/7", "7/8", "8/9", "AUTO",
306     "3/5", "9/10", "2/5", NULL
307   };
308   return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin,
309       property, kf, channel_name, key, code_rates, 9);
310 }
311 
312 static gboolean
gst_dvb_base_bin_conf_set_delsys(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)313 gst_dvb_base_bin_conf_set_delsys (GstElement * dvbbasebin,
314     const gchar * property, GKeyFile * kf, const gchar * channel_name,
315     const gchar * key)
316 {
317   const gchar *delsys[] = {
318     "UNDEFINED", "DVBCA", "DVBCB", "DVBT", "DSS",
319     "DVBS", "DVBS2", "DVBH", "ISDBT", "ISDBS",
320     "ISDBC", "ATSC", "ATSCMH", "DTMB", "CMMB",
321     "DAB", "DVBT2", "TURBO", "DVBCC", NULL
322   };
323   return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin,
324       property, kf, channel_name, key, delsys, 0);
325 }
326 
327 static gboolean
gst_dvb_base_bin_conf_set_hierarchy(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)328 gst_dvb_base_bin_conf_set_hierarchy (GstElement * dvbbasebin,
329     const gchar * property, GKeyFile * kf, const gchar * channel_name,
330     const gchar * key)
331 {
332   const gchar *hierarchies[] = {
333     "NONE", "1", "2", "4", "AUTO", NULL
334   };
335   return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin,
336       property, kf, channel_name, key, hierarchies, 4);
337 }
338 
339 static gboolean
gst_dvb_base_bin_conf_set_modulation(GstElement * dvbbasebin,const gchar * property,GKeyFile * kf,const gchar * channel_name,const gchar * key)340 gst_dvb_base_bin_conf_set_modulation (GstElement * dvbbasebin,
341     const gchar * property, GKeyFile * kf, const gchar * channel_name,
342     const gchar * key)
343 {
344   const gchar *modulations[] = {
345     "QPSK", "QAM/16", "QAM/32", "QAM/64",
346     "QAM/128", "QAM/256", "QAM/AUTO", "VSB/8",
347     "VSB/16", "PSK/8", "APSK/16", "APSK/32",
348     "DQPSK", "QAM/4_NR", NULL
349   };
350   return gst_dvb_base_bin_conf_set_property_from_string_array (dvbbasebin,
351       property, kf, channel_name, key, modulations, 6);
352 }
353 
354 /* FIXME: is channel_name guaranteed to be ASCII or UTF-8? */
355 static gboolean
parse_and_configure_from_v5_conf_file(GstElement * dvbbasebin,const gchar * filename,const gchar * channel_name,GError ** error)356 parse_and_configure_from_v5_conf_file (GstElement * dvbbasebin,
357     const gchar * filename, const gchar * channel_name, GError ** error)
358 {
359   GKeyFile *keyfile;
360   gchar **keys, **keys_p;
361   GError *err = NULL;
362 
363   keyfile = g_key_file_new ();
364   if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &err))
365     goto load_error;
366 
367   if (!g_key_file_has_group (keyfile, channel_name))
368     goto unknown_channel;
369 
370   keys = g_key_file_get_keys (keyfile, channel_name, NULL, &err);
371   if (!keys)
372     goto no_properties;
373 
374   keys_p = keys;
375   while (*keys_p) {
376     const gchar *k = *keys_p;
377     const GstDvbV5ChannelsConfToPropertyMap *map_entry = dvbv5_prop_map;
378     gboolean property_found = FALSE;
379 
380     GST_LOG_OBJECT (dvbbasebin, "Setting property %s", k);
381 
382     while (map_entry->conf_property) {
383       if (strcmp (map_entry->conf_property, k) == 0) {
384         if (!map_entry->set_func (dvbbasebin, map_entry->elem_property, keyfile,
385                 channel_name, k))
386           goto property_error;
387         property_found = TRUE;
388         break;
389       }
390       map_entry++;
391     }
392 
393     if (!property_found)
394       GST_WARNING_OBJECT (dvbbasebin, "Failed to map property '%s'", k);
395 
396     keys_p++;
397   }
398 
399   GST_DEBUG_OBJECT (dvbbasebin, "Successfully parsed channel configuration "
400       "file '%s'", filename);
401   g_strfreev (keys);
402   g_key_file_unref (keyfile);
403   return TRUE;
404 
405 load_error:
406   if ((err->domain == G_FILE_ERROR && err->code == G_FILE_ERROR_NOENT) ||
407       (err->domain == G_KEY_FILE_ERROR
408           && err->code == G_KEY_FILE_ERROR_NOT_FOUND)) {
409     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
410         _("Couldn't find channel configuration file"));
411   } else {
412     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
413         _("Couldn't load channel configuration file: '%s'"), err->message);
414   }
415   g_clear_error (&err);
416   return FALSE;
417 
418 unknown_channel:
419   {
420     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
421         _("Couldn't find details for channel '%s'"), channel_name);
422     g_key_file_unref (keyfile);
423     g_clear_error (&err);
424     return FALSE;
425   }
426 
427 no_properties:
428   {
429     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
430         _("No properties for channel '%s'"), channel_name);
431     g_key_file_unref (keyfile);
432     g_clear_error (&err);
433     return FALSE;
434   }
435 
436 property_error:
437   {
438     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
439         _("Failed to set properties for channel '%s'"), channel_name);
440     g_key_file_unref (keyfile);
441     g_clear_error (&err);
442     return FALSE;
443   }
444 }
445 
446 static GHashTable *
parse_channels_conf_from_zap_file(GstElement * dvbbasebin,const gchar * filename,GError ** error)447 parse_channels_conf_from_zap_file (GstElement * dvbbasebin,
448     const gchar * filename, GError ** error)
449 {
450   gchar *contents;
451   gchar **lines;
452   gchar *line;
453   gchar **fields;
454   int i, parsedchannels = 0;
455   GHashTable *res;
456   GError *err = NULL;
457   const gchar *terrestrial[] = { "inversion", "bandwidth",
458     "code-rate-hp", "code-rate-lp", "modulation", "transmission-mode",
459     "guard", "hierarchy"
460   };
461   const gchar *satellite[] = { "polarity", "diseqc-source",
462     "symbol-rate"
463   };
464   const gchar *cable[] = { "inversion", "symbol-rate", "code-rate-hp",
465     "modulation"
466   };
467 
468   GST_INFO_OBJECT (dvbbasebin, "parsing '%s'", filename);
469 
470   if (!g_file_get_contents (filename, &contents, NULL, &err))
471     goto open_fail;
472 
473   lines = g_strsplit (contents, "\n", 0);
474   res = g_hash_table_new (g_str_hash, g_str_equal);
475 
476   i = 0;
477   line = lines[0];
478   while (line != NULL) {
479     GHashTable *params;
480     int j, numfields;
481 
482     if (line[0] == '#')
483       goto next_line;
484 
485     params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
486     fields = g_strsplit (line, ":", 0);
487     numfields = g_strv_length (fields);
488 
489     switch (numfields) {
490       case 13:                 /* terrestrial */
491         g_hash_table_insert (params, g_strdup ("type"),
492             g_strdup ("terrestrial"));
493         for (j = 2; j <= 9; j++) {
494           g_hash_table_insert (params, g_strdup (terrestrial[j - 2]),
495               g_strdup (fields[j]));
496         }
497         g_hash_table_insert (params, g_strdup ("frequency"),
498             g_strdup (fields[1]));
499         break;
500       case 9:                  /* cable */
501         g_hash_table_insert (params, g_strdup ("type"), g_strdup ("cable"));
502         for (j = 2; j <= 5; j++) {
503           g_hash_table_insert (params, g_strdup (cable[j - 2]),
504               g_strdup (fields[j]));
505         }
506         g_hash_table_insert (params, g_strdup ("frequency"),
507             g_strdup (fields[1]));
508         break;
509       case 8:                  /* satellite */
510         g_hash_table_insert (params, g_strdup ("type"), g_strdup ("satellite"));
511         for (j = 2; j <= 4; j++) {
512           g_hash_table_insert (params, g_strdup (satellite[j - 2]),
513               g_strdup (fields[j]));
514         }
515         /* Some ZAP format variations store freqs in MHz
516          * but we internally use kHz for DVB-S/S2. */
517         if (strlen (fields[1]) < 6) {
518           g_hash_table_insert (params, g_strdup ("frequency"),
519               g_strdup_printf ("%d", atoi (fields[1]) * 1000));
520         } else {
521           g_hash_table_insert (params, g_strdup ("frequency"),
522               g_strdup_printf ("%d", atoi (fields[1])));
523         }
524         break;
525       case 6:                  /* atsc (vsb/qam) */
526         g_hash_table_insert (params, g_strdup ("type"), g_strdup ("atsc"));
527         g_hash_table_insert (params, g_strdup ("modulation"),
528             g_strdup (fields[2]));
529 
530         g_hash_table_insert (params, g_strdup ("frequency"),
531             g_strdup (fields[1]));
532         break;
533       default:
534         goto not_parsed;
535     }
536 
537     /* parsed */
538     g_hash_table_insert (params, g_strdup ("sid"),
539         g_strdup (fields[numfields - 1]));
540     g_hash_table_insert (res, g_strdup (fields[0]), params);
541     parsedchannels++;
542 
543   not_parsed:
544     g_strfreev (fields);
545   next_line:
546     line = lines[++i];
547   }
548 
549   g_strfreev (lines);
550   g_free (contents);
551 
552   if (parsedchannels == 0)
553     goto no_channels;
554 
555   return res;
556 
557 open_fail:
558   if (err->code == G_FILE_ERROR_NOENT) {
559     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
560         _("Couldn't find channel configuration file: '%s'"), err->message);
561   } else {
562     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_READ,
563         _("Couldn't load channel configuration file: '%s'"), err->message);
564   }
565   g_clear_error (&err);
566   return NULL;
567 
568 no_channels:
569   g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
570       _("Channel configuration file doesn't contain any channels"));
571   g_hash_table_unref (res);
572   return NULL;
573 }
574 
575 static gboolean
remove_channel_from_hash(gpointer key,gpointer value,gpointer user_data)576 remove_channel_from_hash (gpointer key, gpointer value, gpointer user_data)
577 {
578   g_free (key);
579   if (value)
580     g_hash_table_destroy ((GHashTable *) value);
581   return TRUE;
582 }
583 
584 static void
destroy_channels_hash(GHashTable * channels)585 destroy_channels_hash (GHashTable * channels)
586 {
587   g_hash_table_foreach_remove (channels, remove_channel_from_hash, NULL);
588 }
589 
590 /* FIXME: is channel_name guaranteed to be ASCII or UTF-8? */
591 static gboolean
parse_and_configure_from_zap_conf_file(GstElement * dvbbasebin,const gchar * filename,const gchar * channel_name,GError ** error)592 parse_and_configure_from_zap_conf_file (GstElement * dvbbasebin,
593     const gchar * filename, const gchar * channel_name, GError ** error)
594 {
595   gboolean ret = FALSE;
596   GHashTable *channels, *params;
597   gchar *type;
598 
599   /* Assumptions are made here about a format that is loosely
600    * defined. Particularly, we assume a given delivery system
601    * out of counting the number of fields per line. dvbsrc has
602    * smarter code to auto-detect a delivery system based on
603    * known-correct combinations of parameters so if you ever
604    * encounter cases where the delivery system is being
605    * wrongly set here, just remove the offending
606    * g_object_set line and let dvbsrc work his magic out. */
607 
608   channels = parse_channels_conf_from_zap_file (dvbbasebin, filename, error);
609 
610   if (!channels)
611     goto beach;
612 
613   params = g_hash_table_lookup (channels, channel_name);
614 
615   if (!params)
616     goto unknown_channel;
617 
618   g_object_set (dvbbasebin, "program-numbers",
619       g_hash_table_lookup (params, "sid"), NULL);
620   /* check if it is terrestrial or satellite */
621   g_object_set (dvbbasebin, "frequency",
622       atoi (g_hash_table_lookup (params, "frequency")), NULL);
623   type = g_hash_table_lookup (params, "type");
624   if (strcmp (type, "terrestrial") == 0) {
625     gchar *val;
626 
627     val = g_hash_table_lookup (params, "inversion");
628     if (strcmp (val, "INVERSION_OFF") == 0)
629       g_object_set (dvbbasebin, "inversion", INVERSION_OFF, NULL);
630     else if (strcmp (val, "INVERSION_ON") == 0)
631       g_object_set (dvbbasebin, "inversion", INVERSION_ON, NULL);
632     else
633       g_object_set (dvbbasebin, "inversion", INVERSION_AUTO, NULL);
634 
635     val = g_hash_table_lookup (params, "bandwidth");
636     if (strcmp (val, "BANDWIDTH_8_MHZ") == 0)
637       g_object_set (dvbbasebin, "bandwidth", 0, NULL);
638     else if (strcmp (val, "BANDWIDTH_7_MHZ") == 0)
639       g_object_set (dvbbasebin, "bandwidth", 1, NULL);
640     else if (strcmp (val, "BANDWIDTH_6_MHZ") == 0)
641       g_object_set (dvbbasebin, "bandwidth", 2, NULL);
642     else if (strcmp (val, "BANDWIDTH_5_MHZ") == 0)
643       g_object_set (dvbbasebin, "bandwidth", 4, NULL);
644     else if (strcmp (val, "BANDWIDTH_10_MHZ") == 0)
645       g_object_set (dvbbasebin, "bandwidth", 5, NULL);
646     else if (strcmp (val, "BANDWIDTH_1_712_MHZ") == 0)
647       g_object_set (dvbbasebin, "bandwidth", 6, NULL);
648     else
649       g_object_set (dvbbasebin, "bandwidth", 3, NULL);
650 
651     val = g_hash_table_lookup (params, "code-rate-hp");
652     if (strcmp (val, "FEC_NONE") == 0)
653       g_object_set (dvbbasebin, "code-rate-hp", 0, NULL);
654     else if (strcmp (val, "FEC_1_2") == 0)
655       g_object_set (dvbbasebin, "code-rate-hp", 1, NULL);
656     else if (strcmp (val, "FEC_2_3") == 0)
657       g_object_set (dvbbasebin, "code-rate-hp", 2, NULL);
658     else if (strcmp (val, "FEC_3_4") == 0)
659       g_object_set (dvbbasebin, "code-rate-hp", 3, NULL);
660     else if (strcmp (val, "FEC_4_5") == 0)
661       g_object_set (dvbbasebin, "code-rate-hp", 4, NULL);
662     else if (strcmp (val, "FEC_5_6") == 0)
663       g_object_set (dvbbasebin, "code-rate-hp", 5, NULL);
664     else if (strcmp (val, "FEC_6_7") == 0)
665       g_object_set (dvbbasebin, "code-rate-hp", 6, NULL);
666     else if (strcmp (val, "FEC_7_8") == 0)
667       g_object_set (dvbbasebin, "code-rate-hp", 7, NULL);
668     else if (strcmp (val, "FEC_8_9") == 0)
669       g_object_set (dvbbasebin, "code-rate-hp", 8, NULL);
670     else
671       g_object_set (dvbbasebin, "code-rate-hp", 9, NULL);
672 
673     val = g_hash_table_lookup (params, "code-rate-lp");
674     if (strcmp (val, "FEC_NONE") == 0)
675       g_object_set (dvbbasebin, "code-rate-lp", 0, NULL);
676     else if (strcmp (val, "FEC_1_2") == 0)
677       g_object_set (dvbbasebin, "code-rate-lp", 1, NULL);
678     else if (strcmp (val, "FEC_2_3") == 0)
679       g_object_set (dvbbasebin, "code-rate-lp", 2, NULL);
680     else if (strcmp (val, "FEC_3_4") == 0)
681       g_object_set (dvbbasebin, "code-rate-lp", 3, NULL);
682     else if (strcmp (val, "FEC_4_5") == 0)
683       g_object_set (dvbbasebin, "code-rate-lp", 4, NULL);
684     else if (strcmp (val, "FEC_5_6") == 0)
685       g_object_set (dvbbasebin, "code-rate-lp", 5, NULL);
686     else if (strcmp (val, "FEC_6_7") == 0)
687       g_object_set (dvbbasebin, "code-rate-lp", 6, NULL);
688     else if (strcmp (val, "FEC_7_8") == 0)
689       g_object_set (dvbbasebin, "code-rate-lp", 7, NULL);
690     else if (strcmp (val, "FEC_8_9") == 0)
691       g_object_set (dvbbasebin, "code-rate-lp", 8, NULL);
692     else
693       g_object_set (dvbbasebin, "code-rate-lp", 9, NULL);
694 
695     val = g_hash_table_lookup (params, "modulation");
696     if (strcmp (val, "QPSK") == 0)
697       g_object_set (dvbbasebin, "modulation", 0, NULL);
698     else if (strcmp (val, "QAM_16") == 0)
699       g_object_set (dvbbasebin, "modulation", 1, NULL);
700     else if (strcmp (val, "QAM_32") == 0)
701       g_object_set (dvbbasebin, "modulation", 2, NULL);
702     else if (strcmp (val, "QAM_64") == 0)
703       g_object_set (dvbbasebin, "modulation", 3, NULL);
704     else if (strcmp (val, "QAM_128") == 0)
705       g_object_set (dvbbasebin, "modulation", 4, NULL);
706     else if (strcmp (val, "QAM_256") == 0)
707       g_object_set (dvbbasebin, "modulation", 5, NULL);
708     else
709       g_object_set (dvbbasebin, "modulation", 6, NULL);
710 
711     val = g_hash_table_lookup (params, "transmission-mode");
712     if (strcmp (val, "TRANSMISSION_MODE_2K") == 0)
713       g_object_set (dvbbasebin, "trans-mode", 0, NULL);
714     else if (strcmp (val, "TRANSMISSION_MODE_8K") == 0)
715       g_object_set (dvbbasebin, "trans-mode", 1, NULL);
716     else
717       g_object_set (dvbbasebin, "trans-mode", 2, NULL);
718 
719     val = g_hash_table_lookup (params, "guard");
720     if (strcmp (val, "GUARD_INTERVAL_1_32") == 0)
721       g_object_set (dvbbasebin, "guard", 0, NULL);
722     else if (strcmp (val, "GUARD_INTERVAL_1_16") == 0)
723       g_object_set (dvbbasebin, "guard", 1, NULL);
724     else if (strcmp (val, "GUARD_INTERVAL_1_8") == 0)
725       g_object_set (dvbbasebin, "guard", 2, NULL);
726     else if (strcmp (val, "GUARD_INTERVAL_1_4") == 0)
727       g_object_set (dvbbasebin, "guard", 3, NULL);
728     else
729       g_object_set (dvbbasebin, "guard", 4, NULL);
730 
731     val = g_hash_table_lookup (params, "hierarchy");
732     if (strcmp (val, "HIERARCHY_NONE") == 0)
733       g_object_set (dvbbasebin, "hierarchy", 0, NULL);
734     else if (strcmp (val, "HIERARCHY_1") == 0)
735       g_object_set (dvbbasebin, "hierarchy", 1, NULL);
736     else if (strcmp (val, "HIERARCHY_2") == 0)
737       g_object_set (dvbbasebin, "hierarchy", 2, NULL);
738     else if (strcmp (val, "HIERARCHY_4") == 0)
739       g_object_set (dvbbasebin, "hierarchy", 3, NULL);
740     else
741       g_object_set (dvbbasebin, "hierarchy", 4, NULL);
742 
743     ret = TRUE;
744   } else if (strcmp (type, "satellite") == 0) {
745     gchar *val;
746 
747     ret = TRUE;
748 
749     g_object_set (dvbbasebin, "delsys", SYS_DVBS, NULL);
750 
751     val = g_hash_table_lookup (params, "polarity");
752     if (val)
753       g_object_set (dvbbasebin, "polarity", val, NULL);
754     else
755       ret = FALSE;
756 
757     val = g_hash_table_lookup (params, "diseqc-source");
758     if (val)
759       g_object_set (dvbbasebin, "diseqc-source", atoi (val), NULL);
760 
761     val = g_hash_table_lookup (params, "symbol-rate");
762     if (val)
763       g_object_set (dvbbasebin, "symbol-rate", atoi (val), NULL);
764     else
765       ret = FALSE;
766   } else if (strcmp (type, "cable") == 0) {
767     gchar *val;
768 
769     g_object_set (dvbbasebin, "delsys", SYS_DVBC_ANNEX_A, NULL);
770 
771     ret = TRUE;
772     val = g_hash_table_lookup (params, "symbol-rate");
773     if (val)
774       g_object_set (dvbbasebin, "symbol-rate", atoi (val) / 1000, NULL);
775     val = g_hash_table_lookup (params, "modulation");
776     if (strcmp (val, "QPSK") == 0)
777       g_object_set (dvbbasebin, "modulation", 0, NULL);
778     else if (strcmp (val, "QAM_16") == 0)
779       g_object_set (dvbbasebin, "modulation", 1, NULL);
780     else if (strcmp (val, "QAM_32") == 0)
781       g_object_set (dvbbasebin, "modulation", 2, NULL);
782     else if (strcmp (val, "QAM_64") == 0)
783       g_object_set (dvbbasebin, "modulation", 3, NULL);
784     else if (strcmp (val, "QAM_128") == 0)
785       g_object_set (dvbbasebin, "modulation", 4, NULL);
786     else if (strcmp (val, "QAM_256") == 0)
787       g_object_set (dvbbasebin, "modulation", 5, NULL);
788     else
789       g_object_set (dvbbasebin, "modulation", 6, NULL);
790     val = g_hash_table_lookup (params, "code-rate-hp");
791     if (strcmp (val, "FEC_NONE") == 0)
792       g_object_set (dvbbasebin, "code-rate-hp", 0, NULL);
793     else if (strcmp (val, "FEC_1_2") == 0)
794       g_object_set (dvbbasebin, "code-rate-hp", 1, NULL);
795     else if (strcmp (val, "FEC_2_3") == 0)
796       g_object_set (dvbbasebin, "code-rate-hp", 2, NULL);
797     else if (strcmp (val, "FEC_3_4") == 0)
798       g_object_set (dvbbasebin, "code-rate-hp", 3, NULL);
799     else if (strcmp (val, "FEC_4_5") == 0)
800       g_object_set (dvbbasebin, "code-rate-hp", 4, NULL);
801     else if (strcmp (val, "FEC_5_6") == 0)
802       g_object_set (dvbbasebin, "code-rate-hp", 5, NULL);
803     else if (strcmp (val, "FEC_6_7") == 0)
804       g_object_set (dvbbasebin, "code-rate-hp", 6, NULL);
805     else if (strcmp (val, "FEC_7_8") == 0)
806       g_object_set (dvbbasebin, "code-rate-hp", 7, NULL);
807     else if (strcmp (val, "FEC_8_9") == 0)
808       g_object_set (dvbbasebin, "code-rate-hp", 8, NULL);
809     else
810       g_object_set (dvbbasebin, "code-rate-hp", 9, NULL);
811     val = g_hash_table_lookup (params, "inversion");
812     if (strcmp (val, "INVERSION_OFF") == 0)
813       g_object_set (dvbbasebin, "inversion", 0, NULL);
814     else if (strcmp (val, "INVERSION_ON") == 0)
815       g_object_set (dvbbasebin, "inversion", 1, NULL);
816     else
817       g_object_set (dvbbasebin, "inversion", 2, NULL);
818   } else if (strcmp (type, "atsc") == 0) {
819     gchar *val;
820 
821     ret = TRUE;
822 
823     g_object_set (dvbbasebin, "delsys", SYS_ATSC, NULL);
824 
825     val = g_hash_table_lookup (params, "modulation");
826     if (strcmp (val, "QAM_64") == 0)
827       g_object_set (dvbbasebin, "modulation", 3, NULL);
828     else if (strcmp (val, "QAM_256") == 0)
829       g_object_set (dvbbasebin, "modulation", 5, NULL);
830     else if (strcmp (val, "8VSB") == 0)
831       g_object_set (dvbbasebin, "modulation", 7, NULL);
832     else if (strcmp (val, "16VSB") == 0)
833       g_object_set (dvbbasebin, "modulation", 8, NULL);
834     else
835       ret = FALSE;
836   }
837 
838   destroy_channels_hash (channels);
839 
840 beach:
841   return ret;
842 
843 unknown_channel:
844   {
845     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_NOT_FOUND,
846         _("Couldn't find details for channel '%s'"), channel_name);
847     destroy_channels_hash (channels);
848     return FALSE;
849   }
850 }
851 
852 static GstDvbChannelConfFormat
detect_file_format(const gchar * filename)853 detect_file_format (const gchar * filename)
854 {
855   gchar *contents;
856   gchar **lines;
857   gchar **line;
858   GstDvbChannelConfFormat ret = CHANNEL_CONF_FORMAT_NONE;
859 
860   if (!g_file_get_contents (filename, &contents, NULL, NULL))
861     return ret;
862 
863   lines = g_strsplit (contents, "\n", 0);
864   line = lines;
865 
866   while (*line) {
867     if (g_str_has_prefix (*line, "[") && g_str_has_suffix (*line, "]")) {
868       ret = CHANNEL_CONF_FORMAT_DVBV5;
869       break;
870     } else if (g_strrstr (*line, ":")) {
871       ret = CHANNEL_CONF_FORMAT_ZAP;
872       break;
873     }
874     line++;
875   }
876 
877   g_strfreev (lines);
878   g_free (contents);
879   return ret;
880 }
881 
882 gboolean
set_properties_for_channel(GstElement * dvbbasebin,const gchar * channel_name,GError ** error)883 set_properties_for_channel (GstElement * dvbbasebin,
884     const gchar * channel_name, GError ** error)
885 {
886   gboolean ret = FALSE;
887   gchar *filename;
888 
889   filename = g_strdup (g_getenv ("GST_DVB_CHANNELS_CONF"));
890   if (filename == NULL) {
891     filename = g_build_filename (g_get_user_config_dir (),
892         "gstreamer-" GST_API_VERSION, "dvb-channels.conf", NULL);
893   }
894 
895   switch (detect_file_format (filename)) {
896     case CHANNEL_CONF_FORMAT_DVBV5:
897       if (!parse_and_configure_from_v5_conf_file (dvbbasebin, filename,
898               channel_name, error)) {
899         GST_WARNING_OBJECT (dvbbasebin, "Problem finding information for "
900             "channel '%s' in configuration file '%s'", channel_name, filename);
901       } else {
902         GST_INFO_OBJECT (dvbbasebin, "Parsed libdvbv5 channel configuration "
903             "file");
904         ret = TRUE;
905       }
906       break;
907     case CHANNEL_CONF_FORMAT_ZAP:
908       if (!parse_and_configure_from_zap_conf_file (dvbbasebin, filename,
909               channel_name, error)) {
910         GST_WARNING_OBJECT (dvbbasebin, "Problem finding information for "
911             "channel '%s' in configuration file '%s'", channel_name, filename);
912       } else {
913         GST_INFO_OBJECT (dvbbasebin, "Parsed ZAP channel configuration file");
914         ret = TRUE;
915       }
916       break;
917     default:
918       GST_WARNING_OBJECT (dvbbasebin, "Unknown configuration file format. "
919           "Can not get parameters for channel");
920   }
921 
922   g_free (filename);
923   return ret;
924 }
925