1 /*
2  * Copyright (c) 2011-2012 - Mauro Carvalho Chehab
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation version 2.1 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16  * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <libdvbv5/dvb-file.h>
24 #include <libdvbv5/dvb-v5-std.h>
25 
26 #include <config.h>
27 
28 #ifdef ENABLE_NLS
29 # include <stdio.h>
30 # include <libintl.h>
31 # define _(string) dgettext(LIBDVBV5_DOMAIN, string)
32 
33 #else
34 # define _(string) string
35 #endif
36 
37 # define N_(string) string
38 
39 
40 #define PTABLE(a) .table = a, .size=ARRAY_SIZE(a)
41 
42 /*
43  * Standard channel.conf format for DVB-T, DVB-C, DVB-S, DVB-S2 and ATSC
44  */
45 static const char *vdr_parse_bandwidth[] = {
46 	[BANDWIDTH_1_712_MHZ] = "B1712",
47 	[BANDWIDTH_5_MHZ] =     "B5",
48 	[BANDWIDTH_6_MHZ] =     "B6",
49 	[BANDWIDTH_7_MHZ] =     "B7",
50 	[BANDWIDTH_8_MHZ] =     "B8",
51 	[BANDWIDTH_10_MHZ] =    "B10",
52 	[BANDWIDTH_AUTO] =      "B999",
53 };
54 
55 static const char *vdr_parse_code_rate_hp[12] = {
56 	[FEC_NONE] = "C0",
57 	[FEC_1_2] =  "C12",
58 	[FEC_2_3] =  "C23",
59 	[FEC_3_4] =  "C34",
60 	[FEC_3_5] =  "C35",
61 	[FEC_4_5] =  "C45",
62 	[FEC_5_6] =  "C56",
63 	[FEC_6_7] =  "C67",
64 	[FEC_7_8] =  "C78",
65 	[FEC_8_9] =  "C89",
66 	[FEC_9_10] = "C910",
67 	[FEC_AUTO] = "C999",
68 };
69 
70 static const char *vdr_parse_code_rate_lp[12] = {
71 	[FEC_NONE] = "D0",
72 	[FEC_1_2] =  "D12",
73 	[FEC_2_3] =  "D23",
74 	[FEC_3_4] =  "D34",
75 	[FEC_3_5] =  "D35",
76 	[FEC_4_5] =  "D45",
77 	[FEC_5_6] =  "D56",
78 	[FEC_6_7] =  "D67",
79 	[FEC_7_8] =  "D78",
80 	[FEC_8_9] =  "D89",
81 	[FEC_9_10] = "D910",
82 	[FEC_AUTO] = "D999",
83 };
84 static const char *vdr_parse_guard_interval[] = {
85 	[GUARD_INTERVAL_1_4] =    "G4",
86 	[GUARD_INTERVAL_1_8] =    "G8",
87 	[GUARD_INTERVAL_1_16] =   "G16",
88 	[GUARD_INTERVAL_1_32] =   "G32",
89 	[GUARD_INTERVAL_1_128] =  "G128",
90 	[GUARD_INTERVAL_19_128] = "G19128",
91 	[GUARD_INTERVAL_19_256] = "G19256",
92 	[GUARD_INTERVAL_AUTO] =   "G999",
93 };
94 
95 static const char *vdr_parse_polarization[] = {
96 	[POLARIZATION_OFF] = "",
97 	[POLARIZATION_H] =   "H",
98 	[POLARIZATION_V] =   "V",
99 	[POLARIZATION_L] =   "L",
100 	[POLARIZATION_R] =   "R",
101 };
102 
103 static const char *vdr_parse_inversion[] = {
104 	[INVERSION_OFF]  = "I0",
105 	[INVERSION_ON]   = "I1",
106 	[INVERSION_AUTO] = "I999",
107 };
108 
109 static const char *vdr_parse_modulation[] = {
110 	[QAM_16] =   "M16",
111 	[QAM_32] =   "M32",
112 	[QAM_64] =   "M64",
113 	[QAM_128] =  "M128",
114 	[QAM_256] =  "M256",
115 	[QPSK] =     "M2",
116 	[PSK_8] =    "M5",
117 	[APSK_16] =  "M6",
118 	[APSK_32] =  "M7",
119 	[VSB_8] =    "M10",
120 	[VSB_16] =   "M11",
121 	[DQPSK] =    "M12",
122 	[QAM_AUTO] = "M999",
123 };
124 
125 static const char *vdr_parse_pilot[] = {
126 	[PILOT_OFF] =  "N0",
127 	[PILOT_ON] =   "N1",
128 	[PILOT_AUTO] = "N999",
129 };
130 
131 static const char *vdr_parse_rolloff[] = {
132 	[ROLLOFF_AUTO] = "O0",
133 	[ROLLOFF_20]   = "O20",
134 	[ROLLOFF_25]   = "O25",
135 	[ROLLOFF_35]   = "O35",
136 };
137 
138 static const char *vdr_parse_trans_mode[] = {
139 	[TRANSMISSION_MODE_1K] =   "T1",
140 	[TRANSMISSION_MODE_2K] =   "T2",
141 	[TRANSMISSION_MODE_4K] =   "T4",
142 	[TRANSMISSION_MODE_8K] =   "T8",
143 	[TRANSMISSION_MODE_16K] =  "T16",
144 	[TRANSMISSION_MODE_32K] =  "T32",
145 	[TRANSMISSION_MODE_AUTO] = "T999",
146 };
147 
148 static const char *vdr_parse_hierarchy[] = {
149 	[HIERARCHY_1] =    "Y1",
150 	[HIERARCHY_2] =    "Y2",
151 	[HIERARCHY_4] =    "Y4",
152 	[HIERARCHY_AUTO] = "Y999",
153 	[HIERARCHY_NONE] = "Y0",
154 };
155 
156 static const char *vdr_parse_delivery_system[] = {
157 	[SYS_DVBS]  = "S0",
158 	[SYS_DVBS2] = "S1",
159 	[SYS_DVBT]  = "S0",
160 	[SYS_DVBT2] = "S1",
161 };
162 
163 /*
164  * The tables below deal only with the "Parameters" part of the VDR
165  * format.
166  */
167 static const struct dvb_parse_table sys_atsc_table[] = {
168 	{ DTV_MODULATION, PTABLE(vdr_parse_modulation) },
169 	{ DTV_INVERSION, PTABLE(vdr_parse_inversion) },
170 };
171 
172 static const struct dvb_parse_table sys_dvbc_table[] = {
173 	{ DTV_SYMBOL_RATE, NULL, 0 },
174 	{ DTV_INNER_FEC, PTABLE(vdr_parse_code_rate_hp) },
175 	{ DTV_MODULATION, PTABLE(vdr_parse_modulation) },
176 	{ DTV_INVERSION, PTABLE(vdr_parse_inversion) },
177 };
178 
179 static const struct dvb_parse_table sys_dvbs_table[] = {
180 	{ DTV_DELIVERY_SYSTEM, PTABLE(vdr_parse_delivery_system) },
181 	{ DTV_POLARIZATION, PTABLE(vdr_parse_polarization) },
182 	{ DTV_SYMBOL_RATE, NULL, 0 },
183 	{ DTV_INNER_FEC, PTABLE(vdr_parse_code_rate_hp) },
184 	{ DTV_INVERSION, PTABLE(vdr_parse_inversion) },
185 };
186 
187 static const struct dvb_parse_table sys_dvbs2_table[] = {
188 	{ DTV_DELIVERY_SYSTEM, PTABLE(vdr_parse_delivery_system) },
189 	{ DTV_POLARIZATION, PTABLE(vdr_parse_polarization) },
190 	{ DTV_SYMBOL_RATE, NULL, 0 },
191 	{ DTV_INNER_FEC, PTABLE(vdr_parse_code_rate_hp) },
192 	{ DTV_INVERSION, PTABLE(vdr_parse_inversion) },
193 	/* DVB-S2 specifics */
194 	{ DTV_MODULATION, PTABLE(vdr_parse_modulation) },
195 	{ DTV_PILOT, PTABLE(vdr_parse_pilot) },
196 	{ DTV_ROLLOFF, PTABLE(vdr_parse_rolloff) },
197 	{ DTV_STREAM_ID, NULL, },
198 };
199 
200 static const struct dvb_parse_table sys_dvbt_table[] = {
201 	{ DTV_BANDWIDTH_HZ, PTABLE(vdr_parse_bandwidth) },
202 	{ DTV_CODE_RATE_HP, PTABLE(vdr_parse_code_rate_hp) },
203 	{ DTV_CODE_RATE_LP, PTABLE(vdr_parse_code_rate_lp) },
204 	{ DTV_GUARD_INTERVAL, PTABLE(vdr_parse_guard_interval) },
205 	{ DTV_INVERSION, PTABLE(vdr_parse_inversion) },
206 	{ DTV_MODULATION, PTABLE(vdr_parse_modulation) },
207 	{ DTV_DELIVERY_SYSTEM, PTABLE(vdr_parse_delivery_system) },
208 	{ DTV_TRANSMISSION_MODE, PTABLE(vdr_parse_trans_mode) },
209 	{ DTV_HIERARCHY, PTABLE(vdr_parse_hierarchy) },
210 };
211 
212 static const struct dvb_parse_table sys_dvbt2_table[] = {
213 	{ DTV_BANDWIDTH_HZ, PTABLE(vdr_parse_bandwidth) },
214 	{ DTV_CODE_RATE_HP, PTABLE(vdr_parse_code_rate_hp) },
215 	{ DTV_CODE_RATE_LP, PTABLE(vdr_parse_code_rate_lp) },
216 	{ DTV_GUARD_INTERVAL, PTABLE(vdr_parse_guard_interval) },
217 	{ DTV_INVERSION, PTABLE(vdr_parse_inversion) },
218 	{ DTV_MODULATION, PTABLE(vdr_parse_modulation) },
219 	{ DTV_DELIVERY_SYSTEM, PTABLE(vdr_parse_delivery_system) },
220 	{ DTV_TRANSMISSION_MODE, PTABLE(vdr_parse_trans_mode) },
221 	{ DTV_HIERARCHY, PTABLE(vdr_parse_hierarchy) },
222 	/* DVB-T2 specifics */
223 	{ DTV_STREAM_ID, NULL, },
224 };
225 
226 static const struct dvb_parse_file vdr_file_format = {
227 	.formats = {
228 		{
229 			.id		= "A",
230 			.delsys		= SYS_ATSC,
231 			PTABLE(sys_atsc_table),
232 		}, {
233 			.id		= "C",
234 			.delsys		= SYS_DVBC_ANNEX_A,
235 			PTABLE(sys_dvbc_table),
236 		}, {
237 			.id		= "S",
238 			.delsys		= SYS_DVBS,
239 			PTABLE(sys_dvbs_table),
240 		}, {
241 			.id		= "S",
242 			.delsys		= SYS_DVBS2,
243 			PTABLE(sys_dvbs2_table),
244 		}, {
245 			.id		= "T",
246 			.delsys		= SYS_DVBT,
247 			PTABLE(sys_dvbt_table),
248 		}, {
249 			.id		= "T",
250 			.delsys		= SYS_DVBT2,
251 			PTABLE(sys_dvbt2_table),
252 		}, {
253 			NULL, 0, NULL, 0,
254 		}
255 	}
256 };
257 
dvb_write_format_vdr(const char * fname,struct dvb_file * dvb_file)258 int dvb_write_format_vdr(const char *fname,
259 			 struct dvb_file *dvb_file)
260 {
261 	const struct dvb_parse_file *parse_file = &vdr_file_format;
262 	const struct dvb_parse_struct *formats = parse_file->formats;
263 	int i, j, line = 0;
264 	FILE *fp;
265 	const struct dvb_parse_struct *fmt;
266 	struct dvb_entry *entry;
267 	const struct dvb_parse_table *table;
268 	const char *id;
269 	uint32_t delsys, freq, data, srate;
270 	char err_msg[80];
271 
272 	fp = fopen(fname, "w");
273 	if (!fp) {
274 		perror(fname);
275 		return -errno;
276 	}
277 
278 	for (entry = dvb_file->first_entry; entry != NULL; entry = entry->next) {
279 		if (dvb_retrieve_entry_prop(entry, DTV_DELIVERY_SYSTEM, &delsys) < 0)
280 			continue;
281 
282 		for (i = 0; formats[i].delsys != 0; i++) {
283 			if (formats[i].delsys == delsys)
284 				break;
285 		}
286 		if (formats[i].delsys == 0) {
287 			fprintf(stderr,
288 				_("WARNING: entry %d: delivery system %d not supported on this format. skipping entry\n"),
289 				 line, delsys);
290 			continue;
291 		}
292 		id = formats[i].id;
293 
294 		if (!entry->channel) {
295 			fprintf(stderr,
296 				_("WARNING: entry %d: channel name not found. skipping entry\n"),
297 				 line);
298 			continue;
299 		}
300 
301 		if (dvb_retrieve_entry_prop(entry, DTV_FREQUENCY, &freq) < 0) {
302 			fprintf(stderr,
303 				_("WARNING: entry %d: frequency not found. skipping entry\n"),
304 				 line);
305 			continue;
306 		}
307 
308 		/* Output channel name */
309 		fprintf(fp, "%s", entry->channel);
310 		if (entry->vchannel)
311 			fprintf(fp, ",%s", entry->vchannel);
312 		fprintf(fp, ":");
313 
314 		/*
315 		 * Output frequency:
316 		 *	in kHz for terrestrial/cable
317 		 *	in MHz for satellite
318 		 */
319 		fprintf(fp, "%i:", freq / 1000);
320 
321 		/* Output modulation parameters */
322 		fmt = &formats[i];
323 		for (i = 0; i < fmt->size; i++) {
324 			table = &fmt->table[i];
325 
326 			for (j = 0; j < entry->n_props; j++)
327 				if (entry->props[j].cmd == table->prop)
328 					break;
329 
330 			if (!table->size || j >= entry->n_props)
331 				continue;
332 
333 			data = entry->props[j].u.data;
334 
335 			if (table->prop == DTV_BANDWIDTH_HZ) {
336 				for (j = 0; j < ARRAY_SIZE(fe_bandwidth_name); j++) {
337 					if (fe_bandwidth_name[j] == data) {
338 						data = j;
339 						break;
340 					}
341 				}
342 				if (j == ARRAY_SIZE(fe_bandwidth_name))
343 					data = BANDWIDTH_AUTO;
344 			}
345 			if (data >= table->size) {
346 				sprintf(err_msg,
347 						_("value not supported"));
348 				goto error;
349 			}
350 
351 			fprintf(fp, "%s", table->table[data]);
352 		}
353 		fprintf(fp, ":");
354 
355 		/*
356 		 * Output sources configuration for VDR
357 		 *
358 		 *   S (satellite) xy.z (orbital position in degrees) E or W (east or west)
359 		 *
360 		 *   FIXME: in case of ATSC we use "A", this is what w_scan does
361 		 */
362 
363 		if (entry->location) {
364 			switch(delsys) {
365 			case SYS_DVBS:
366 			case SYS_DVBS2:
367 				fprintf(fp, "%s", entry->location);
368 				break;
369 			default:
370 				fprintf(fp, "%s", id);
371 				break;
372 			}
373 		} else {
374 			fprintf(fp, "%s", id);
375 		}
376 		fprintf(fp, ":");
377 
378 		/* Output symbol rate */
379 		srate = 27500000;
380 		switch(delsys) {
381 		case SYS_DVBT:
382 			srate = 0;
383 			break;
384 		case SYS_DVBS:
385 		case SYS_DVBS2:
386 		case SYS_DVBC_ANNEX_A:
387 			if (dvb_retrieve_entry_prop(entry, DTV_SYMBOL_RATE, &srate) < 0) {
388 				sprintf(err_msg,
389 						_("symbol rate not found"));
390 				goto error;
391 			}
392 		}
393 		fprintf(fp, "%d:", srate / 1000);
394 
395 		/* Output video PID(s) */
396 		for (i = 0; i < entry->video_pid_len; i++) {
397 			if (i)
398 				fprintf(fp,",");
399 			fprintf(fp, "%d", entry->video_pid[i]);
400 		}
401 		if (!i)
402 			fprintf(fp, "0");
403 		fprintf(fp, ":");
404 
405 		/* Output audio PID(s) */
406 		for (i = 0; i < entry->audio_pid_len; i++) {
407 			if (i)
408 				fprintf(fp,",");
409 			fprintf(fp, "%d", entry->audio_pid[i]);
410 		}
411 		if (!i)
412 			fprintf(fp, "0");
413 		fprintf(fp, ":");
414 
415 		/* FIXME: Output teletex PID(s) */
416 		fprintf(fp, "0:");
417 
418 		/* Output Conditional Access - let VDR discover it */
419 		fprintf(fp, "0:");
420 
421 		/* Output Service ID */
422 		fprintf(fp, "%d:", entry->service_id);
423 
424 		/* Output Network ID */
425 		fprintf(fp, "%d:", entry->network_id);
426 
427 		/* Output Transport Stream ID */
428 		fprintf(fp, "%d:", entry->transport_id);
429 
430 		/* Output Radio ID
431 		 * this is the last entry, tagged bei a new line (not a colon!)
432 		 */
433 		fprintf(fp, "0\n");
434 		line++;
435 	};
436 	fclose (fp);
437 	return 0;
438 
439 error:
440 	fprintf(stderr, _("ERROR: %s while parsing entry %d of %s\n"),
441 		 err_msg, line, fname);
442 	fclose(fp);
443 	return -1;
444 }
445