Index: media/mpeg2/mpeg2ts_pes.cc |
diff --git a/media/mpeg2/mpeg2ts_pes.cc b/media/mpeg2/mpeg2ts_pes.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..748b83d64085151e5ff73cf7c3db96d02ebfc603 |
--- /dev/null |
+++ b/media/mpeg2/mpeg2ts_pes.cc |
@@ -0,0 +1,370 @@ |
+// Copyright (c) 2013 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/mpeg2/mpeg2ts_pes.h" |
+ |
+#include "base/logging.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "media/base/bit_reader.h" |
+#include "media/mpeg2/es_parser.h" |
+#include "media/mpeg2/mpeg2ts_common.h" |
+#include "media/mpeg2/mpeg2ts_def.h" |
+ |
+namespace { |
+ |
+int64 UnrollTimestamp(int64 previous_unrolled_time, |
+ int64 time, |
+ int nbits) { |
+ // |timestamp| has a precision of |nbits| |
+ // so make sure the highest bits are set to 0. |
+ DCHECK_EQ((time >> nbits), 0); |
+ |
+ // Consider 3 possibilities to estimate the missing high bits of |time|. |
+ int64 previous_unrolled_time_high = |
+ (previous_unrolled_time >> nbits); |
+ int64 time0 = ((previous_unrolled_time_high - 1) << nbits) | time; |
+ int64 time1 = ((previous_unrolled_time_high + 0) << nbits) | time; |
+ int64 time2 = ((previous_unrolled_time_high + 1) << nbits) | time; |
+ |
+ // Select the min absolute difference with the current time |
+ // so as to ensure time continuity. |
+ int64 diff0 = time0 - previous_unrolled_time; |
+ int64 diff1 = time1 - previous_unrolled_time; |
+ int64 diff2 = time2 - previous_unrolled_time; |
+ if (diff0 < 0) { |
+ diff0 = -diff0; |
+ } |
+ if (diff1 < 0) { |
+ diff1 = -diff1; |
+ } |
+ if (diff2 < 0) { |
+ diff2 = -diff2; |
+ } |
+ |
+ int64 unrolled_time; |
+ int64 min_diff; |
+ if (diff1 < diff0) { |
+ unrolled_time = time1; |
+ min_diff = diff1; |
+ } else { |
+ unrolled_time = time0; |
+ min_diff = diff0; |
+ } |
+ if (diff2 < min_diff) { |
+ unrolled_time = time2; |
+ } |
+ LOG_IF(INFO, unrolled_time != time1) |
+ << "Unrolling time:" |
+ << " previous_unrolled_time=" << previous_unrolled_time |
+ << " time=" << time |
+ << " unrolled_time=" << unrolled_time; |
+ |
+ return unrolled_time; |
+} |
+ |
+bool IsTimestampSectionValid(int64 timestamp_section) { |
+ // |pts_section| has 40 bits: |
+ // - starting with either '0010' or '0011' or '0001' |
+ // - and ending with a marker bit. |
+ // See ITU H.222 standard - PES section. |
+ |
+ // Verify that all the marker bits are one. |
+ bool is_valid = |
+ ((timestamp_section & 0x1) != 0) && |
+ ((timestamp_section & 0x10000) != 0) && |
+ ((timestamp_section & 0x100000000) != 0); |
+ return is_valid; |
+} |
+ |
+int64 ConvertTimestampSectionToTimestamp(int64 timestamp_section) { |
+ int64 timestamp = |
+ (((timestamp_section >> 33) & 0x7) << 30) | |
+ (((timestamp_section >> 17) & 0x7fff) << 15) | |
+ (((timestamp_section >> 1) & 0x7fff) << 0); |
+ return timestamp; |
+} |
+ |
+} // namespace |
+ |
+namespace media { |
+namespace mpeg2ts { |
+ |
+Mpeg2TsPesParser::Mpeg2TsPesParser(EsParser* es_parser) |
+ : es_parser_(es_parser), |
+ wait_for_unit_start_(true), |
+ previous_pts_valid_(false), |
+ previous_pts_(0), |
+ previous_dts_valid_(false), |
+ previous_dts_(0) { |
+} |
+ |
+Mpeg2TsPesParser::~Mpeg2TsPesParser() { |
+} |
+ |
+bool Mpeg2TsPesParser::Parse(bool payload_unit_start_indicator, |
+ const uint8* buf, int size) { |
+ if (wait_for_unit_start_ && !payload_unit_start_indicator) { |
+ // Ignore partial PES. |
+ return true; |
+ } |
+ wait_for_unit_start_ = false; |
+ |
+ if (payload_unit_start_indicator) { |
+ // If we have a pending packet with an undefined size |
+ // (size == 0), try to parse and emit the previous packet. |
+ if (raw_pes_.size() > 0) { |
+ // TODO(damienv): should check the result of InternalParse for a possible |
+ // error. |
+ InternalParse(); |
+ raw_pes_.resize(0); |
+ } |
+ } |
+ |
+ // Add the data to the parser state. |
+ if (size > 0) { |
+ int old_size = raw_pes_.size(); |
+ raw_pes_.resize(old_size + size); |
+ memcpy(&raw_pes_[old_size], buf, size); |
+ } |
+ |
+ // Check whether we have enough data to start parsing. |
+ if (raw_pes_.size() < 6) |
+ return true; |
+ int pes_packet_length = |
+ (static_cast<int>(raw_pes_[4]) << 8) | |
+ (static_cast<int>(raw_pes_[5])); |
+ VLOG(LOG_LEVEL_PES) << "pes_packet_length=" << pes_packet_length; |
+ if (pes_packet_length == 0) { |
+ // Video packets are emitted when a new unit is coming |
+ // since there is no way to know the size of the packet before. |
+ return true; |
+ } |
+ if (static_cast<int>(raw_pes_.size()) < pes_packet_length + 6) { |
+ // Don't throw an error when there is not enough data, |
+ // just wait for more data to come. |
+ return true; |
+ } |
+ |
+ // Parse the packet. |
+ bool parse_result = InternalParse(); |
+ raw_pes_.resize(0); |
+ return parse_result; |
+} |
+ |
+void Mpeg2TsPesParser::Flush() { |
+ // Force emitting the previous buffer by faking a |
+ // payload_unit_start_indicator. |
+ // For video, the pes packet length is set to 0, |
+ // thus a video packet can only be emitted when the next video |
+ // packet is coming. |
+ Parse(true, NULL, 0); |
+ raw_pes_.resize(0); |
+ |
+ // Flush the underlying ES parser. |
+ es_parser_->Flush(); |
+} |
+ |
+bool Mpeg2TsPesParser::InternalParse() { |
+ BitReader bit_reader(&raw_pes_[0], raw_pes_.size()); |
+ int remaining_size = raw_pes_.size(); |
+ |
+ // Read: |
+ // - packet_start_code_prefix |
+ // - stream_id |
+ // - pes_packet_length |
+ if (remaining_size < 6) { |
+ return false; |
+ } |
+ int packet_start_code_prefix; |
+ DCHECK(bit_reader.ReadBits(24, &packet_start_code_prefix)); |
+ if (packet_start_code_prefix != 0x000001) { |
+ LOG(WARNING) << "Invalid PES start code: " |
+ << std::hex << "0x" << packet_start_code_prefix << std::dec; |
+ return false; |
+ } |
+ int stream_id; |
+ DCHECK(bit_reader.ReadBits(8, &stream_id)); |
+ VLOG(LOG_LEVEL_PES) << "stream_id=" << std::hex << stream_id << std::dec; |
+ int pes_packet_length; |
+ DCHECK(bit_reader.ReadBits(16, &pes_packet_length)); |
+ bool pes_packet_length_defined = (pes_packet_length != 0); |
+ remaining_size -= 6; |
+ |
+ if (stream_id != kProgramStreamMap && |
+ stream_id != kPaddingStream && |
+ stream_id != kPrivateStream2 && |
+ stream_id != kEcmStream && |
+ stream_id != kEmmStream && |
+ stream_id != kProgramStreamDirectory && |
+ stream_id != kDsmccStream && |
+ stream_id != kItu222TypeEStream) { |
+ if (remaining_size < 3) { |
+ return false; |
+ } |
+ int dummy_2; |
+ DCHECK(bit_reader.ReadBits(2, &dummy_2)); |
+ if (dummy_2 != 0x2) { |
+ LOG(WARNING) << "Unexpected bits"; |
+ return false; |
+ } |
+ int PES_scrambling_control; |
+ DCHECK(bit_reader.ReadBits(2, &PES_scrambling_control)); |
+ bool PES_priority; |
+ DCHECK(bit_reader.ReadBits(1, &PES_priority)); |
+ bool data_alignment_indicator; |
+ DCHECK(bit_reader.ReadBits(1, &data_alignment_indicator)); |
+ bool copyright; |
+ DCHECK(bit_reader.ReadBits(1, ©right)); |
+ bool original_or_copy; |
+ DCHECK(bit_reader.ReadBits(1, &original_or_copy)); |
+ int pts_dts_flags; |
+ DCHECK(bit_reader.ReadBits(2, &pts_dts_flags)); |
+ bool escr_flag; |
+ DCHECK(bit_reader.ReadBits(1, &escr_flag)); |
+ bool es_rate_flag; |
+ DCHECK(bit_reader.ReadBits(1, &es_rate_flag)); |
+ bool dsm_trick_mode_flag; |
+ DCHECK(bit_reader.ReadBits(1, &dsm_trick_mode_flag)); |
+ bool additional_copy_info_flag; |
+ DCHECK(bit_reader.ReadBits(1, &additional_copy_info_flag)); |
+ bool pes_crc_flag; |
+ DCHECK(bit_reader.ReadBits(1, &pes_crc_flag)); |
+ bool pes_extension_flag; |
+ DCHECK(bit_reader.ReadBits(1, &pes_extension_flag)); |
+ int pes_header_data_length; |
+ DCHECK(bit_reader.ReadBits(8, &pes_header_data_length)); |
+ remaining_size -= 3; |
+ pes_packet_length -= 3; |
+ |
+ bool is_pts_valid = false; |
+ bool is_dts_valid = false; |
+ int64 pts_section = 0; |
+ int64 dts_section = 0; |
+ if (pts_dts_flags == 0x2) { |
+ if (remaining_size < 5) { |
+ return false; |
+ } |
+ DCHECK(bit_reader.ReadBits(40, &pts_section)); |
+ if (((pts_section >> 36) & 0xf) != 0x2 || |
+ !IsTimestampSectionValid(pts_section)) { |
+ LOG(WARNING) << "Invalid PTS section: " |
+ << std::hex << "0x" << pts_section << std::dec; |
+ return false; |
+ } |
+ is_pts_valid = true; |
+ remaining_size -= 5; |
+ pes_packet_length -= 5; |
+ pes_header_data_length -= 5; |
+ } |
+ |
+ if (pts_dts_flags == 0x3) { |
+ if (remaining_size < 10) { |
+ return false; |
+ } |
+ DCHECK(bit_reader.ReadBits(40, &pts_section)); |
+ if (((pts_section >> 36) & 0xf) != 0x3 || |
+ !IsTimestampSectionValid(pts_section)) { |
+ LOG(WARNING) << "Invalid PTS section: " |
+ << std::hex << "0x" << pts_section << std::dec; |
+ return false; |
+ } |
+ DCHECK(bit_reader.ReadBits(40, &dts_section)); |
+ if (((dts_section >> 36) & 0xf) != 0x1 || |
+ !IsTimestampSectionValid(dts_section)) { |
+ LOG(WARNING) << "Invalid DTS section: " |
+ << std::hex << "0x" << dts_section << std::dec; |
+ return false; |
+ } |
+ is_pts_valid = true; |
+ is_dts_valid = true; |
+ remaining_size -= 10; |
+ pes_packet_length -= 10; |
+ pes_header_data_length -= 10; |
+ } |
+ |
+ int64 pts = 0; |
+ int64 dts = 0; |
+ base::TimeDelta media_pts; |
+ base::TimeDelta media_dts; |
+ if (is_pts_valid) { |
+ pts = ConvertTimestampSectionToTimestamp(pts_section); |
+ if (previous_pts_valid_) { |
+ pts = UnrollTimestamp(previous_pts_, pts, 33); |
+ } |
+ previous_pts_ = pts; |
+ previous_pts_valid_ = true; |
+ media_pts = base::TimeDelta::FromMicroseconds((1000 * pts) / 90); |
+ } |
+ |
+ if (is_dts_valid) { |
+ dts = ConvertTimestampSectionToTimestamp(dts_section); |
+ if (previous_dts_valid_) { |
+ dts = UnrollTimestamp(previous_dts_, dts, 33); |
+ } |
+ previous_dts_ = dts; |
+ previous_dts_valid_ = true; |
+ media_dts = base::TimeDelta::FromMicroseconds((1000 * dts) / 90); |
+ } |
+ |
+ // TODO(damienv): check whether we need some info |
+ // from the rest of the PES packet header. |
+ if (pes_header_data_length < 0) { |
+ LOG(WARNING) << "Invalid PES header length"; |
+ return false; |
+ } |
+ if (pes_header_data_length > 0) { |
+ if (remaining_size < pes_header_data_length) { |
+ return false; |
+ } |
+ for (int k = 0; k < pes_header_data_length; k++) { |
+ int dummy; |
+ DCHECK(bit_reader.ReadBits(8, &dummy)); |
+ } |
+ remaining_size -= pes_header_data_length; |
+ pes_packet_length -= pes_header_data_length; |
+ } |
+ |
+ // Read the PES packet. |
+ if (pes_packet_length_defined && pes_packet_length < 0) { |
+ LOG(WARNING) << "Invalid ES length: " << pes_packet_length; |
+ return false; |
+ } |
+ int es_size = pes_packet_length; |
+ if (!pes_packet_length_defined) { |
+ es_size = remaining_size; |
+ } |
+ VLOG(LOG_LEVEL_PES) |
+ << "Emit a reassembled PES:" |
+ << " size=" << es_size |
+ << " pts=" << pts |
+ << " dts=" << dts |
+ << " data_alignment_indicator=" << data_alignment_indicator; |
+ |
+ int dump_count = 16; |
+ if (es_size < 16) |
+ dump_count = es_size; |
+ int es_offset = raw_pes_.size() - remaining_size; |
+ VLOG(LOG_LEVEL_PES) |
+ << "=> " << base::HexEncode(&raw_pes_[es_offset], dump_count); |
+ if (es_parser_) { |
+ es_parser_->Parse(&raw_pes_[es_offset], es_size, |
+ is_pts_valid, media_pts, |
+ is_dts_valid, media_dts); |
+ } |
+ return true; |
+ } |
+ |
+ if (stream_id == kPaddingStream) { |
+ return true; |
+ } |
+ |
+ // Handle the remaining stream ids. |
+ VLOG(LOG_LEVEL_PES) |
+ << "PES packet:" |
+ << " size=" << pes_packet_length; |
+ return true; |
+} |
+ |
+} // namespace mpeg2ts |
+} // namespace media |