libtsfuncs is a library for mpeg PSI parsing and generation. https://georgi.unixsol.org/programs/libtsfuncs/

descs.c 20KB

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