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

tsfuncs_descriptors.c 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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);
  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);
  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("Tag 0x%02x (%02d), sz: %d, CA descriptor: CAID 0x%04x (%02d) | CA PID 0x%04x (%d) | %s\n",
  171. tag, tag,
  172. this_length,
  173. CA_ID, CA_ID,
  174. CA_PID, CA_PID,
  175. ts_get_CA_sys_txt(ts_get_CA_sys(CA_ID))
  176. );
  177. break;
  178. }
  179. case 10: { // We'll assume the length is a multiple of 4
  180. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Language descriptor:\n", pad, tag, tag, this_length);
  181. for (i=0; i<this_length/4; i++) {
  182. uint8_t audio_type = *(data+(i*4)+3);
  183. ts_LOGf("%s Lang: %c%c%c Type: (%d) %s\n", pad,
  184. *(data+(i*4)+0), *(data+(i*4)+1), *(data+(i*4)+2),
  185. audio_type,
  186. (audio_type == 0 ? "" :
  187. audio_type == 1 ? "clean effects" :
  188. audio_type == 2 ? "visual impaired commentary" :
  189. audio_type == 3 ? "clean effects" : "reserved")
  190. );
  191. }
  192. break;
  193. }
  194. case 14: { // Maximum bitrate descriptor
  195. uint32_t max_bitrate = ((data[0] &~ 0xc0) << 16) | (data[1] << 8) | data[2]; // 11xxxxxx xxxxxxxx xxxxxxxx
  196. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Maximum bitrate descriptor: 0x%04x (%u Bytes/sec)\n",
  197. pad, tag, tag, this_length,
  198. max_bitrate, max_bitrate * 50); // The value is in units of 50 bytes/second
  199. break;
  200. }
  201. case 0x40: { // Network name descriptor
  202. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Network name descriptor\n", pad, tag, tag, this_length);
  203. dvb_print_string(pad, "Network name:", &data[0], this_length);
  204. break;
  205. }
  206. case 0x41: { // service_list_descriptor
  207. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Service_list_descriptor\n", pad, tag, tag, this_length);
  208. for (i=0; i<this_length; i+=3) {
  209. uint16_t service_id;
  210. uint8_t service_type;
  211. service_id = data[i + 0] << 8;
  212. service_id |= data[i + 1];
  213. service_type = data[i + 2];
  214. ts_LOGf("%s Service_Id: 0x%04x (%d) Type: 0x%02x (%d)\n", pad,
  215. service_id, service_id,
  216. service_type, service_type);
  217. }
  218. break;
  219. }
  220. case 0x44: { // cable_delivery_descriptor
  221. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Cable_delivery_system descriptor\n", pad, tag, tag, this_length);
  222. uint32_t freq, symbol_rate;
  223. uint16_t reserved;
  224. uint8_t FEC_outer, FEC_inner;
  225. uint8_t modulation;
  226. freq = data[0] << 24;
  227. freq |= data[1] << 16;
  228. freq |= data[2] << 8;
  229. freq |= data[3]; // 4 bytes
  230. reserved = data[4] << 8; // 11111111
  231. reserved |= data[5] &~ 0x0f; // 1111xxxx
  232. FEC_outer = data[5] &~ 0xf0; // xxxx1111
  233. modulation = data[6]; // 1 byte
  234. symbol_rate = data[7] << 24;
  235. symbol_rate |= data[8] << 16;
  236. symbol_rate |= data[9] << 8; // 28 bits, the last 4 bits are FEC_inner
  237. symbol_rate |= data[10]; // 28 bits, the last 4 bits are FEC_inner
  238. FEC_inner = data[10] &~ 0xf0;
  239. ts_LOGf("%s Frequency : 0x%08x\n", pad, freq);
  240. ts_LOGf("%s FEC_outer : %s (0x%x) (reserved 0x%03x)\n" , pad,
  241. (FEC_outer == 0 ? "Not defined" :
  242. FEC_outer == 1 ? "no outer FEC coding" :
  243. FEC_outer == 2 ? "RS (204/188)" : "Reserved"),
  244. FEC_outer, reserved >> 4);
  245. ts_LOGf("%s Modulation : %s (%d/0x%02x)\n", pad,
  246. (modulation == 0 ? "Not defined" :
  247. modulation == 1 ? "16-QAM" :
  248. modulation == 2 ? "32-QAM" :
  249. modulation == 3 ? "64-QAM" :
  250. modulation == 4 ? "128-QAM" :
  251. modulation == 5 ? "256-QAM" : "Reserved"),
  252. modulation, modulation);
  253. ts_LOGf("%s symbol_rate: 0x%07x\n", pad, symbol_rate);
  254. ts_LOGf("%s FEC_inner : 0x%x\n" , pad, FEC_inner);
  255. break;
  256. }
  257. case 0x45: { // VBI_data_descriptor
  258. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, VBI_data descriptor (not decoded!!!)\n", pad, tag, tag, this_length);
  259. break;
  260. }
  261. case 0x48: { // Service descriptor
  262. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Service descriptor:\n", pad, tag, tag, this_length);
  263. ts_LOGf("%s Service type : %s\n", pad,
  264. data[0] == 0x01 ? "digital tv service" :
  265. data[0] == 0x02 ? "digital radio service" : "other");
  266. uint8_t provider_name_length = data[1];
  267. dvb_print_string(pad, "Provider name:", &data[2], provider_name_length);
  268. uint8_t service_name_length = data[2 + provider_name_length];
  269. dvb_print_string(pad, "Service name :", &data[3 + provider_name_length], service_name_length);
  270. break;
  271. }
  272. case 0x4d: { // short_event_descriptor
  273. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Short event descriptor:\n", pad, tag, tag, this_length);
  274. ts_LOGf("%s Lang : %c%c%c\n", pad, data[0], data[1], data[2]);
  275. uint8_t event_name_length = data[3];
  276. dvb_print_string(pad, "Event:", &data[4], event_name_length);
  277. uint8_t text_length = data[4 + event_name_length];
  278. dvb_print_string(pad, "Text :", &data[5 + event_name_length], text_length);
  279. break;
  280. }
  281. case 0x4e: { // extended_event_descriptor
  282. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Extended event descriptor:\n", pad, tag, tag, this_length);
  283. uint8_t desc_number = data[0] >> 4; // xxxx 1111
  284. uint8_t last_desc_number = data[0] &~ 0xf0; // 1111 xxxx
  285. ts_LOGf("%s Desc_number: %d Last Desc_number: %d\n", pad, desc_number, last_desc_number);
  286. ts_LOGf("%s Lang : %c%c%c\n", pad, data[1], data[2], data[3]);
  287. uint8_t items_length = data[4];
  288. ts_LOGf("%s ItemsLen: %d\n", pad, items_length);
  289. i = 5;
  290. while (i < items_length+5) {
  291. uint8_t item_desc_len = data[i++];
  292. if (item_desc_len)
  293. dvb_print_string(pad, " - Desc:", &data[i], item_desc_len);
  294. i += item_desc_len;
  295. uint8_t item_len = data[i++];
  296. if (item_len)
  297. dvb_print_string(pad, " - Text:", &data[i], item_len);
  298. i += item_len;
  299. }
  300. uint8_t text_length = data[5 + items_length];
  301. dvb_print_string(pad, "Text :", &data[6 + items_length], text_length);
  302. break;
  303. }
  304. case 0x50: { // Component descriptor
  305. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Component descriptor:\n", pad, tag, tag, this_length);
  306. uint8_t reserved = data[0] >> 4; // 1111 xxxx
  307. uint8_t stream_content = data[0] &~ 0xF0; // xxxx 1111
  308. uint8_t component_type = data[1]; // See Table 26 ETSI EN 300 468
  309. uint8_t component_tag = data[2];
  310. ts_LOGf("%s Stream_content: %d Component_type:%d Component_tag:%d res:0x%x\n", pad,
  311. stream_content, component_type, component_tag, reserved);
  312. ts_LOGf("%s Lang : %c%c%c\n", pad, data[3], data[4], data[5]);
  313. dvb_print_string(pad, "Text :", &data[6], this_length-6);
  314. break;
  315. }
  316. case 0x52: { // Stream identifier descriptor
  317. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Stream identifier descriptor: Component_tag: 0x%02x (%d)\n",
  318. pad, tag, tag, this_length,
  319. data[0], data[0]);
  320. break;
  321. }
  322. case 0x54: { // Content descriptor
  323. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Content descriptor:\n", pad, tag, tag, this_length);
  324. for (i=0; i<this_length; i+=2) {
  325. uint8_t c1 = data[i + 0] >> 4;
  326. uint8_t c2 = data[i + 0] &~ 0xf0;
  327. uint8_t u1 = data[i + 1] >> 4;
  328. uint8_t u2 = data[i + 1] &~ 0xf0;
  329. ts_LOGf("%s Content1: %d Content2: %d User1: %d User2: %d\n", pad, c1, c2, u1 ,u2);
  330. }
  331. break;
  332. }
  333. case 0x55: { // Parental rating descriptor
  334. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Parental rating descriptor:\n", pad, tag, tag, this_length);
  335. for (i=0; i<this_length; i+=4) {
  336. ts_LOGf("%s Country: %c%c%c\n", pad, data[i+0], data[i+1], data[i+2]);
  337. if (data[i+3] == 0)
  338. ts_LOGf("%s Rating : undefined\n", pad);
  339. else if (data[i+3] >= 0x01 && data[i+3] <= 0x0f)
  340. ts_LOGf("%s Rating : min age %d years\n", pad, data[i+3] + 3);
  341. else
  342. ts_LOGf("%s Rating : private - 0x%02x (%d)\n", pad, data[i+3], data[i+3]);
  343. }
  344. break;
  345. }
  346. case 0x56: { // teletext
  347. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Teletext descriptor:\n", pad, tag, tag, this_length);
  348. for (i=0; i<this_length; i+= 5) {
  349. int teletext_type, teletext_magazine, teletext_page;
  350. ts_LOGf("%s Lang: %c%c%c\n", pad, data[i], data[i+1], data[i+2]);
  351. teletext_type = (data[i+3] & 0xF8) >> 3;
  352. teletext_magazine = (data[i+3] & 0x07);
  353. teletext_page = data[i+4];
  354. ts_LOGf("%s Type: %d, Desc: %s\n", pad, teletext_type,
  355. (teletext_type == 1 ? "Initial" :
  356. teletext_type == 2 ? "Subtitles" :
  357. teletext_type == 3 ? "Additional info" :
  358. teletext_type == 4 ? "Program schedule" :
  359. teletext_type == 5 ? "Hearing impaired subtitles" : "(reserved)")
  360. );
  361. ts_LOGf("%s Magazine: %d, Page: %d\n", pad, teletext_magazine, teletext_page);
  362. }
  363. break;
  364. }
  365. case 0x58: { // local_timeoffset
  366. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Local timeoffset descriptor\n", pad, tag, tag, this_length);
  367. if (this_length == 13) {
  368. uint16_t mjd, lto, lto_next;
  369. uint32_t bcd;
  370. time_t ts;
  371. struct tm tm;
  372. uint8_t region_id, reserved, polarity;
  373. ts_LOGf("%s Country code: %c%c%c\n", pad, data[0], data[1], data[2]);
  374. region_id = data[ 3] >> 2; // xxxxxx11
  375. reserved =(data[ 3] &~ 0xfd) >> 1; // 111111x1
  376. polarity =(data[ 3] &~ 0xfe); // 1111111x
  377. lto = data[ 4] << 8;
  378. lto |= data[ 5];
  379. mjd = data[ 6] << 8;
  380. mjd |= data[ 7];
  381. bcd = data[ 8] << 16;
  382. bcd |= data[ 9] << 8;
  383. bcd |= data[10];
  384. lto_next = data[11] << 8;
  385. lto_next |= data[12];
  386. ts = ts_time_decode_mjd(mjd, bcd, &tm);
  387. ts_LOGf("%s Region_id : %d\n", pad, region_id);
  388. ts_LOGf("%s Reserved : %d\n", pad, reserved);
  389. ts_LOGf("%s LTO polarity: %d\n", pad, polarity);
  390. ts_LOGf("%s LTO : %c%04x\n", pad, polarity ? '-' : '+', lto);
  391. ts_LOGf("%s Change time : (%04d-%02d-%02d %02d:%02d:%02d) /0x%04x%06x, %ld/\n", pad,
  392. tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
  393. tm.tm_hour, tm.tm_min, tm.tm_sec,
  394. mjd, bcd, ts);
  395. ts_LOGf("%s LTO next : %c%04x\n", pad, polarity ? '-' : '+', lto_next);
  396. } else {
  397. ts_LOGf("%s !!! length != 13 (%d)\n", pad, this_length);
  398. }
  399. break;
  400. }
  401. case 0x59: {
  402. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Subtitling descriptor:\n", pad, tag, tag, this_length);
  403. for (i=0; i+8 <= this_length; i+=8) {
  404. char lang[4];
  405. unsigned int subtitling_type = data[i+3];
  406. unsigned int composition_page_id = (data[i+4] << 8) | data[i+5];
  407. unsigned int ancillary_page_id = (data[i+6] << 8) | data[i+7];
  408. lang[0] = data[i + 0];
  409. lang[1] = data[i + 1];
  410. lang[2] = data[i + 2];
  411. lang[3] = 0;
  412. ts_LOGf("%s Lang: %s, Sub_type: %u, Composition_page_id: %u, Ancillary_page_id: %u\n",
  413. pad, lang, subtitling_type, composition_page_id, ancillary_page_id);
  414. }
  415. break;
  416. }
  417. case 0x62: { // frequency_list_descriptor
  418. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Frequency_list_descriptor\n", pad, tag, tag, this_length);
  419. uint8_t reserved = data[0] >> 2; // 111111xx
  420. uint8_t coding_type = data[0] &~ 0xfc; // xxxxxx11
  421. ts_LOGf("%s Coding_type: %s (%d/0x%x) Reserved: 0x%x\n", pad,
  422. (coding_type == 0 ? "Not defined" :
  423. coding_type == 1 ? "Satellite" :
  424. coding_type == 2 ? "Cable" :
  425. coding_type == 3 ? "Terrestrial" : "Reserved"),
  426. coding_type, coding_type, reserved);
  427. for (i=1; i<this_length; i+=4) {
  428. uint32_t centre_freq;
  429. centre_freq = data[i + 0] << 24;
  430. centre_freq |= data[i + 1] << 16;
  431. centre_freq |= data[i + 2] << 8;
  432. centre_freq |= data[i + 3];
  433. ts_LOGf("%s Frequency: 0x%08x\n", pad, centre_freq);
  434. }
  435. break;
  436. }
  437. case 0x69: { // PDC descriptor
  438. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, PDC descriptor: Prg_id_label: 0x%02x%02x%02x\n",
  439. pad, tag, tag, this_length,
  440. data[0] &~ 0xf0, data[1], data[2]);
  441. break;
  442. }
  443. case 0x6a: {
  444. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, AC-3 descriptor\n", pad, tag, tag, this_length);
  445. break;
  446. }
  447. /*
  448. case 0x87: { // Private descriptor! LCN Logical channel descriptor
  449. ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Logical channel descriptor\n", pad, tag, tag, this_length);
  450. for (i=0; i<= this_length; i+=4) {
  451. uint16_t service_id = (data[0+i] << 8) | data[1+i]; // xxxxxxxx xxxxxxxx
  452. uint8_t visible = (data[2+i] >> 7); // x1111111
  453. // uint8_t reserved1 = (data[2+i] &~ 0x80) >> 2; // 1xxxxx11
  454. uint16_t lcn = (data[2+i] &~ 0x3f); // 111111xx
  455. lcn |= data[3+i]; // xxxxxxxx
  456. ts_LOGf("%s Service_ID: 0x%04x (%4d) LCN: %3d Visible: %d\n",
  457. pad,
  458. service_id, service_id,
  459. lcn, visible);
  460. }
  461. break;
  462. }
  463. */
  464. default: {
  465. char *dump = ts_hex_dump(data, this_length);
  466. ts_LOGf("%s*** Unknown Tag 0x%02x (%02d), sz: %d, data: %s\n", pad, tag, tag, this_length, dump);
  467. free(dump);
  468. break;
  469. }
  470. }
  471. data_len -= this_length;
  472. data += this_length;
  473. }
  474. }