]> git.sesse.net Git - decode_sdt/blob - decode_sdt.cc
Deduplication was too aggressive; fix to make it on channel instead.
[decode_sdt] / decode_sdt.cc
1 /*
2  * Decode SDT and NIT from a TS stream (for instance dvr0), and output in a
3  * relatively machine-readable format.
4  *
5  * Based on the decode_sdt.c example from libdvbpsi, and uses some source code
6  * from MuMuDVB.
7  *
8  * Copyright (C):
9  *    2001-2011 VideoLAN
10  *    Dave Chapman <dave@dchapman.com> 2001, 2002.
11  *    2004-2011 Brice DUBOST 
12  *    2013 Steinar H. Gunderson
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdbool.h>
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <iconv.h>
37
38 #include <stdint.h>
39
40 #include <dvbpsi/dvbpsi.h>
41 #include <dvbpsi/psi.h>
42 #include <dvbpsi/demux.h>
43 #include <dvbpsi/descriptor.h>
44 #include <dvbpsi/sdt.h>
45 #include <dvbpsi/nit.h>
46
47 #include <set>
48
49 static int packet_num = 0;
50 static int last_used_packet_num = 0;
51
52 struct channel_id {
53         int i_network_id;
54         int i_service_id;
55         int i_extension;
56
57         bool operator< (const channel_id &other) const {
58                 if (i_network_id != other.i_network_id)
59                         return (i_network_id < other.i_network_id);
60                 if (i_service_id != other.i_service_id)
61                         return (i_service_id < other.i_service_id);
62                 return (i_extension < other.i_extension);
63         }
64 };
65
66 /**
67 @brief The different encodings that can be used
68 Cf EN 300 468 Annex A (I used v1.9.1)
69  */
70 static const char *encodings_en300468[] ={
71         "ISO8859-1",
72         "ISO8859-2",
73         "ISO8859-3",
74         "ISO8859-4",
75         "ISO8859-5",
76         "ISO8859-6",
77         "ISO8859-7",
78         "ISO8859-8",
79         "ISO8859-9",
80         "ISO8859-10",
81         "ISO8859-11",
82         "ISO8859-12",
83         "ISO8859-13",
84         "ISO8859-14",
85         "ISO8859-15",
86         "ISO-10646", //control char 0x11
87         "GB2312",    //control char 0x13
88         "BIG5",      //control char 0x14
89         "ISO-10646/UTF8",      //control char 0x15
90 };
91
92 // Convert text according to EN 300 468 annex A.
93 static int convert_en300468_string(char *string, int max_len)
94 {
95         int encoding_control_char = 8; //cf encodings_en300468
96         char *dest;
97         char *tempdest, *tempbuf;
98         unsigned char *src;
99         /* remove control characters and convert to UTF-8 the channel name */
100         //If no channel encoding is specified, it seems that most of the broadcasters
101         //uses ISO/IEC 8859-9. But the norm (EN 300 468) said that it should be Latin-1 (ISO/IEC 6937 + euro)
102
103         //temporary buffers allocation
104         tempdest = tempbuf = (char *)malloc(sizeof(char)*2*strlen(string));
105         if (tempdest==NULL) {
106                 return -1;
107         }
108
109         int len = 0;
110         for (src = (unsigned char *)string; *src; src++) {
111                 if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f)) {
112                         //We copy non-control characters
113                         *tempdest++ = *src;
114                         len++;
115                 } else if (*src <= 0x20) {
116                         //control character recognition based on EN 300 468 v1.9.1 Annex A
117                         if (*src <= 0x0b) {
118                                 encoding_control_char=(int) *src+4-1;
119                         } else if(*src == 0x10) { //ISO/IEC 8859 : See table A.4
120                                 src++;//we skip the current byte
121                                 src++;//This one is always set to 0
122                                 if (*src >= 0x01 && *src <=0x0f)
123                                         encoding_control_char=(int) *src-1;
124                         }
125                         else if (*src==0x11) // ISO/IEC 10646 : Basic Multilingual Plane
126                                 encoding_control_char = 15;
127                         else if (*src==0x13) // GB-2312-1980 : Simplified Chinese Character
128                                 encoding_control_char = 16;
129                         else if (*src==0x14) // Big5 subset of ISO/IEC 10646 : Traditional Chinese
130                                 encoding_control_char = 17;
131                         else if (*src==0x15) // UTF-8 encoding of ISO/IEC 10646 : Basic Multilingual Plane
132                                 encoding_control_char = 18;
133                 } 
134         }
135         //Conversion to utf8
136         iconv_t cd;
137         //we open the conversion table
138         cd = iconv_open("UTF8", encodings_en300468[encoding_control_char]);
139
140         size_t inSize, outSize = max_len;
141         inSize = len;
142         //pointers initialisation because iconv change them, we store
143         dest = string;
144         tempdest = tempbuf;
145         //conversion
146         iconv(cd, &tempdest, &inSize, &dest, &outSize);
147         *dest = '\0';
148         free(tempbuf);
149         iconv_close(cd);
150
151         return encoding_control_char;
152 }
153
154
155 static bool ReadPacket(int i_fd, uint8_t * p_dst)
156 {
157         static uint8_t buf[188 * 100];
158         static int in_buf_start = 0;
159         static int in_buf_end = 0;
160         
161 has_stuff:
162         // Skip to the first 0x47 byte.
163         while (in_buf_end > in_buf_start && buf[in_buf_start] != 0x47) {
164                 ++in_buf_start;
165         }
166
167         if (in_buf_end - in_buf_start >= 188) {
168                 memcpy(p_dst, buf + in_buf_start, 188);
169                 in_buf_start += 188;
170                 return true;
171         }
172
173         // Check if there's a partial packet in the buffer.
174         int partial_len = in_buf_end - in_buf_start;
175         if (partial_len == 0) {
176                 in_buf_start = in_buf_end = 0;
177         } else if (partial_len > 0) {
178                 memmove(buf, buf + in_buf_start, partial_len);
179                 in_buf_start = 0;
180                 in_buf_end = partial_len;
181         }
182
183         int ret = read(i_fd, buf + in_buf_start, sizeof(buf) - in_buf_end);
184         if (ret == -1) {
185                 perror("read");
186                 return false;
187         }
188         if (ret == 0) {
189                 return false;
190         }
191
192         in_buf_end += ret;
193
194         goto has_stuff;
195 }
196
197
198 static void ParseSDT(void *p_zero, dvbpsi_sdt_t * p_sdt)
199 {
200         dvbpsi_sdt_service_t *p_service = p_sdt->p_first_service;
201         for (dvbpsi_sdt_service_t * p_service = p_sdt->p_first_service;
202              p_service != NULL;
203              p_service = p_service->p_next) {
204                 int service_type = -1;
205                 char provider_name[256] = { 0 };
206                 char channel_name[256] = { 0 };
207                 dvbpsi_descriptor_t *p_descriptor =
208                     p_service->p_first_descriptor;
209
210                 // Search for a Service Descriptor (0x48), which contains the provider and channel names.
211                 for (dvbpsi_descriptor_t * p_descriptor = p_service->p_first_descriptor;
212                      p_descriptor != NULL;
213                      p_descriptor = p_descriptor->p_next) {
214                         uint8_t *ptr;
215                         int i, len;
216                         if (p_descriptor->i_tag != 0x48) {
217                                 continue;
218                         }
219                         service_type = p_descriptor->p_data[0];
220                         ptr = p_descriptor->p_data + 1;
221
222                         len = *ptr++;
223                         memcpy(provider_name, ptr, len);
224                         provider_name[len] = 0;
225                         ptr += len;
226
227                         len = *ptr++;
228                         memcpy(channel_name, ptr, len);
229                         channel_name[len] = 0;
230
231                         convert_en300468_string(provider_name, sizeof(provider_name));
232                         convert_en300468_string(channel_name, sizeof(channel_name));
233                 }
234
235                 // Show television types only.
236                 if (service_type == 0x01 || service_type == 0x11 || service_type == 0x16 ||
237                     service_type == 0x19 || service_type == 0x1c) {
238                         // Do our own deduplication, since libdvbpsi only deduplicates against
239                         // the last version seen, not the entire history.
240                         static std::set<channel_id> seen_channels;
241                         channel_id id = { p_sdt->i_network_id, p_service->i_service_id, p_sdt->i_extension };
242                         if (seen_channels.insert(id).second) {
243                                 last_used_packet_num = packet_num;
244                                 printf("sid %d/%d: ts_id=%d, provider_name=\"%s\", channel_name=\"%s\"\n",
245                                      p_sdt->i_network_id, p_service->i_service_id,
246                                      p_sdt->i_extension, provider_name,
247                                      channel_name);
248                         }
249                 }
250         }
251         dvbpsi_sdt_delete(p_sdt);
252 }
253
254 // little endian assumed
255 typedef struct {
256         uint8_t frequency_4;
257         uint8_t frequency_3;
258         uint8_t frequency_2;
259         uint8_t frequency_1;
260         uint16_t orbital_position;      // big-endian
261         uint8_t modulation_type : 2;
262         uint8_t modulation_system : 1;
263         uint8_t roll_off : 2;
264         uint8_t polarization : 2;
265         uint8_t west_east_flag : 1;
266         uint8_t symbol_rate_12;
267         uint8_t symbol_rate_34;
268         uint8_t symbol_rate_56;
269         uint8_t FEC_inner : 4;
270         uint8_t symbol_rate_7 : 4;
271 } descr_sat_delivery_t;
272
273 // Canal Digital stores their LCN in the NIT (Wikipedia says some do it in
274 // the BAT). I haven't been able to find any format for this, even in NorDIG's
275 // specification, so this is done by visual inspection.
276 static void ParseLCN(int network_id, const dvbpsi_descriptor_t *p_descriptor)
277 {
278         uint8_t *ptr = p_descriptor->p_data;
279         int channel_list_id = *ptr++;
280
281         // Skip the name and the three-letter country code.
282         int len = *ptr++;
283         ptr += len + 3;
284
285         // The LCN itself.
286         len = *ptr++;
287         for (int i = 0; i < len; i += 4) {
288                 int sid = (ptr[i] << 8) | ptr[i + 1];
289                 int lcn = ((ptr[i + 2] << 8) | ptr[i + 3]) & 0x3fff;
290                 printf("lcn %d/%d/%d: lcn=%d\n", network_id, channel_list_id, sid, lcn);
291         }
292 }
293
294 static void ParseDSD(const dvbpsi_nit_t *p_nit, const dvbpsi_nit_ts_t *p_ts, const dvbpsi_descriptor_t *p_descriptor)
295 {
296         char buf[256], *ptr;
297         int i, len;
298         descr_sat_delivery_t *d =
299                 (descr_sat_delivery_t *) p_descriptor->p_data;
300         printf("ts %d/%d: ", p_nit->i_network_id, p_ts->i_ts_id);
301         printf("freq=%x%02x%02x, ", d->frequency_4,
302                         d->frequency_3, d->frequency_2,
303                         d->frequency_1);
304         printf("pol=%c, ", "HVLR"[d->polarization]);
305         if (d->modulation_system) {
306                 printf("delivery_system=DVBS2, ");
307         } else {
308                 printf("delivery_system=DVBS, ");
309         }
310         if (d->modulation_type == 0) {
311                 printf("modulation=QAMAUTO, ");
312         } else if (d->modulation_type == 1) {
313                 printf("modulation=QPSK, ");
314         } else if (d->modulation_type == 2) {
315                 printf("modulation=8PSK, ");
316         } else if (d->modulation_type == 3) {
317                 printf("modulation=QAM64, ");
318         }
319         printf("srate=%x%02x%02x, ", d->symbol_rate_12, d->symbol_rate_34, d->symbol_rate_56);
320         if (d->FEC_inner == 0) {
321                 printf("coderate=auto");
322         } else if (d->FEC_inner == 1) {
323                 printf("coderate=1/2");
324         } else if (d->FEC_inner == 2) {
325                 printf("coderate=2/3");
326         } else if (d->FEC_inner == 3) {
327                 printf("coderate=3/4");
328         } else if (d->FEC_inner == 4) {
329                 printf("coderate=5/6");
330         } else if (d->FEC_inner == 5) {
331                 printf("coderate=7/8");
332         } else if (d->FEC_inner == 6) {
333                 printf("coderate=8/9");
334         } else if (d->FEC_inner == 7) {
335                 printf("coderate=3/5");
336         } else if (d->FEC_inner == 8) {
337                 printf("coderate=4/5");
338         } else if (d->FEC_inner == 9) {
339                 printf("coderate=9/10");
340         } else {
341                 printf("coderate=none");
342         }
343         printf("\n");
344 }
345
346 static void ParseNIT(void *p_zero, dvbpsi_nit_t * p_nit)
347 {
348         last_used_packet_num = packet_num;
349         for (dvbpsi_nit_ts_t *p_ts = p_nit->p_first_ts;
350              p_ts != NULL;
351              p_ts = p_ts->p_next) {
352                 // Search through the descriptors until we find one of type 0x43
353                 // (Delivery System Descriptor).
354                 for (dvbpsi_descriptor_t *p_descriptor = p_ts->p_first_descriptor;
355                      p_descriptor != NULL;
356                      p_descriptor = p_descriptor->p_next) {
357                         if (p_descriptor->i_tag == 0x87) {
358                                 ParseLCN(p_nit->i_network_id, p_descriptor);
359                         }
360                         if (p_descriptor->i_tag == 0x43) {
361                                 ParseDSD(p_nit, p_ts, p_descriptor);
362                         }
363                 }
364         }
365         dvbpsi_nit_delete(p_nit);
366 }
367
368 /*****************************************************************************
369  * DVBPSI messaging callback
370  *****************************************************************************/
371 static void message(dvbpsi_t * handle, const dvbpsi_msg_level_t level,
372                     const char *msg)
373 {
374         switch (level) {
375         case DVBPSI_MSG_ERROR:
376                 fprintf(stderr, "Error: ");
377                 break;
378         case DVBPSI_MSG_WARN:
379                 fprintf(stderr, "Warning: ");
380                 break;
381         case DVBPSI_MSG_DEBUG:
382                 fprintf(stderr, "Debug: ");
383                 break;
384         default:                /* do nothing */
385                 return;
386         }
387         fprintf(stderr, "%s\n", msg);
388 }
389
390 /*****************************************************************************
391  * NewSubtable
392  *****************************************************************************/
393 static void NewSubtableNIT(dvbpsi_t * p_dvbpsi, uint8_t i_table_id,
394                            uint16_t i_extension, void *p_zero)
395 {
396         if (i_table_id == 0x40) {
397                 if (!dvbpsi_nit_attach(p_dvbpsi, i_table_id, i_extension, ParseNIT, NULL))
398                         fprintf(stderr, "Failed to attach PSI subdecoder\n");
399         }
400 }
401
402 /*****************************************************************************
403  * NewSubtable
404  *****************************************************************************/
405 static void NewSubtableSDT(dvbpsi_t * p_dvbpsi, uint8_t i_table_id,
406                            uint16_t i_extension, void *p_zero)
407 {
408         if (i_table_id == 0x42 || i_table_id == 0x46) {
409                 if (!dvbpsi_sdt_attach(p_dvbpsi, i_table_id, i_extension, ParseSDT, NULL))
410                         fprintf(stderr, "Failed to attach SDT subdecoder\n");
411         }
412 }
413
414 /*****************************************************************************
415  * main
416  *****************************************************************************/
417 int main(int i_argc, char *pa_argv[])
418 {
419         int i_fd;
420         uint8_t data[188];
421         dvbpsi_t *p_dvbpsi_sdt, *p_dvbpsi_nit;
422         bool b_ok;
423
424         if (i_argc != 2)
425                 return 1;
426
427         i_fd = open(pa_argv[1], 0);
428         if (i_fd < 0)
429                 return 1;
430
431         p_dvbpsi_sdt = dvbpsi_new(&message, DVBPSI_MSG_ERROR);
432         if (p_dvbpsi_sdt == NULL)
433                 goto out;
434
435         p_dvbpsi_nit = dvbpsi_new(&message, DVBPSI_MSG_ERROR);
436         if (p_dvbpsi_nit == NULL)
437                 goto out;
438
439         if (!dvbpsi_AttachDemux(p_dvbpsi_sdt, NewSubtableSDT, NULL))
440                 goto out;
441
442         if (!dvbpsi_AttachDemux(p_dvbpsi_nit, NewSubtableNIT, NULL))
443                 goto out;
444
445         while (ReadPacket(i_fd, data)) {
446                 uint16_t i_pid = ((uint16_t)(data[1] & 0x1f) << 8) + data[2];
447                 if (i_pid == 0x10)
448                         dvbpsi_packet_push(p_dvbpsi_nit, data);
449                 if (i_pid == 0x11)
450                         dvbpsi_packet_push(p_dvbpsi_sdt, data);
451
452                 // After 100k packets with no new information, we assume we are done.
453                 ++packet_num;
454                 if ((packet_num - last_used_packet_num) >= 100000)
455                         break;
456         }
457
458 out:
459         if (p_dvbpsi_sdt) {
460                 dvbpsi_DetachDemux(p_dvbpsi_sdt);
461                 dvbpsi_delete(p_dvbpsi_sdt);
462         }
463         if (p_dvbpsi_nit) {
464                 dvbpsi_DetachDemux(p_dvbpsi_nit);
465                 dvbpsi_delete(p_dvbpsi_nit);
466         }
467         close(i_fd);
468         return 0;
469 }