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