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