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.

tsfuncs_descriptors.c 20KB

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