libtsfuncs is a library for mpeg PSI parsing and generation. https://georgi.unixsol.org/programs/libtsfuncs/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

descs.c 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /*
  2. * MPEG/DVB descriptor parsing
  3. * Copyright (C) 2010-2011 Unix Solutions Ltd.
  4. *
  5. * Released under MIT license.
  6. * See LICENSE-MIT.txt for license terms.
  7. */
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <netdb.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14. #include <netdb.h>
  15. #include "tsfuncs.h"
  16. static void dvb_print_string(char *pad, char *prefix, uint8_t *input, int text_length) {
  17. if (text_length < 0) {
  18. ts_LOGf("%s !!! %s text_length < 0, %d\n", pad, prefix, text_length);
  19. return;
  20. }
  21. if (text_length == 0) {
  22. ts_LOGf("%s %s \"\" (size: %d)\n", pad, prefix, text_length);
  23. return;
  24. }
  25. char *text = calloc(1, text_length + 1);
  26. memcpy(text, input, text_length);
  27. if (text[0] < 32)
  28. ts_LOGf("%s %s \"%s\" (charset: 0x%02x size: %d)\n", pad, prefix, text+1, text[0], text_length-1);
  29. else
  30. ts_LOGf("%s %s \"%s\" (size: %d)\n", pad, prefix, text, text_length);
  31. free(text);
  32. }
  33. void ts_descriptor_dump(uint8_t *desc_data, int desc_data_len) {
  34. char *pad = " * ";
  35. uint8_t *data = desc_data;
  36. int data_len = desc_data_len;
  37. while (data_len >= 2) {
  38. int i;
  39. uint8_t tag = data[0];
  40. uint8_t this_length = data[1];
  41. // ts_LOGf("%sDescriptor tag: 0x%02x (%d) size: %d\n", padA, tag, tag, this_length);
  42. data += 2;
  43. data_len -= 2;
  44. if (this_length > data_len) {
  45. // Not much we can do - try giving up?
  46. ts_LOGf("%s!!! Descriptor 0x%02x says length %d, but only %d bytes left\n", pad, tag, this_length, data_len);
  47. return;
  48. }
  49. switch (tag) {
  50. case 2: { // Video stream descriptor
  51. char *dump = ts_hex_dump(data, this_length, 0);
  52. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Video stream descriptor: %s\n", pad, tag, tag, this_length, dump);
  53. free(dump);
  54. struct {
  55. uint8_t multiple_frame_rate_flag : 1,
  56. frame_rate_code : 4,
  57. mpeg1_only_flag : 1,
  58. constraint_parameter_flag : 1,
  59. still_picture_flag : 1;
  60. uint8_t profile_and_level_indication;
  61. uint8_t chroma_format : 2,
  62. frame_rate_extension_flag : 1,
  63. reserved : 5;
  64. uint8_t escape:1, profile:3, level:4;
  65. } vs;
  66. if (this_length >= 1) {
  67. vs.multiple_frame_rate_flag = bit_on(data[0], bit_8);
  68. vs.frame_rate_code = (data[0] &~ 0x80) >> 3; // 1xxxx111
  69. vs.mpeg1_only_flag = bit_on(data[0], bit_3);
  70. vs.constraint_parameter_flag = bit_on(data[0], bit_2);
  71. vs.still_picture_flag = bit_on(data[0], bit_1);
  72. ts_LOGf("%s - multiple_frame_rate_flag : %d\n", pad, vs.multiple_frame_rate_flag);
  73. ts_LOGf("%s - frame_rate_code : %d (%s)\n", pad, vs.frame_rate_code,
  74. vs.frame_rate_code == 0 ? "forbidden" :
  75. vs.frame_rate_code == 1 ? "23.976" :
  76. vs.frame_rate_code == 2 ? "24.00" :
  77. vs.frame_rate_code == 3 ? "25.00" :
  78. vs.frame_rate_code == 4 ? "29.97" :
  79. vs.frame_rate_code == 5 ? "30.00" :
  80. vs.frame_rate_code == 6 ? "50.00" :
  81. vs.frame_rate_code == 7 ? "59.94" :
  82. vs.frame_rate_code == 8 ? "60.00" : "reserved"
  83. );
  84. ts_LOGf("%s - mpeg1_only_flag : %d\n", pad, vs.mpeg1_only_flag);
  85. ts_LOGf("%s - constraint_parameter_flag : %d\n", pad, vs.constraint_parameter_flag);
  86. ts_LOGf("%s - still_picture_flag : %d\n", pad, vs.still_picture_flag);
  87. }
  88. if (this_length >= 2 && vs.mpeg1_only_flag == 0) {
  89. vs.profile_and_level_indication = data[1];
  90. vs.chroma_format = data[2] >> 6; // xx111111
  91. vs.frame_rate_extension_flag = bit_on(data[2], bit_6); // 11x11111
  92. vs.reserved = data[2] &~ 0xE0; // 111xxxxx
  93. vs.profile = (vs.profile_and_level_indication &~ 0x8f) >> 4; // x111xxxx
  94. vs.level = vs.profile_and_level_indication &~ 0xf0; // xxxx1111
  95. ts_LOGf("%s - profile_and_level_indication : 0x%02x, Profile: %d (%s), Level: %d (%s)\n", pad,
  96. vs.profile_and_level_indication,
  97. vs.profile,
  98. vs.profile == 1 ? "High" :
  99. vs.profile == 2 ? "Spatially Scalable" :
  100. vs.profile == 3 ? "SNR Scalable" :
  101. vs.profile == 4 ? "Main" :
  102. vs.profile == 5 ? "Simple" : "Reserved",
  103. vs.level,
  104. vs.level == 4 ? "High" :
  105. vs.level == 6 ? "High 1440" :
  106. vs.level == 8 ? "Main" :
  107. vs.level == 10 ? "Low" : "Reserved"
  108. );
  109. ts_LOGf("%s - chroma_format : %d (%s)\n", pad, vs.chroma_format,
  110. vs.chroma_format == 0 ? "reserved" :
  111. vs.chroma_format == 1 ? "4:2:0" :
  112. vs.chroma_format == 2 ? "4:2:2" :
  113. vs.chroma_format == 3 ? "4:4:4" : "unknown"
  114. );
  115. ts_LOGf("%s - frame_rate_extension_flag : %d\n", pad, vs.frame_rate_extension_flag);
  116. ts_LOGf("%s - reserved : 0x%x\n", pad, vs.reserved);
  117. }
  118. break;
  119. }
  120. case 3: { // Audio stream descriptor
  121. char *dump = ts_hex_dump(data, this_length, 0);
  122. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Audio stream descriptor: %s\n", pad, tag, tag, this_length, dump);
  123. free(dump);
  124. struct {
  125. uint8_t free_format_flag : 1,
  126. ID : 1,
  127. layer : 2,
  128. vbr_flag : 1,
  129. reserved : 3;
  130. } as;
  131. if (this_length >= 1) {
  132. as.free_format_flag = bit_on(data[0], bit_8);
  133. as.ID = bit_on(data[0], bit_7);
  134. as.layer = (data[0] &~ 0xcf) >> 4; // 11xx1111
  135. as.vbr_flag = bit_on(data[0], bit_4);
  136. as.reserved = data[0] &~ 0xf0; // 1111xxxx
  137. ts_LOGf("%s - free_format_flag : %d\n", pad, as.free_format_flag);
  138. ts_LOGf("%s - ID : %d (%s)\n", pad, as.ID, as.ID ? "MPEG Audio" : "Other");
  139. ts_LOGf("%s - layer : %d (%s)\n", pad, as.layer,
  140. as.layer == 0 ? "reserved" :
  141. as.layer == 1 ? "Layer III" :
  142. as.layer == 2 ? "Layer II" :
  143. as.layer == 3 ? "Layer I" : "reserved"
  144. );
  145. ts_LOGf("%s - vbr_audio_flag : %d\n", pad, as.vbr_flag);
  146. ts_LOGf("%s - reserved : 0x%x\n", pad, as.reserved);
  147. }
  148. break;
  149. }
  150. case 5: { // Registration descriptor
  151. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Registration descriptor\n", pad, tag, tag, this_length);
  152. uint32_t reg_ident = data[0] << 24;
  153. reg_ident |= data[1] << 16;
  154. reg_ident |= data[2] << 8;
  155. reg_ident |= data[3];
  156. // See http://smpte-ra.org/mpegreg/mpegreg.html
  157. ts_LOGf("%s Registration ident: 0x%04x (%c%c%c%c)\n", pad, reg_ident,
  158. data[0], data[1], data[2], data[3]);
  159. dvb_print_string(pad, "Registration data :", &data[4], this_length-4);
  160. break;
  161. }
  162. case 6: { // I see this in data, so might as well "explain" it
  163. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Data stream alignment descriptor: Alignment type: 0x%02x (%s)\n",
  164. pad, tag, tag, this_length,
  165. data[0],
  166. data[0] == 0x00 ? "Reserved" :
  167. data[0] == 0x01 ? "Slice, or video access unit" :
  168. data[0] == 0x02 ? "Video access unit" :
  169. data[0] == 0x03 ? "GOP, or SEQ" :
  170. data[0] == 0x04 ? "SEQ" : "Reserved"
  171. );
  172. break;
  173. }
  174. case 9: { // CA descriptor
  175. uint16_t CA_ID = (data[0] << 8) | data[1];
  176. uint16_t CA_PID = ((data[2] & 0x1F) << 8) | data[3];
  177. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, CA descriptor: CAID 0x%04x (%02d) | CA PID 0x%04x (%d) | %s\n",
  178. pad,
  179. tag, tag,
  180. this_length,
  181. CA_ID, CA_ID,
  182. CA_PID, CA_PID,
  183. ts_get_CA_sys_txt(ts_get_CA_sys(CA_ID))
  184. );
  185. break;
  186. }
  187. case 10: { // We'll assume the length is a multiple of 4
  188. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Language descriptor:\n", pad, tag, tag, this_length);
  189. for (i=0; i<this_length/4; i++) {
  190. uint8_t audio_type = *(data+(i*4)+3);
  191. ts_LOGf("%s Lang: %c%c%c Type: (%d) %s\n", pad,
  192. *(data+(i*4)+0), *(data+(i*4)+1), *(data+(i*4)+2),
  193. audio_type,
  194. (audio_type == 0 ? "" :
  195. audio_type == 1 ? "clean effects" :
  196. audio_type == 2 ? "visual impaired commentary" :
  197. audio_type == 3 ? "clean effects" : "reserved")
  198. );
  199. }
  200. break;
  201. }
  202. case 14: { // Maximum bitrate descriptor
  203. uint32_t max_bitrate = ((data[0] &~ 0xc0) << 16) | (data[1] << 8) | data[2]; // 11xxxxxx xxxxxxxx xxxxxxxx
  204. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Maximum bitrate descriptor: 0x%04x (%u Bytes/sec)\n",
  205. pad, tag, tag, this_length,
  206. max_bitrate, max_bitrate * 50); // The value is in units of 50 bytes/second
  207. break;
  208. }
  209. case 0x40: { // Network name descriptor
  210. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Network name descriptor\n", pad, tag, tag, this_length);
  211. dvb_print_string(pad, "Network name:", &data[0], this_length);
  212. break;
  213. }
  214. case 0x41: { // service_list_descriptor
  215. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Service_list_descriptor\n", pad, tag, tag, this_length);
  216. for (i=0; i<this_length; i+=3) {
  217. uint16_t service_id;
  218. uint8_t service_type;
  219. service_id = data[i + 0] << 8;
  220. service_id |= data[i + 1];
  221. service_type = data[i + 2];
  222. ts_LOGf("%s Service_Id: 0x%04x (%d) Type: 0x%02x (%d)\n", pad,
  223. service_id, service_id,
  224. service_type, service_type);
  225. }
  226. break;
  227. }
  228. case 0x44: { // cable_delivery_descriptor
  229. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Cable_delivery_system descriptor\n", pad, tag, tag, this_length);
  230. uint32_t freq, symbol_rate;
  231. uint16_t reserved;
  232. uint8_t FEC_outer, FEC_inner;
  233. uint8_t modulation;
  234. freq = data[0] << 24;
  235. freq |= data[1] << 16;
  236. freq |= data[2] << 8;
  237. freq |= data[3]; // 4 bytes
  238. reserved = data[4] << 8; // 11111111
  239. reserved |= data[5] &~ 0x0f; // 1111xxxx
  240. FEC_outer = data[5] &~ 0xf0; // xxxx1111
  241. modulation = data[6]; // 1 byte
  242. symbol_rate = data[7] << 24;
  243. symbol_rate |= data[8] << 16;
  244. symbol_rate |= data[9] << 8; // 28 bits, the last 4 bits are FEC_inner
  245. symbol_rate |= data[10]; // 28 bits, the last 4 bits are FEC_inner
  246. FEC_inner = data[10] &~ 0xf0;
  247. ts_LOGf("%s Frequency : 0x%08x\n", pad, freq);
  248. ts_LOGf("%s FEC_outer : %s (0x%x) (reserved 0x%03x)\n" , pad,
  249. (FEC_outer == 0 ? "Not defined" :
  250. FEC_outer == 1 ? "no outer FEC coding" :
  251. FEC_outer == 2 ? "RS (204/188)" : "Reserved"),
  252. FEC_outer, reserved >> 4);
  253. ts_LOGf("%s Modulation : %s (%d/0x%02x)\n", pad,
  254. (modulation == 0 ? "Not defined" :
  255. modulation == 1 ? "16-QAM" :
  256. modulation == 2 ? "32-QAM" :
  257. modulation == 3 ? "64-QAM" :
  258. modulation == 4 ? "128-QAM" :
  259. modulation == 5 ? "256-QAM" : "Reserved"),
  260. modulation, modulation);
  261. ts_LOGf("%s symbol_rate: 0x%07x\n", pad, symbol_rate);
  262. ts_LOGf("%s FEC_inner : 0x%x\n" , pad, FEC_inner);
  263. break;
  264. }
  265. case 0x45: { // VBI_data_descriptor
  266. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, VBI_data descriptor (not decoded!!!)\n", pad, tag, tag, this_length);
  267. break;
  268. }
  269. case 0x48: { // Service descriptor
  270. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Service descriptor:\n", pad, tag, tag, this_length);
  271. ts_LOGf("%s Service type : %s\n", pad,
  272. data[0] == 0x01 ? "digital tv service" :
  273. data[0] == 0x02 ? "digital radio service" : "other");
  274. uint8_t provider_name_length = data[1];
  275. dvb_print_string(pad, "Provider name:", &data[2], provider_name_length);
  276. uint8_t service_name_length = data[2 + provider_name_length];
  277. dvb_print_string(pad, "Service name :", &data[3 + provider_name_length], service_name_length);
  278. break;
  279. }
  280. case 0x4d: { // short_event_descriptor
  281. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Short event descriptor:\n", pad, tag, tag, this_length);
  282. ts_LOGf("%s Lang : %c%c%c\n", pad, data[0], data[1], data[2]);
  283. uint8_t event_name_length = data[3];
  284. dvb_print_string(pad, "Event:", &data[4], event_name_length);
  285. uint8_t text_length = data[4 + event_name_length];
  286. dvb_print_string(pad, "Text :", &data[5 + event_name_length], text_length);
  287. break;
  288. }
  289. case 0x4e: { // extended_event_descriptor
  290. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Extended event descriptor:\n", pad, tag, tag, this_length);
  291. uint8_t desc_number = data[0] >> 4; // xxxx 1111
  292. uint8_t last_desc_number = data[0] &~ 0xf0; // 1111 xxxx
  293. ts_LOGf("%s Desc_number: %d Last Desc_number: %d\n", pad, desc_number, last_desc_number);
  294. ts_LOGf("%s Lang : %c%c%c\n", pad, data[1], data[2], data[3]);
  295. uint8_t items_length = data[4];
  296. ts_LOGf("%s ItemsLen: %d\n", pad, items_length);
  297. i = 5;
  298. while (i < items_length+5) {
  299. uint8_t item_desc_len = data[i++];
  300. if (item_desc_len)
  301. dvb_print_string(pad, " - Desc:", &data[i], item_desc_len);
  302. i += item_desc_len;
  303. uint8_t item_len = data[i++];
  304. if (item_len)
  305. dvb_print_string(pad, " - Text:", &data[i], item_len);
  306. i += item_len;
  307. }
  308. uint8_t text_length = data[5 + items_length];
  309. dvb_print_string(pad, "Text :", &data[6 + items_length], text_length);
  310. break;
  311. }
  312. case 0x50: { // Component descriptor
  313. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Component descriptor:\n", pad, tag, tag, this_length);
  314. uint8_t reserved = data[0] >> 4; // 1111 xxxx
  315. uint8_t stream_content = data[0] &~ 0xF0; // xxxx 1111
  316. uint8_t component_type = data[1]; // See Table 26 ETSI EN 300 468
  317. uint8_t component_tag = data[2];
  318. ts_LOGf("%s Stream_content: %d Component_type:%d Component_tag:%d res:0x%x\n", pad,
  319. stream_content, component_type, component_tag, reserved);
  320. ts_LOGf("%s Lang : %c%c%c\n", pad, data[3], data[4], data[5]);
  321. dvb_print_string(pad, "Text :", &data[6], this_length-6);
  322. break;
  323. }
  324. case 0x52: { // Stream identifier descriptor
  325. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Stream identifier descriptor: Component_tag: 0x%02x (%d)\n",
  326. pad, tag, tag, this_length,
  327. data[0], data[0]);
  328. break;
  329. }
  330. case 0x54: { // Content descriptor
  331. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Content descriptor:\n", pad, tag, tag, this_length);
  332. for (i=0; i<this_length; i+=2) {
  333. uint8_t c1 = data[i + 0] >> 4;
  334. uint8_t c2 = data[i + 0] &~ 0xf0;
  335. uint8_t u1 = data[i + 1] >> 4;
  336. uint8_t u2 = data[i + 1] &~ 0xf0;
  337. ts_LOGf("%s Content1: %d Content2: %d User1: %d User2: %d\n", pad, c1, c2, u1 ,u2);
  338. }
  339. break;
  340. }
  341. case 0x55: { // Parental rating descriptor
  342. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Parental rating descriptor:\n", pad, tag, tag, this_length);
  343. for (i=0; i<this_length; i+=4) {
  344. ts_LOGf("%s Country: %c%c%c\n", pad, data[i+0], data[i+1], data[i+2]);
  345. if (data[i+3] == 0)
  346. ts_LOGf("%s Rating : undefined\n", pad);
  347. else if (data[i+3] >= 0x01 && data[i+3] <= 0x0f)
  348. ts_LOGf("%s Rating : min age %d years\n", pad, data[i+3] + 3);
  349. else
  350. ts_LOGf("%s Rating : private - 0x%02x (%d)\n", pad, data[i+3], data[i+3]);
  351. }
  352. break;
  353. }
  354. case 0x56: { // teletext
  355. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Teletext descriptor:\n", pad, tag, tag, this_length);
  356. for (i=0; i<this_length; i+= 5) {
  357. int teletext_type, teletext_magazine, teletext_page;
  358. ts_LOGf("%s Lang: %c%c%c\n", pad, data[i], data[i+1], data[i+2]);
  359. teletext_type = (data[i+3] & 0xF8) >> 3;
  360. teletext_magazine = (data[i+3] & 0x07);
  361. teletext_page = data[i+4];
  362. ts_LOGf("%s Type: %d, Desc: %s\n", pad, teletext_type,
  363. (teletext_type == 1 ? "Initial" :
  364. teletext_type == 2 ? "Subtitles" :
  365. teletext_type == 3 ? "Additional info" :
  366. teletext_type == 4 ? "Program schedule" :
  367. teletext_type == 5 ? "Hearing impaired subtitles" : "(reserved)")
  368. );
  369. ts_LOGf("%s Magazine: %d, Page: %d\n", pad, teletext_magazine, teletext_page);
  370. }
  371. break;
  372. }
  373. case 0x58: { // local_timeoffset
  374. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Local timeoffset descriptor\n", pad, tag, tag, this_length);
  375. if (this_length % 13 != 0) {
  376. ts_LOGf("%s !!! length %% 13 != 0 (%d)\n", pad, this_length);
  377. break;
  378. }
  379. while (this_length > 0) {
  380. uint16_t mjd, lto, lto_next;
  381. uint32_t bcd;
  382. time_t ts;
  383. struct tm tm;
  384. uint8_t region_id, reserved, polarity;
  385. ts_LOGf("%s Country code: %c%c%c\n", pad, data[0], data[1], data[2]);
  386. region_id = data[ 3] >> 2; // xxxxxx11
  387. reserved =(data[ 3] &~ 0xfd) >> 1; // 111111x1
  388. polarity =(data[ 3] &~ 0xfe); // 1111111x
  389. lto = data[ 4] << 8;
  390. lto |= data[ 5];
  391. mjd = data[ 6] << 8;
  392. mjd |= data[ 7];
  393. bcd = data[ 8] << 16;
  394. bcd |= data[ 9] << 8;
  395. bcd |= data[10];
  396. lto_next = data[11] << 8;
  397. lto_next |= data[12];
  398. ts = ts_time_decode_mjd(mjd, bcd, &tm);
  399. ts_LOGf("%s Region_id : %d\n", pad, region_id);
  400. ts_LOGf("%s Reserved : %d\n", pad, reserved);
  401. ts_LOGf("%s LTO polarity: %d\n", pad, polarity);
  402. ts_LOGf("%s LTO : %c%04x\n", pad, polarity ? '-' : '+', lto);
  403. ts_LOGf("%s Change time : (%04d-%02d-%02d %02d:%02d:%02d) /0x%04x%06x, %ld/\n", pad,
  404. tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
  405. tm.tm_hour, tm.tm_min, tm.tm_sec,
  406. mjd, bcd, ts);
  407. ts_LOGf("%s LTO next : %c%04x\n", pad, polarity ? '-' : '+', lto_next);
  408. data += 13;
  409. data_len -= this_length;
  410. this_length -= 13;
  411. }
  412. break;
  413. }
  414. case 0x59: {
  415. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Subtitling descriptor:\n", pad, tag, tag, this_length);
  416. for (i=0; i+8 <= this_length; i+=8) {
  417. char lang[4];
  418. unsigned int subtitling_type = data[i+3];
  419. unsigned int composition_page_id = (data[i+4] << 8) | data[i+5];
  420. unsigned int ancillary_page_id = (data[i+6] << 8) | data[i+7];
  421. lang[0] = data[i + 0];
  422. lang[1] = data[i + 1];
  423. lang[2] = data[i + 2];
  424. lang[3] = 0;
  425. ts_LOGf("%s Lang: %s, Sub_type: %u, Composition_page_id: %u, Ancillary_page_id: %u\n",
  426. pad, lang, subtitling_type, composition_page_id, ancillary_page_id);
  427. }
  428. break;
  429. }
  430. case 0x5f: { // Private NorDig descriptor
  431. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Private Nordig descriptor:\n", pad, tag, tag, this_length);
  432. for (i=0; i<this_length; i+=4) {
  433. uint8_t u1 = data[i + 0] ;
  434. uint8_t u2 = data[i + 1] ;
  435. uint8_t u3 = data[i + 2] ;
  436. uint8_t u4 = data[i + 3] ;
  437. ts_LOGf("%s Data1: 0x%02x Data2: %02x Data3: %02x Data4: %02x\n", pad, u1, u2, u3 ,u4);
  438. }
  439. break;
  440. }
  441. case 0x62: { // frequency_list_descriptor
  442. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Frequency_list_descriptor\n", pad, tag, tag, this_length);
  443. uint8_t reserved = data[0] >> 2; // 111111xx
  444. uint8_t coding_type = data[0] &~ 0xfc; // xxxxxx11
  445. ts_LOGf("%s Coding_type: %s (%d/0x%x) Reserved: 0x%x\n", pad,
  446. (coding_type == 0 ? "Not defined" :
  447. coding_type == 1 ? "Satellite" :
  448. coding_type == 2 ? "Cable" :
  449. coding_type == 3 ? "Terrestrial" : "Reserved"),
  450. coding_type, coding_type, reserved);
  451. for (i=1; i<this_length; i+=4) {
  452. uint32_t centre_freq;
  453. centre_freq = data[i + 0] << 24;
  454. centre_freq |= data[i + 1] << 16;
  455. centre_freq |= data[i + 2] << 8;
  456. centre_freq |= data[i + 3];
  457. ts_LOGf("%s Frequency: 0x%08x\n", pad, centre_freq);
  458. }
  459. break;
  460. }
  461. case 0x69: { // PDC descriptor
  462. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, PDC descriptor: Prg_id_label: 0x%02x%02x%02x\n",
  463. pad, tag, tag, this_length,
  464. data[0] &~ 0xf0, data[1], data[2]);
  465. break;
  466. }
  467. case 0x6a: {
  468. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, AC-3 descriptor\n", pad, tag, tag, this_length);
  469. break;
  470. }
  471. case 0x83: { // Private descriptor! LCN Logical channel descriptor
  472. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Logical channel descriptor\n", pad, tag, tag, this_length);
  473. for (i=0; i<this_length; i+=4) {
  474. uint16_t service_id;
  475. uint8_t visible;
  476. // uint8_t reserved1;
  477. uint16_t lcn;
  478. service_id = data[i + 0] << 8;
  479. service_id |= data[i + 1];
  480. visible = data[i+2] >> 7; // x1111111
  481. // reserved1 = data[i+2] &~ 0x80 >> 6; // 1x111111
  482. lcn = (data[i+2] &~ 0xc0) << 8; // 11xxxxxx
  483. lcn |= data[i+3]; // xxxxxxxx
  484. ts_LOGf("%s Service_ID: 0x%04x (%4d) LCN: %3d Visible: %d\n",
  485. pad, service_id, service_id, lcn, visible);
  486. }
  487. break;
  488. }
  489. default: {
  490. char *dump = ts_hex_dump(data, this_length, 0);
  491. ts_LOGf("%s*** Unknown Tag 0x%02x (%02d), sz: %d, data: %s\n", pad, tag, tag, this_length, dump);
  492. free(dump);
  493. break;
  494. }
  495. }
  496. data_len -= this_length;
  497. data += this_length;
  498. }
  499. }