| /********** |
| This library is free software; you can redistribute it and/or modify it under |
| the terms of the GNU Lesser General Public License as published by the |
| Free Software Foundation; either version 3 of the License, or (at your |
| option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| This library is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this library; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| **********/ |
| // "liveMedia" |
| // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
| // A parser for a MPEG Transport Stream |
| // Implementation |
| |
| #include "MPEG2TransportStreamParser.hh" |
| |
| void MPEG2TransportStreamParser |
| ::parsePMT(PIDState_PMT* pidState, Boolean pusi, unsigned numDataBytes) { |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\tProgram Map Table\n"); |
| #endif |
| unsigned startPos = curOffset(); |
| |
| do { |
| if (pusi) { |
| u_int8_t pointer_field = get1Byte(); |
| skipBytes(pointer_field); // usually 0 |
| } |
| |
| u_int8_t table_id = get1Byte(); |
| if (table_id != 0x02) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): bad table_id: 0x%02x\n", |
| pidState->PID, pusi, numDataBytes, table_id); |
| #endif |
| break; |
| } |
| |
| u_int16_t flagsPlusSection_length = get2Bytes(); |
| u_int16_t section_length = flagsPlusSection_length&0x0FFF; |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\tsection_length: %d\n", section_length); |
| #endif |
| if (section_length < 13/*too small for remaining fields + CRC*/ || |
| section_length > 1021/*as per specification*/) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): Bad section_length: %d\n", |
| pidState->PID, pusi, numDataBytes, section_length); |
| #endif |
| break; |
| } |
| unsigned endPos = curOffset() + section_length; |
| if (endPos - startPos > numDataBytes) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): section_length %d gives us a total size %d that's too large!\n", |
| pidState->PID, pusi, numDataBytes, section_length, endPos - startPos); |
| #endif |
| break; |
| } |
| |
| u_int16_t program_number = get2Bytes(); |
| if (program_number != pidState->program_number) { |
| #ifdef DEBUG_ERRORS |
| 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", |
| pidState->PID, pusi, numDataBytes, program_number, pidState->program_number); |
| #endif |
| break; |
| } |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\tprogram_number: %d\n", program_number); |
| |
| u_int8_t version_number_byte = get1Byte(); |
| u_int8_t version_number = (version_number_byte&0x1E)>>1; |
| u_int8_t section_number = get1Byte(); |
| u_int8_t last_section_number = get1Byte(); |
| fprintf(stderr, "\t\tversion_number: %d; section_number: %d; last_section_number: %d\n", |
| version_number, section_number, last_section_number); |
| u_int16_t PCR_PID = get2Bytes(); PCR_PID &= 0x1FFF; |
| fprintf(stderr, "\t\tPCR_PID: 0x%04x\n", PCR_PID); |
| #else |
| skipBytes(5); |
| #endif |
| |
| u_int16_t program_info_length = get2Bytes(); program_info_length &= 0x0FFF; |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\tprogram_info_length: %d\n", program_info_length); |
| #endif |
| unsigned endOfDescriptors = curOffset() + program_info_length; |
| if (endOfDescriptors + 4/*CRC*/ - startPos > numDataBytes) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): program_info_length %d gives us a total size %d that's too large!\n", |
| pidState->PID, pusi, numDataBytes, program_info_length, endOfDescriptors + 4 - startPos); |
| #endif |
| break; |
| } |
| parseStreamDescriptors(program_info_length); |
| |
| while (curOffset() <= endPos - 4/*for CRC*/ - 5/*for mapping fields*/) { |
| u_int8_t stream_type = get1Byte(); |
| u_int16_t elementary_PID = get2Bytes(); elementary_PID &= 0x1FFF; |
| u_int16_t ES_info_length = get2Bytes(); ES_info_length &= 0x0FFF; |
| #ifdef DEBUG_CONTENTS |
| extern StreamType StreamTypes[]; |
| char const* const streamTypeDesc = StreamTypes[stream_type].description; |
| fprintf(stderr, "\t\tstream_type: 0x%02x (%s); elementary_PID: 0x%04x; ES_info_length: %d\n", |
| stream_type, streamTypeDesc == NULL ? "???" : streamTypeDesc, elementary_PID, ES_info_length); |
| #endif |
| endOfDescriptors = curOffset() + ES_info_length; |
| if (endOfDescriptors + 4/*CRC*/ - startPos > numDataBytes) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parsePMT(0x%04x, %d, %d): ES_info_length %d gives us a total size %d that's too large!\n", |
| pidState->PID, pusi, numDataBytes, ES_info_length, endOfDescriptors + 4 - startPos); |
| #endif |
| break; |
| } |
| parseStreamDescriptors(ES_info_length); |
| |
| if (fPIDState[elementary_PID] == NULL) { |
| fPIDState[elementary_PID] |
| = new PIDState_STREAM(*this, elementary_PID, program_number, stream_type); |
| } |
| } |
| } while (0); |
| |
| // Skip (ignore) all remaining bytes in this packet (including the CRC): |
| int numBytesLeft = numDataBytes - (curOffset() - startPos); |
| if (numBytesLeft > 0) { |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t+%d CRC and stuffing bytes\n", numBytesLeft); |
| #endif |
| skipBytes(numBytesLeft); |
| } |
| } |
| |
| #ifdef DEBUG_CONTENTS |
| #define pDesc(str) do { fprintf(stderr, "\t\t\tdescriptor_tag: 0x%02x (%s); descriptor_length: %d\n",descriptor_tag, (str), descriptor_length); } while (0) |
| #else |
| #define pDesc(str) |
| #endif |
| |
| void MPEG2TransportStreamParser::parseStreamDescriptors(unsigned numDescriptorBytes) { |
| while (numDescriptorBytes >= 2/* enough for "descriptor_tag" and "descriptor_length" */) { |
| u_int8_t descriptor_tag = get1Byte(); |
| u_int8_t descriptor_length = get1Byte(); |
| numDescriptorBytes -= 2; |
| |
| if (descriptor_length > numDescriptorBytes) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parseStreamDescriptors() error: Saw descriptor_length %d > remaining bytes %d\n", |
| descriptor_length, numDescriptorBytes); |
| #endif |
| skipBytes(numDescriptorBytes); numDescriptorBytes = 0; |
| break; |
| } |
| |
| Boolean parsedDescriptor = False; |
| switch (descriptor_tag) { |
| // Note: These are the tags that we've seen to date. Add more when we see more. |
| case 0x02: { |
| pDesc("video"); |
| if (descriptor_length < 1) break; |
| u_int8_t flags = get1Byte(); |
| Boolean MPEG_1_only_flag = (flags&0x04) != 0; |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t\t\tflags: 0x%02x (frame_rate_code 0x%1x; MPEG_1_only_flag %d)\n", |
| flags, (flags&0x78)>>3, MPEG_1_only_flag); |
| #endif |
| if (MPEG_1_only_flag == 0) { |
| if (descriptor_length < 3) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t profile_and_level_indication = get1Byte(); |
| flags = get1Byte(); |
| fprintf(stderr, "\t\t\t\tprofile_and_level_indication 0x%02x; flags 0x%02x (chroma_format 0x%1x)\n", |
| profile_and_level_indication, flags, (flags&0xC0)>>6); |
| #else |
| skipBytes(2); |
| #endif |
| } |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x03: { |
| pDesc("audio"); |
| if (descriptor_length < 1) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t flags = get1Byte(); |
| fprintf(stderr, "\t\t\t\tflags: 0x%02x (layer %d)\n", flags, (flags&0x30)>>4); |
| #else |
| skipBytes(1); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x05: { |
| pDesc("registration"); |
| if (descriptor_length < 4) break; |
| #ifdef DEBUG_CONTENTS |
| u_int32_t format_identifier = get4Bytes(); |
| fprintf(stderr, "\t\t\t\tformat_identifier: 0x%08x (%c%c%c%c)\n", |
| format_identifier, |
| format_identifier>>24, format_identifier>>16, format_identifier>>8, format_identifier); |
| if (descriptor_length > 4) { |
| fprintf(stderr, "\t\t\t\tadditional_identification_info: "); |
| for (unsigned i = 4; i < descriptor_length; ++i) fprintf(stderr, "%02x:", get1Byte()); |
| fprintf(stderr, "\n"); |
| } |
| #else |
| skipBytes(descriptor_length); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x06: { |
| pDesc("data stream alignment"); |
| if (descriptor_length < 1) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t alignment_type = get1Byte(); |
| fprintf(stderr, "\t\t\t\talignment_type: 0x%02x\n", alignment_type); |
| #else |
| skipBytes(1); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x0a: { |
| pDesc("ISO 639 language descriptor"); |
| for (unsigned i = 0; i < descriptor_length/4; ++i) { |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t\t\tISO_639_language_code: %c%c%c; audio_type: 0x%02x\n", |
| get1Byte(), get1Byte(), get1Byte(), get1Byte()); |
| #else |
| skipBytes(4); |
| #endif |
| } |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x0b: { |
| pDesc("system clock"); |
| if (descriptor_length < 2) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t flags = get1Byte(); |
| Boolean external_clock_ref = (flags&0x80) != 0; |
| u_int8_t clock_accuracy_integer = flags&0x3F; |
| |
| u_int8_t clock_accuracy_exponent = get1Byte(); clock_accuracy_exponent >>= 5; |
| float ppm = clock_accuracy_integer*1.0; |
| for (unsigned i = 0; i < clock_accuracy_exponent; ++i) ppm /= 10.0; |
| fprintf(stderr, "\t\t\t\texternal_clock: %d; clock_accuracy int: %d, exp: %d -> %f ppm\n", |
| external_clock_ref, clock_accuracy_integer, clock_accuracy_exponent, ppm); |
| #else |
| skipBytes(2); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x0e: { |
| pDesc("maximum bitrate"); |
| if (descriptor_length < 3) break; |
| #ifdef DEBUG_CONTENTS |
| u_int32_t maximum_bitrate = ((get1Byte()&0x3F)<<16)|get2Bytes(); // 22 bits |
| fprintf(stderr, "\t\t\t\tmaximum_bitrate: %d => %f Mbps\n", |
| maximum_bitrate, (maximum_bitrate*50*8)/1000000.0); |
| #else |
| skipBytes(3); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x10: { |
| pDesc("smoothing buffer"); |
| if (descriptor_length < 6) break; |
| #ifdef DEBUG_CONTENTS |
| u_int32_t sb_leak_rate = ((get1Byte()&0x3F)<<16)|get2Bytes(); // 22 bits |
| u_int32_t sb_size = ((get1Byte()&0x3F)<<16)|get2Bytes(); // 22 bits |
| fprintf(stderr, "\t\t\t\tsb_leak_rate: %d => %f Mbps; sb_size: %d bytes\n", |
| sb_leak_rate, (sb_leak_rate*400)/1000000.0, sb_size); |
| #else |
| skipBytes(6); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x1d: { |
| pDesc("IOD parameters for ISO/IEC 14496-1"); |
| // Note: We don't know how to parse this. (Where's a document that describes this?) |
| skipBytes(descriptor_length); numDescriptorBytes -= descriptor_length; |
| parsedDescriptor = True; |
| break; |
| } |
| case 0x28: { |
| pDesc("H.264 video parameters"); |
| if (descriptor_length < 4) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t profile_idc = get1Byte(); |
| u_int8_t flags1 = get1Byte(); |
| u_int8_t level_idc = get1Byte(); |
| u_int8_t flags2 = get1Byte(); |
| fprintf(stderr, "\t\t\t\tprofile_idc: 0x%02x, flags1: 0x%02x, level_idc: 0x%02x, flags2: 0x%02x\n", |
| profile_idc, flags1, level_idc, flags2); |
| #else |
| skipBytes(4); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x52: { |
| pDesc("stream identifier"); |
| if (descriptor_length < 1) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t component_tag = get1Byte(); |
| fprintf(stderr, "\t\t\t\tcomponent_tag: %d\n", component_tag); |
| #else |
| skipBytes(1); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x56: { |
| pDesc("teletext"); |
| for (unsigned i = 0; i < descriptor_length/5; ++i) { |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t\t\tISO_639_language_code: %c%c%c", |
| get1Byte(), get1Byte(), get1Byte()); |
| u_int8_t typePlusMagazine = get1Byte(); |
| fprintf(stderr, "; type: 0x%02x; magazine: %d; page: %d\n", |
| typePlusMagazine>>3, typePlusMagazine&0x07, get1Byte()); |
| #else |
| skipBytes(5); |
| #endif |
| } |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x59: { |
| pDesc("subtitling"); |
| for (unsigned i = 0; i < descriptor_length/8; ++i) { |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t\t\tISO_639_language_code: %c%c%c", |
| get1Byte(), get1Byte(), get1Byte()); |
| fprintf(stderr, "; subtitling_type: 0x%02x; composition_page_id: 0x%04x; ancillary_page_id: 0x%04x\n", |
| get1Byte(), get2Bytes(), get2Bytes()); |
| #else |
| skipBytes(8); |
| #endif |
| } |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x6f: { |
| pDesc("application signalling"); |
| for (unsigned i = 0; i < descriptor_length/3; ++i) { |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t\t\tapplication_type: 0x%04x; AIT_version_number: %d\n", |
| get2Bytes()&0x7FFF, get1Byte()&0x1F); |
| #else |
| skipBytes(3); |
| #endif |
| } |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x7a: { |
| pDesc("enhanced AC-3"); |
| if (descriptor_length < 1) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t flags = get1Byte(); |
| fprintf(stderr, "\t\t\t\tflags: 0x%02x", flags); |
| if (descriptor_length > 1) { |
| fprintf(stderr, "; extra bytes: "); |
| for (unsigned i = 1; i < descriptor_length; ++i) fprintf(stderr, "0x%02x ", get1Byte()); |
| } |
| fprintf(stderr, "\n"); |
| #else |
| skipBytes(descriptor_length); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x81: { |
| pDesc("AC-3 audio"); |
| if (descriptor_length < 3) break; |
| #ifdef DEBUG_CONTENTS |
| u_int8_t flags = get1Byte(); |
| fprintf(stderr, "\t\t\t\tsample_rate_code: %d; bsid: 0x%02x", |
| flags>>5, flags&0x1F); |
| flags = get1Byte(); |
| fprintf(stderr, "; bit_rate_code: %d; surround_mode: %d", |
| flags>>2, flags&0x03); |
| flags = get1Byte(); |
| fprintf(stderr, "; bsmod: %d; num_channels: %d; full_svc: %d", |
| flags>>5, (flags&0x1E)>>1, (flags&0x01)); |
| if (descriptor_length > 3) { |
| fprintf(stderr, "; extra bytes: "); |
| for (unsigned i = 3; i < descriptor_length; ++i) fprintf(stderr, "0x%02x ", get1Byte()); |
| } |
| fprintf(stderr, "\n"); |
| #else |
| skipBytes(descriptor_length); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| case 0x86: { |
| pDesc("caption service"); |
| if (descriptor_length < 1) break; |
| u_int8_t number_of_services = get1Byte()&0x1F; |
| #ifdef DEBUG_CONTENTS |
| fprintf(stderr, "\t\t\t\tnumber_of_services: %d\n", number_of_services); |
| #endif |
| if (descriptor_length < number_of_services*6) break; |
| #ifdef DEBUG_CONTENTS |
| for (unsigned i = 0; i < number_of_services; ++i) { |
| fprintf(stderr, "\t\t\t\t\tlanguage: %c%c%c", get1Byte(), get1Byte(), get1Byte()); |
| |
| u_int8_t flags = get1Byte(); |
| Boolean digital_cc = (flags&0x80) != 0; |
| fprintf(stderr, "; digital_cc %d", digital_cc); |
| if (digital_cc == 0) { |
| fprintf(stderr, "; line21_field: %d", flags&0x01); |
| } else { |
| fprintf(stderr, "; caption_service_number: %d", flags&0x3F); |
| } |
| |
| u_int16_t flags2 = get2Bytes(); |
| fprintf(stderr, "; easy_reader: %d; wide_aspect_ratio: %d\n", |
| (flags2&0x8000) != 0, (flags2&0x4000) != 0); |
| } |
| #else |
| skipBytes(number_of_services*6); |
| #endif |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| default: { |
| pDesc("???"); |
| skipBytes(descriptor_length); |
| numDescriptorBytes -= descriptor_length; parsedDescriptor = True; |
| break; |
| } |
| } |
| if (!parsedDescriptor) break; // an error occurred |
| } |
| |
| // Skip over any remaining descriptor bytes (as a result of a parsing error): |
| if (numDescriptorBytes > 0) { |
| #ifdef DEBUG_ERRORS |
| fprintf(stderr, "MPEG2TransportStreamParser::parseStreamDescriptors() Parsing error left %d bytes unparsed\n", |
| numDescriptorBytes); |
| #endif |
| skipBytes(numDescriptorBytes); |
| } |
| } |
| |
| |
| //########## PIDState_PMT implementation ########## |
| |
| PIDState_PMT |
| ::PIDState_PMT(MPEG2TransportStreamParser& parser, u_int16_t pid, u_int16_t programNumber) |
| : PIDState(parser, pid, PMT), |
| program_number(programNumber) { |
| } |
| |
| PIDState_PMT::~PIDState_PMT() { |
| } |