// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/formats/mp2t/ts_packet.h" #include "base/memory/scoped_ptr.h" #include "media/base/bit_reader.h" #include "media/formats/mp2t/mp2t_common.h" namespace media { namespace mp2t { static const uint8 kTsHeaderSyncword = 0x47; // static int TsPacket::Sync(const uint8* buf, int size) { int k = 0; for (; k < size; k++) { // Verify that we have 4 syncwords in a row when possible, // this should improve synchronization robustness. // TODO(damienv): Consider the case where there is garbage // between TS packets. bool is_header = true; for (int i = 0; i < 4; i++) { int idx = k + i * kPacketSize; if (idx >= size) break; if (buf[idx] != kTsHeaderSyncword) { DVLOG(LOG_LEVEL_TS) << "ByteSync" << idx << ": " << std::hex << static_cast(buf[idx]) << std::dec; is_header = false; break; } } if (is_header) break; } DVLOG_IF(1, k != 0) << "SYNC: nbytes_skipped=" << k; return k; } // static TsPacket* TsPacket::Parse(const uint8* buf, int size) { if (size < kPacketSize) { DVLOG(1) << "Buffer does not hold one full TS packet:" << " buffer_size=" << size; return NULL; } DCHECK_EQ(buf[0], kTsHeaderSyncword); if (buf[0] != kTsHeaderSyncword) { DVLOG(1) << "Not on a TS syncword:" << " buf[0]=" << std::hex << static_cast(buf[0]) << std::dec; return NULL; } scoped_ptr ts_packet(new TsPacket()); bool status = ts_packet->ParseHeader(buf); if (!status) { DVLOG(1) << "Parsing header failed"; return NULL; } return ts_packet.release(); } TsPacket::TsPacket() { } TsPacket::~TsPacket() { } bool TsPacket::ParseHeader(const uint8* buf) { BitReader bit_reader(buf, kPacketSize); payload_ = buf; payload_size_ = kPacketSize; // Read the TS header: 4 bytes. int syncword; int transport_error_indicator; int payload_unit_start_indicator; int transport_priority; int transport_scrambling_control; int adaptation_field_control; RCHECK(bit_reader.ReadBits(8, &syncword)); RCHECK(bit_reader.ReadBits(1, &transport_error_indicator)); RCHECK(bit_reader.ReadBits(1, &payload_unit_start_indicator)); RCHECK(bit_reader.ReadBits(1, &transport_priority)); RCHECK(bit_reader.ReadBits(13, &pid_)); RCHECK(bit_reader.ReadBits(2, &transport_scrambling_control)); RCHECK(bit_reader.ReadBits(2, &adaptation_field_control)); RCHECK(bit_reader.ReadBits(4, &continuity_counter_)); payload_unit_start_indicator_ = (payload_unit_start_indicator != 0); payload_ += 4; payload_size_ -= 4; // Default values when no adaptation field. discontinuity_indicator_ = false; random_access_indicator_ = false; // Done since no adaptation field. if ((adaptation_field_control & 0x2) == 0) return true; // Read the adaptation field if needed. int adaptation_field_length; RCHECK(bit_reader.ReadBits(8, &adaptation_field_length)); DVLOG(LOG_LEVEL_TS) << "adaptation_field_length=" << adaptation_field_length; payload_ += 1; payload_size_ -= 1; if ((adaptation_field_control & 0x1) == 0 && adaptation_field_length != 183) { DVLOG(1) << "adaptation_field_length=" << adaptation_field_length; return false; } if ((adaptation_field_control & 0x1) == 1 && adaptation_field_length > 182) { DVLOG(1) << "adaptation_field_length=" << adaptation_field_length; // This is not allowed by the spec. // However, some badly encoded streams are using // adaptation_field_length = 183 return false; } // adaptation_field_length = '0' is used to insert a single stuffing byte // in the adaptation field of a transport stream packet. if (adaptation_field_length == 0) return true; bool status = ParseAdaptationField(&bit_reader, adaptation_field_length); payload_ += adaptation_field_length; payload_size_ -= adaptation_field_length; return status; } bool TsPacket::ParseAdaptationField(BitReader* bit_reader, int adaptation_field_length) { DCHECK_GT(adaptation_field_length, 0); int adaptation_field_start_marker = bit_reader->bits_available() / 8; int discontinuity_indicator; int random_access_indicator; int elementary_stream_priority_indicator; int pcr_flag; int opcr_flag; int splicing_point_flag; int transport_private_data_flag; int adaptation_field_extension_flag; RCHECK(bit_reader->ReadBits(1, &discontinuity_indicator)); RCHECK(bit_reader->ReadBits(1, &random_access_indicator)); RCHECK(bit_reader->ReadBits(1, &elementary_stream_priority_indicator)); RCHECK(bit_reader->ReadBits(1, &pcr_flag)); RCHECK(bit_reader->ReadBits(1, &opcr_flag)); RCHECK(bit_reader->ReadBits(1, &splicing_point_flag)); RCHECK(bit_reader->ReadBits(1, &transport_private_data_flag)); RCHECK(bit_reader->ReadBits(1, &adaptation_field_extension_flag)); discontinuity_indicator_ = (discontinuity_indicator != 0); random_access_indicator_ = (random_access_indicator != 0); if (pcr_flag) { int64 program_clock_reference_base; int reserved; int program_clock_reference_extension; RCHECK(bit_reader->ReadBits(33, &program_clock_reference_base)); RCHECK(bit_reader->ReadBits(6, &reserved)); RCHECK(bit_reader->ReadBits(9, &program_clock_reference_extension)); } if (opcr_flag) { int64 original_program_clock_reference_base; int reserved; int original_program_clock_reference_extension; RCHECK(bit_reader->ReadBits(33, &original_program_clock_reference_base)); RCHECK(bit_reader->ReadBits(6, &reserved)); RCHECK( bit_reader->ReadBits(9, &original_program_clock_reference_extension)); } if (splicing_point_flag) { int splice_countdown; RCHECK(bit_reader->ReadBits(8, &splice_countdown)); } if (transport_private_data_flag) { int transport_private_data_length; RCHECK(bit_reader->ReadBits(8, &transport_private_data_length)); RCHECK(bit_reader->SkipBits(8 * transport_private_data_length)); } if (adaptation_field_extension_flag) { int adaptation_field_extension_length; RCHECK(bit_reader->ReadBits(8, &adaptation_field_extension_length)); RCHECK(bit_reader->SkipBits(8 * adaptation_field_extension_length)); } // The rest of the adaptation field should be stuffing bytes. int adaptation_field_remaining_size = adaptation_field_length - (adaptation_field_start_marker - bit_reader->bits_available() / 8); RCHECK(adaptation_field_remaining_size >= 0); for (int k = 0; k < adaptation_field_remaining_size; k++) { int stuffing_byte; RCHECK(bit_reader->ReadBits(8, &stuffing_byte)); // Unfortunately, a lot of streams exist in the field that do not fill // the remaining of the adaptation field with the expected stuffing value: // do not fail if that's the case. DVLOG_IF(1, stuffing_byte != 0xff) << "Stream not compliant: invalid stuffing byte " << std::hex << stuffing_byte; } DVLOG(LOG_LEVEL_TS) << "random_access_indicator=" << random_access_indicator_; return true; } } // namespace mp2t } // namespace media