ebbc46950f3556f587bc3a2d4a6f949d2b8643aa
[vlc.git] / modules / demux / playlist / dvb.c
1 /*****************************************************************************
2  * dvb.c: LinuxTV channels list
3  *****************************************************************************
4  * Copyright (C) 2005-2012 VLC authors and VideoLAN
5  *
6  * Authors: Gildas Bazin <gbazin@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <limits.h>
32 #include <assert.h>
33
34 #include <vlc_common.h>
35 #include <vlc_access.h>
36 #include <vlc_charset.h>
37
38 #include "playlist.h"
39
40 static int ReadDir(stream_t *, input_item_node_t *);
41 static input_item_t *ParseLine(char *line);
42
43 /** Detect dvb-utils zap channels.conf format */
44 int Import_DVB(vlc_object_t *p_this)
45 {
46     stream_t *demux = (stream_t *)p_this;
47
48     if (!stream_HasExtension(demux, ".conf" ) && !demux->obj.force )
49         return VLC_EGENERIC;
50
51     /* Check if this really is a channels file */
52     const uint8_t *peek;
53     int len = vlc_stream_Peek(demux->s, &peek, 1023);
54     if (len <= 0)
55         return VLC_EGENERIC;
56
57     const uint8_t *eol = memchr(peek, '\n', len);
58     if (eol == NULL)
59         return VLC_EGENERIC;
60     len = eol - peek;
61
62     char line[len + 1];
63     memcpy(line, peek, len);
64     line[len] = '\0';
65
66     input_item_t *item = ParseLine(line);
67     if (item == NULL)
68         return VLC_EGENERIC;
69     input_item_Release(item);
70
71     msg_Dbg(demux, "found valid channels.conf file");
72     demux->pf_control = PlaylistControl;
73     demux->pf_readdir = ReadDir;
74
75     return VLC_SUCCESS;
76 }
77
78 /** Parses the whole channels.conf file */
79 static int ReadDir(stream_t *s, input_item_node_t *subitems)
80 {
81     char *line;
82
83     while ((line = vlc_stream_ReadLine(s->s)) != NULL)
84     {
85         input_item_t *item = ParseLine(line);
86         free(line);
87         if (item == NULL)
88             continue;
89
90         input_item_node_AppendItem(subitems, item);
91         input_item_Release(item);
92     }
93
94     return VLC_SUCCESS;
95 }
96
97 static int cmp(const void *k, const void *e)
98 {
99     return strcmp(k, e);
100 }
101
102 static const char *ParseFEC(const char *str)
103 {
104      static const struct fec
105      {
106          char dvb[5];
107          char vlc[5];
108      } tab[] = {
109          { "1_2", "1/2" }, { "2_3", "2/3" }, { "3_4", "3/4" },
110          { "4_5", "4/5" }, { "5_6", "5/6" }, { "6_7", "6/7" },
111          { "7_8", "7/8" }, { "8_9", "8/9" }, { "9_10", "9/10" },
112          { "AUTO", "" },   { "NONE", "0" }
113      };
114
115      if (str == NULL || strncmp(str, "FEC_", 4))
116          return NULL;
117      str += 4;
118
119      const struct fec *f = bsearch(str, tab, sizeof (tab) / sizeof(tab[0]),
120                                    sizeof (tab[0]), cmp);
121      return (f != NULL) ? f->vlc : NULL;
122 }
123
124 static const char *ParseModulation(const char *str)
125 {
126      static const struct mod
127      {
128          char dvb[9];
129          char vlc[7];
130      } tab[] = {
131          { "8VSB", "8VSB" }, { "APSK_16", "16APSK" }, { "APSK_32", "32APSK" },
132          { "DQPSK", "DQPSK" }, { "PSK_8", "8PSK" }, { "QPSK", "QPSK" },
133          { "QAM_128", "128QAM" }, { "QAM_16", "16QAM" },
134          { "QAM_256", "256QAM" }, { "QAM_32", "32QAM" },
135          { "QAM_64", "64QAM" }, { "QAM_AUTO", "QAM" },
136          { "VSB_16", "16VSB" }, { "VSB_8", "8VSB" }
137      };
138
139      if( str == NULL )
140          return NULL;
141
142      const struct mod *m = bsearch(str, tab, sizeof (tab) / sizeof(tab[0]),
143                                    sizeof (tab[0]), cmp);
144      return (m != NULL) ? m->vlc : NULL;
145 }
146
147 static const char *ParseGuard(const char *str)
148 {
149      static const struct guard
150      {
151          char dvb[7];
152          char vlc[7];
153      } tab[] = {
154          { "19_128", "19/128" }, { "19_256", "19/256" }, { "1_128", "1/128" },
155          { "1_16", "1/16" }, { "1_32", "1/32" }, { "1_4", "1/4" },
156          { "1_8", "1/8" }, { "AUTO", "" },
157      };
158
159      if (str == NULL || strncmp(str, "GUARD_INTERVAL_", 15))
160          return NULL;
161      str += 15;
162
163      const struct guard *g = bsearch(str, tab, sizeof (tab) / sizeof(tab[0]),
164                                      sizeof (tab[0]), cmp);
165      return (g != NULL) ? g->vlc : NULL;
166 }
167
168 /* http://www.linuxtv.org/vdrwiki/index.php/Syntax_of_channels.conf or not...
169  * Read the dvb-apps source code for reference. */
170 static input_item_t *ParseLine(char *line)
171 {
172     char *str, *end;
173
174     line += strspn(line, " \t\r"); /* skip leading white spaces */
175     if (*line == '#')
176         return NULL; /* skip comments */
177
178     /* Extract channel cute name */
179     char *name = strsep(&line, ":");
180     assert(name != NULL);
181     EnsureUTF8(name);
182
183     /* Extract central frequency */
184     str = strsep(&line, ":");
185     if (str == NULL)
186         return NULL;
187     unsigned long freq = strtoul(str, &end, 10);
188     if (*end)
189         return NULL;
190
191     /* Extract tuning parameters */
192     str = strsep(&line, ":");
193     if (str == NULL)
194         return NULL;
195
196     char *mrl;
197
198     if (!strcmp(str, "h") || !strcmp(str, "v"))
199     {   /* DVB-S */
200         char polarization = toupper(*str);
201
202         /* TODO: sat no. */
203         str = strsep(&line, ":");
204         if (str == NULL)
205             return NULL;
206
207         /* baud rate */
208         str = strsep(&line, ":");
209         if (str == NULL)
210             return NULL;
211
212         unsigned long rate = strtoul(str, &end, 10);
213         if (*end || rate > (ULONG_MAX / 1000u))
214             return NULL;
215         rate *= 1000;
216
217         if (asprintf(&mrl,
218                      "dvb-s://frequency=%"PRIu64":polarization=%c:srate=%lu",
219                      freq * UINT64_C(1000000), polarization, rate) == -1)
220             mrl = NULL;
221     }
222     else
223     if (!strncmp(str, "INVERSION_", 10))
224     {   /* DVB-C or DVB-T */
225         int inversion;
226
227         str += 10;
228         if (strcmp(str, "AUTO"))
229             inversion = -1;
230         else if (strcmp(str, "OFF"))
231             inversion = 0;
232         else if (strcmp(str, "ON"))
233             inversion = 1;
234         else
235             return NULL;
236
237         str = strsep(&line, ":");
238         if (str == NULL)
239             return NULL;
240
241         if (strncmp(str, "BANDWIDTH_", 10))
242         {   /* DVB-C */
243             unsigned long rate = strtoul(str, &end, 10);
244             if (*end)
245                 return NULL;
246
247             const char *fec = ParseFEC(strsep(&line, ":"));
248             const char *mod = ParseModulation(strsep(&line,":"));
249             if (fec == NULL || mod == NULL)
250                 return NULL;
251
252             if (asprintf(&mrl, "dvb-c://frequency=%lu:inversion:%d:srate=%lu:"
253                          "fec=%s:modulation=%s", freq, inversion, rate, fec,
254                          mod) == -1)
255                 mrl = NULL;
256         }
257         else
258         {   /* DVB-T */
259             unsigned bandwidth = atoi(str + 10);
260
261             const char *hp = ParseFEC(strsep(&line, ":"));
262             const char *lp = ParseFEC(strsep(&line, ":"));
263             const char *mod = ParseModulation(strsep(&line, ":"));
264
265             if (hp == NULL || lp == NULL || mod == NULL)
266                 return NULL;
267
268             str = strsep(&line, ":");
269             if (str == NULL || strncmp(str, "TRANSMISSION_MODE_", 18))
270                 return NULL;
271             int xmit = atoi(str);
272             if (xmit == 0)
273                 xmit = -1; /* AUTO */
274
275             const char *guard = ParseGuard(strsep(&line,":"));
276             if (guard == NULL)
277                 return NULL;
278
279             str = strsep(&line, ":");
280             if (str == NULL || strncmp(str, "HIERARCHY_", 10))
281                 return NULL;
282             str += 10;
283             int hierarchy = atoi(str);
284             if (!strcmp(str, "AUTO"))
285                 hierarchy = -1;
286
287             if (asprintf(&mrl, "dvb-t://frequency=%lu:inversion=%d:"
288                          "bandwidth=%u:code-rate-hp=%s:code-rate-lp=%s:"
289                          "modulation=%s:transmission=%d:guard=%s:"
290                          "hierarchy=%d", freq, inversion, bandwidth, hp, lp,
291                          mod, xmit, guard, hierarchy) == -1)
292                 mrl = NULL;
293         }
294     }
295     else
296     {   /* ATSC */
297         const char *mod = ParseModulation(str);
298         if (mod == NULL)
299             return NULL;
300
301         if (asprintf(&mrl, "atsc://frequency=%lu:modulation=%s", freq,
302                      mod) == -1)
303             mrl = NULL;
304     }
305
306     if (unlikely(mrl == NULL))
307         return NULL;
308
309     /* Video PID (TODO? set video track) */
310     strsep(&line, ":");
311     /* Audio PID (TODO? set audio track) */
312     strsep(&line, ":");
313     /* Extract SID */
314     str = strsep(&line, ":");
315     if (str == NULL)
316     {
317         free(mrl);
318         return NULL;
319     }
320     unsigned long sid = strtoul(str, &end, 10);
321     if (*end || sid > 65535)
322     {
323         free(mrl);
324         return NULL;
325     }
326
327     char sid_opt[sizeof("program=65535")];
328     snprintf(sid_opt, sizeof(sid_opt), "program=%lu", sid);
329
330     input_item_t *item = input_item_NewCard(mrl, name);
331     free(mrl);
332     if (item != NULL)
333         input_item_AddOption(item, sid_opt, 0);
334     return item;
335 }