| 1 | /********** |
| 2 | This library is free software; you can redistribute it and/or modify it under |
| 3 | the terms of the GNU Lesser General Public License as published by the |
| 4 | Free Software Foundation; either version 3 of the License, or (at your |
| 5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| 6 | |
| 7 | This library is distributed in the hope that it will be useful, but WITHOUT |
| 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| 10 | more details. |
| 11 | |
| 12 | You should have received a copy of the GNU Lesser General Public License |
| 13 | along with this library; if not, write to the Free Software Foundation, Inc., |
| 14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 15 | **********/ |
| 16 | // "liveMedia" |
| 17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| 18 | // A parser for a MPEG Transport Stream |
| 19 | // Implementation |
| 20 | |
| 21 | #include "MPEG2TransportStreamParser.hh" |
| 22 | |
| 23 | void MPEG2TransportStreamParser |
| 24 | ::parsePMT(PIDState_PMT* pidState, Boolean pusi, unsigned numDataBytes) { |
| 25 | #ifdef DEBUG_CONTENTS |
| 26 | fprintf(stderr, "\tProgram Map Table\n" ); |
| 27 | #endif |
| 28 | unsigned startPos = curOffset(); |
| 29 | |
| 30 | do { |
| 31 | if (pusi) { |
| 32 | u_int8_t pointer_field = get1Byte(); |
| 33 | skipBytes(pointer_field); // usually 0 |
| 34 | } |
| 35 | |
| 36 | u_int8_t table_id = get1Byte(); |
| 37 | if (table_id != 0x02) { |
| 38 | #ifdef DEBUG_ERRORS |
| 39 | fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): bad table_id: 0x%02x\n" , |
| 40 | pidState->PID, pusi, numDataBytes, table_id); |
| 41 | #endif |
| 42 | break; |
| 43 | } |
| 44 | |
| 45 | u_int16_t flagsPlusSection_length = get2Bytes(); |
| 46 | u_int16_t section_length = flagsPlusSection_length&0x0FFF; |
| 47 | #ifdef DEBUG_CONTENTS |
| 48 | fprintf(stderr, "\t\tsection_length: %d\n" , section_length); |
| 49 | #endif |
| 50 | if (section_length < 13/*too small for remaining fields + CRC*/ || |
| 51 | section_length > 1021/*as per specification*/) { |
| 52 | #ifdef DEBUG_ERRORS |
| 53 | fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): Bad section_length: %d\n" , |
| 54 | pidState->PID, pusi, numDataBytes, section_length); |
| 55 | #endif |
| 56 | break; |
| 57 | } |
| 58 | unsigned endPos = curOffset() + section_length; |
| 59 | if (endPos - startPos > numDataBytes) { |
| 60 | #ifdef DEBUG_ERRORS |
| 61 | fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): section_length %d gives us a total size %d that's too large!\n" , |
| 62 | pidState->PID, pusi, numDataBytes, section_length, endPos - startPos); |
| 63 | #endif |
| 64 | break; |
| 65 | } |
| 66 | |
| 67 | u_int16_t program_number = get2Bytes(); |
| 68 | if (program_number != pidState->program_number) { |
| 69 | #ifdef DEBUG_ERRORS |
| 70 | fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): program_number %d does not match the value %d that was given to us in the PAT!\n" , |
| 71 | pidState->PID, pusi, numDataBytes, program_number, pidState->program_number); |
| 72 | #endif |
| 73 | break; |
| 74 | } |
| 75 | #ifdef DEBUG_CONTENTS |
| 76 | fprintf(stderr, "\t\tprogram_number: %d\n" , program_number); |
| 77 | |
| 78 | u_int8_t version_number_byte = get1Byte(); |
| 79 | u_int8_t version_number = (version_number_byte&0x1E)>>1; |
| 80 | u_int8_t section_number = get1Byte(); |
| 81 | u_int8_t last_section_number = get1Byte(); |
| 82 | fprintf(stderr, "\t\tversion_number: %d; section_number: %d; last_section_number: %d\n" , |
| 83 | version_number, section_number, last_section_number); |
| 84 | u_int16_t PCR_PID = get2Bytes(); PCR_PID &= 0x1FFF; |
| 85 | fprintf(stderr, "\t\tPCR_PID: 0x%04x\n" , PCR_PID); |
| 86 | #else |
| 87 | skipBytes(5); |
| 88 | #endif |
| 89 | |
| 90 | u_int16_t program_info_length = get2Bytes(); program_info_length &= 0x0FFF; |
| 91 | #ifdef DEBUG_CONTENTS |
| 92 | fprintf(stderr, "\t\tprogram_info_length: %d\n" , program_info_length); |
| 93 | #endif |
| 94 | unsigned endOfDescriptors = curOffset() + program_info_length; |
| 95 | if (endOfDescriptors + 4/*CRC*/ - startPos > numDataBytes) { |
| 96 | #ifdef DEBUG_ERRORS |
| 97 | fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): program_info_length %d gives us a total size %d that's too large!\n" , |
| 98 | pidState->PID, pusi, numDataBytes, program_info_length, endOfDescriptors + 4 - startPos); |
| 99 | #endif |
| 100 | break; |
| 101 | } |
| 102 | parseStreamDescriptors(program_info_length); |
| 103 | |
| 104 | while (curOffset() <= endPos - 4/*for CRC*/ - 5/*for mapping fields*/) { |
| 105 | u_int8_t stream_type = get1Byte(); |
| 106 | u_int16_t elementary_PID = get2Bytes(); elementary_PID &= 0x1FFF; |
| 107 | u_int16_t ES_info_length = get2Bytes(); ES_info_length &= 0x0FFF; |
| 108 | #ifdef DEBUG_CONTENTS |
| 109 | extern StreamType StreamTypes[]; |
| 110 | char const* const streamTypeDesc = StreamTypes[stream_type].description; |
| 111 | fprintf(stderr, "\t\tstream_type: 0x%02x (%s); elementary_PID: 0x%04x; ES_info_length: %d\n" , |
| 112 | stream_type, streamTypeDesc == NULL ? "???" : streamTypeDesc, elementary_PID, ES_info_length); |
| 113 | #endif |
| 114 | endOfDescriptors = curOffset() + ES_info_length; |
| 115 | if (endOfDescriptors + 4/*CRC*/ - startPos > numDataBytes) { |
| 116 | #ifdef DEBUG_ERRORS |
| 117 | fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): ES_info_length %d gives us a total size %d that's too large!\n" , |
| 118 | pidState->PID, pusi, numDataBytes, ES_info_length, endOfDescriptors + 4 - startPos); |
| 119 | #endif |
| 120 | break; |
| 121 | } |
| 122 | parseStreamDescriptors(ES_info_length); |
| 123 | |
| 124 | if (fPIDState[elementary_PID] == NULL) { |
| 125 | fPIDState[elementary_PID] |
| 126 | = new PIDState_STREAM(*this, elementary_PID, program_number, stream_type); |
| 127 | } |
| 128 | } |
| 129 | } while (0); |
| 130 | |
| 131 | // Skip (ignore) all remaining bytes in this packet (including the CRC): |
| 132 | int numBytesLeft = numDataBytes - (curOffset() - startPos); |
| 133 | if (numBytesLeft > 0) { |
| 134 | #ifdef DEBUG_CONTENTS |
| 135 | fprintf(stderr, "\t\t+%d CRC and stuffing bytes\n" , numBytesLeft); |
| 136 | #endif |
| 137 | skipBytes(numBytesLeft); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | #ifdef DEBUG_CONTENTS |
| 142 | #define pDesc(str) do { fprintf(stderr, "\t\t\tdescriptor_tag: 0x%02x (%s); descriptor_length: %d\n",descriptor_tag, (str), descriptor_length); } while (0) |
| 143 | #else |
| 144 | #define pDesc(str) |
| 145 | #endif |
| 146 | |
| 147 | void MPEG2TransportStreamParser::parseStreamDescriptors(unsigned numDescriptorBytes) { |
| 148 | while (numDescriptorBytes >= 2/* enough for "descriptor_tag" and "descriptor_length" */) { |
| 149 | u_int8_t descriptor_tag = get1Byte(); |
| 150 | u_int8_t descriptor_length = get1Byte(); |
| 151 | numDescriptorBytes -= 2; |
| 152 | |
| 153 | if (descriptor_length > numDescriptorBytes) { |
| 154 | #ifdef DEBUG_ERRORS |
| 155 | fprintf(stderr, "MPEG2TransportStreamParser::parseStreamDescriptors() error: Saw descriptor_length %d > remaining bytes %d\n" , |
| 156 | descriptor_length, numDescriptorBytes); |
| 157 | #endif |
| 158 | skipBytes(numDescriptorBytes); numDescriptorBytes = 0; |
| 159 | break; |
| 160 | } |
| 161 | |
| 162 | Boolean parsedDescriptor = False; |
| 163 | switch (descriptor_tag) { |
| 164 | // Note: These are the tags that we've seen to date. Add more when we see more. |
| 165 | case 0x02: { |
| 166 | pDesc("video" ); |
| 167 | if (descriptor_length < 1) break; |
| 168 | u_int8_t flags = get1Byte(); |
| 169 | Boolean MPEG_1_only_flag = (flags&0x04) != 0; |
| 170 | #ifdef DEBUG_CONTENTS |
| 171 | fprintf(stderr, "\t\t\t\tflags: 0x%02x (frame_rate_code 0x%1x; MPEG_1_only_flag %d)\n" , |
| 172 | flags, (flags&0x78)>>3, MPEG_1_only_flag); |
| 173 | #endif |
| 174 | if (MPEG_1_only_flag == 0) { |
| 175 | if (descriptor_length < 3) break; |
| 176 | #ifdef DEBUG_CONTENTS |
| 177 | u_int8_t profile_and_level_indication = get1Byte(); |
| 178 | flags = get1Byte(); |
| 179 | fprintf(stderr, "\t\t\t\tprofile_and_level_indication 0x%02x; flags 0x%02x (chroma_format 0x%1x)\n" , |
| 180 | profile_and_level_indication, flags, (flags&0xC0)>>6); |
| 181 | #else |
| 182 | skipBytes(2); |
| 183 | #endif |
| 184 | } |
| 185 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 186 | break; |
| 187 | } |
| 188 | case 0x03: { |
| 189 | pDesc("audio" ); |
| 190 | if (descriptor_length < 1) break; |
| 191 | #ifdef DEBUG_CONTENTS |
| 192 | u_int8_t flags = get1Byte(); |
| 193 | fprintf(stderr, "\t\t\t\tflags: 0x%02x (layer %d)\n" , flags, (flags&0x30)>>4); |
| 194 | #else |
| 195 | skipBytes(1); |
| 196 | #endif |
| 197 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 198 | break; |
| 199 | } |
| 200 | case 0x05: { |
| 201 | pDesc("registration" ); |
| 202 | if (descriptor_length < 4) break; |
| 203 | #ifdef DEBUG_CONTENTS |
| 204 | u_int32_t format_identifier = get4Bytes(); |
| 205 | fprintf(stderr, "\t\t\t\tformat_identifier: 0x%08x (%c%c%c%c)\n" , |
| 206 | format_identifier, |
| 207 | format_identifier>>24, format_identifier>>16, format_identifier>>8, format_identifier); |
| 208 | if (descriptor_length > 4) { |
| 209 | fprintf(stderr, "\t\t\t\tadditional_identification_info: " ); |
| 210 | for (unsigned i = 4; i < descriptor_length; ++i) fprintf(stderr, "%02x:" , get1Byte()); |
| 211 | fprintf(stderr, "\n" ); |
| 212 | } |
| 213 | #else |
| 214 | skipBytes(descriptor_length); |
| 215 | #endif |
| 216 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 217 | break; |
| 218 | } |
| 219 | case 0x06: { |
| 220 | pDesc("data stream alignment" ); |
| 221 | if (descriptor_length < 1) break; |
| 222 | #ifdef DEBUG_CONTENTS |
| 223 | u_int8_t alignment_type = get1Byte(); |
| 224 | fprintf(stderr, "\t\t\t\talignment_type: 0x%02x\n" , alignment_type); |
| 225 | #else |
| 226 | skipBytes(1); |
| 227 | #endif |
| 228 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 229 | break; |
| 230 | } |
| 231 | case 0x0a: { |
| 232 | pDesc("ISO 639 language descriptor" ); |
| 233 | for (unsigned i = 0; i < descriptor_length/4; ++i) { |
| 234 | #ifdef DEBUG_CONTENTS |
| 235 | fprintf(stderr, "\t\t\t\tISO_639_language_code: %c%c%c; audio_type: 0x%02x\n" , |
| 236 | get1Byte(), get1Byte(), get1Byte(), get1Byte()); |
| 237 | #else |
| 238 | skipBytes(4); |
| 239 | #endif |
| 240 | } |
| 241 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 242 | break; |
| 243 | } |
| 244 | case 0x0b: { |
| 245 | pDesc("system clock" ); |
| 246 | if (descriptor_length < 2) break; |
| 247 | #ifdef DEBUG_CONTENTS |
| 248 | u_int8_t flags = get1Byte(); |
| 249 | Boolean external_clock_ref = (flags&0x80) != 0; |
| 250 | u_int8_t clock_accuracy_integer = flags&0x3F; |
| 251 | |
| 252 | u_int8_t clock_accuracy_exponent = get1Byte(); clock_accuracy_exponent >>= 5; |
| 253 | float ppm = clock_accuracy_integer*1.0; |
| 254 | for (unsigned i = 0; i < clock_accuracy_exponent; ++i) ppm /= 10.0; |
| 255 | fprintf(stderr, "\t\t\t\texternal_clock: %d; clock_accuracy int: %d, exp: %d -> %f ppm\n" , |
| 256 | external_clock_ref, clock_accuracy_integer, clock_accuracy_exponent, ppm); |
| 257 | #else |
| 258 | skipBytes(2); |
| 259 | #endif |
| 260 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 261 | break; |
| 262 | } |
| 263 | case 0x0e: { |
| 264 | pDesc("maximum bitrate" ); |
| 265 | if (descriptor_length < 3) break; |
| 266 | #ifdef DEBUG_CONTENTS |
| 267 | u_int32_t maximum_bitrate = ((get1Byte()&0x3F)<<16)|get2Bytes(); // 22 bits |
| 268 | fprintf(stderr, "\t\t\t\tmaximum_bitrate: %d => %f Mbps\n" , |
| 269 | maximum_bitrate, (maximum_bitrate*50*8)/1000000.0); |
| 270 | #else |
| 271 | skipBytes(3); |
| 272 | #endif |
| 273 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 274 | break; |
| 275 | } |
| 276 | case 0x10: { |
| 277 | pDesc("smoothing buffer" ); |
| 278 | if (descriptor_length < 6) break; |
| 279 | #ifdef DEBUG_CONTENTS |
| 280 | u_int32_t sb_leak_rate = ((get1Byte()&0x3F)<<16)|get2Bytes(); // 22 bits |
| 281 | u_int32_t sb_size = ((get1Byte()&0x3F)<<16)|get2Bytes(); // 22 bits |
| 282 | fprintf(stderr, "\t\t\t\tsb_leak_rate: %d => %f Mbps; sb_size: %d bytes\n" , |
| 283 | sb_leak_rate, (sb_leak_rate*400)/1000000.0, sb_size); |
| 284 | #else |
| 285 | skipBytes(6); |
| 286 | #endif |
| 287 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 288 | break; |
| 289 | } |
| 290 | case 0x1d: { |
| 291 | pDesc("IOD parameters for ISO/IEC 14496-1" ); |
| 292 | // Note: We don't know how to parse this. (Where's a document that describes this?) |
| 293 | skipBytes(descriptor_length); numDescriptorBytes -= descriptor_length; |
| 294 | parsedDescriptor = True; |
| 295 | break; |
| 296 | } |
| 297 | case 0x28: { |
| 298 | pDesc("H.264 video parameters" ); |
| 299 | if (descriptor_length < 4) break; |
| 300 | #ifdef DEBUG_CONTENTS |
| 301 | u_int8_t profile_idc = get1Byte(); |
| 302 | u_int8_t flags1 = get1Byte(); |
| 303 | u_int8_t level_idc = get1Byte(); |
| 304 | u_int8_t flags2 = get1Byte(); |
| 305 | fprintf(stderr, "\t\t\t\tprofile_idc: 0x%02x, flags1: 0x%02x, level_idc: 0x%02x, flags2: 0x%02x\n" , |
| 306 | profile_idc, flags1, level_idc, flags2); |
| 307 | #else |
| 308 | skipBytes(4); |
| 309 | #endif |
| 310 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 311 | break; |
| 312 | } |
| 313 | case 0x52: { |
| 314 | pDesc("stream identifier" ); |
| 315 | if (descriptor_length < 1) break; |
| 316 | #ifdef DEBUG_CONTENTS |
| 317 | u_int8_t component_tag = get1Byte(); |
| 318 | fprintf(stderr, "\t\t\t\tcomponent_tag: %d\n" , component_tag); |
| 319 | #else |
| 320 | skipBytes(1); |
| 321 | #endif |
| 322 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 323 | break; |
| 324 | } |
| 325 | case 0x56: { |
| 326 | pDesc("teletext" ); |
| 327 | for (unsigned i = 0; i < descriptor_length/5; ++i) { |
| 328 | #ifdef DEBUG_CONTENTS |
| 329 | fprintf(stderr, "\t\t\t\tISO_639_language_code: %c%c%c" , |
| 330 | get1Byte(), get1Byte(), get1Byte()); |
| 331 | u_int8_t typePlusMagazine = get1Byte(); |
| 332 | fprintf(stderr, "; type: 0x%02x; magazine: %d; page: %d\n" , |
| 333 | typePlusMagazine>>3, typePlusMagazine&0x07, get1Byte()); |
| 334 | #else |
| 335 | skipBytes(5); |
| 336 | #endif |
| 337 | } |
| 338 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 339 | break; |
| 340 | } |
| 341 | case 0x59: { |
| 342 | pDesc("subtitling" ); |
| 343 | for (unsigned i = 0; i < descriptor_length/8; ++i) { |
| 344 | #ifdef DEBUG_CONTENTS |
| 345 | fprintf(stderr, "\t\t\t\tISO_639_language_code: %c%c%c" , |
| 346 | get1Byte(), get1Byte(), get1Byte()); |
| 347 | fprintf(stderr, "; subtitling_type: 0x%02x; composition_page_id: 0x%04x; ancillary_page_id: 0x%04x\n" , |
| 348 | get1Byte(), get2Bytes(), get2Bytes()); |
| 349 | #else |
| 350 | skipBytes(8); |
| 351 | #endif |
| 352 | } |
| 353 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 354 | break; |
| 355 | } |
| 356 | case 0x6f: { |
| 357 | pDesc("application signalling" ); |
| 358 | for (unsigned i = 0; i < descriptor_length/3; ++i) { |
| 359 | #ifdef DEBUG_CONTENTS |
| 360 | fprintf(stderr, "\t\t\t\tapplication_type: 0x%04x; AIT_version_number: %d\n" , |
| 361 | get2Bytes()&0x7FFF, get1Byte()&0x1F); |
| 362 | #else |
| 363 | skipBytes(3); |
| 364 | #endif |
| 365 | } |
| 366 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 367 | break; |
| 368 | } |
| 369 | case 0x7a: { |
| 370 | pDesc("enhanced AC-3" ); |
| 371 | if (descriptor_length < 1) break; |
| 372 | #ifdef DEBUG_CONTENTS |
| 373 | u_int8_t flags = get1Byte(); |
| 374 | fprintf(stderr, "\t\t\t\tflags: 0x%02x" , flags); |
| 375 | if (descriptor_length > 1) { |
| 376 | fprintf(stderr, "; extra bytes: " ); |
| 377 | for (unsigned i = 1; i < descriptor_length; ++i) fprintf(stderr, "0x%02x " , get1Byte()); |
| 378 | } |
| 379 | fprintf(stderr, "\n" ); |
| 380 | #else |
| 381 | skipBytes(descriptor_length); |
| 382 | #endif |
| 383 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 384 | break; |
| 385 | } |
| 386 | case 0x81: { |
| 387 | pDesc("AC-3 audio" ); |
| 388 | if (descriptor_length < 3) break; |
| 389 | #ifdef DEBUG_CONTENTS |
| 390 | u_int8_t flags = get1Byte(); |
| 391 | fprintf(stderr, "\t\t\t\tsample_rate_code: %d; bsid: 0x%02x" , |
| 392 | flags>>5, flags&0x1F); |
| 393 | flags = get1Byte(); |
| 394 | fprintf(stderr, "; bit_rate_code: %d; surround_mode: %d" , |
| 395 | flags>>2, flags&0x03); |
| 396 | flags = get1Byte(); |
| 397 | fprintf(stderr, "; bsmod: %d; num_channels: %d; full_svc: %d" , |
| 398 | flags>>5, (flags&0x1E)>>1, (flags&0x01)); |
| 399 | if (descriptor_length > 3) { |
| 400 | fprintf(stderr, "; extra bytes: " ); |
| 401 | for (unsigned i = 3; i < descriptor_length; ++i) fprintf(stderr, "0x%02x " , get1Byte()); |
| 402 | } |
| 403 | fprintf(stderr, "\n" ); |
| 404 | #else |
| 405 | skipBytes(descriptor_length); |
| 406 | #endif |
| 407 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 408 | break; |
| 409 | } |
| 410 | case 0x86: { |
| 411 | pDesc("caption service" ); |
| 412 | if (descriptor_length < 1) break; |
| 413 | u_int8_t number_of_services = get1Byte()&0x1F; |
| 414 | #ifdef DEBUG_CONTENTS |
| 415 | fprintf(stderr, "\t\t\t\tnumber_of_services: %d\n" , number_of_services); |
| 416 | #endif |
| 417 | if (descriptor_length < number_of_services*6) break; |
| 418 | #ifdef DEBUG_CONTENTS |
| 419 | for (unsigned i = 0; i < number_of_services; ++i) { |
| 420 | fprintf(stderr, "\t\t\t\t\tlanguage: %c%c%c" , get1Byte(), get1Byte(), get1Byte()); |
| 421 | |
| 422 | u_int8_t flags = get1Byte(); |
| 423 | Boolean digital_cc = (flags&0x80) != 0; |
| 424 | fprintf(stderr, "; digital_cc %d" , digital_cc); |
| 425 | if (digital_cc == 0) { |
| 426 | fprintf(stderr, "; line21_field: %d" , flags&0x01); |
| 427 | } else { |
| 428 | fprintf(stderr, "; caption_service_number: %d" , flags&0x3F); |
| 429 | } |
| 430 | |
| 431 | u_int16_t flags2 = get2Bytes(); |
| 432 | fprintf(stderr, "; easy_reader: %d; wide_aspect_ratio: %d\n" , |
| 433 | (flags2&0x8000) != 0, (flags2&0x4000) != 0); |
| 434 | } |
| 435 | #else |
| 436 | skipBytes(number_of_services*6); |
| 437 | #endif |
| 438 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 439 | break; |
| 440 | } |
| 441 | default: { |
| 442 | pDesc("???" ); |
| 443 | skipBytes(descriptor_length); |
| 444 | numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| 445 | break; |
| 446 | } |
| 447 | } |
| 448 | if (!parsedDescriptor) break; // an error occurred |
| 449 | } |
| 450 | |
| 451 | // Skip over any remaining descriptor bytes (as a result of a parsing error): |
| 452 | if (numDescriptorBytes > 0) { |
| 453 | #ifdef DEBUG_ERRORS |
| 454 | fprintf(stderr, "MPEG2TransportStreamParser::parseStreamDescriptors() Parsing error left %d bytes unparsed\n" , |
| 455 | numDescriptorBytes); |
| 456 | #endif |
| 457 | skipBytes(numDescriptorBytes); |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | |
| 462 | //########## PIDState_PMT implementation ########## |
| 463 | |
| 464 | PIDState_PMT |
| 465 | ::PIDState_PMT(MPEG2TransportStreamParser& parser, u_int16_t pid, u_int16_t programNumber) |
| 466 | : PIDState(parser, pid, PMT), |
| 467 | program_number(programNumber) { |
| 468 | } |
| 469 | |
| 470 | PIDState_PMT::~PIDState_PMT() { |
| 471 | } |
| 472 | |