Commit 97fd1b87 authored by damienv's avatar damienv Committed by Commit bot

Make the timestamp unroll offset consistent accross PES pids.

BUG=411015

Review URL: https://codereview.chromium.org/539343002

Cr-Commit-Position: refs/heads/master@{#295129}
parent 83ea5899
...@@ -374,6 +374,8 @@ component("media") { ...@@ -374,6 +374,8 @@ component("media") {
"formats/mp2t/mp2t_common.h", "formats/mp2t/mp2t_common.h",
"formats/mp2t/mp2t_stream_parser.cc", "formats/mp2t/mp2t_stream_parser.cc",
"formats/mp2t/mp2t_stream_parser.h", "formats/mp2t/mp2t_stream_parser.h",
"formats/mp2t/timestamp_unroller.cc",
"formats/mp2t/timestamp_unroller.h",
"formats/mp2t/ts_packet.cc", "formats/mp2t/ts_packet.cc",
"formats/mp2t/ts_packet.h", "formats/mp2t/ts_packet.h",
"formats/mp2t/ts_section.h", "formats/mp2t/ts_section.h",
...@@ -542,6 +544,7 @@ test("media_unittests") { ...@@ -542,6 +544,7 @@ test("media_unittests") {
"formats/mp2t/es_parser_test_base.cc", "formats/mp2t/es_parser_test_base.cc",
"formats/mp2t/es_parser_test_base.h", "formats/mp2t/es_parser_test_base.h",
"formats/mp2t/mp2t_stream_parser_unittest.cc", "formats/mp2t/mp2t_stream_parser_unittest.cc",
"formats/mp2t/timestamp_unroller_unittest.cc",
"formats/mp4/aac_unittest.cc", "formats/mp4/aac_unittest.cc",
"formats/mp4/avc_unittest.cc", "formats/mp4/avc_unittest.cc",
"formats/mp4/box_reader_unittest.cc", "formats/mp4/box_reader_unittest.cc",
......
...@@ -159,6 +159,8 @@ bool EsParserAdts::ParseFromEsQueue() { ...@@ -159,6 +159,8 @@ bool EsParserAdts::ParseFromEsQueue() {
is_key_frame, is_key_frame,
DemuxerStream::AUDIO, 0); DemuxerStream::AUDIO, 0);
stream_parser_buffer->set_timestamp(current_pts); stream_parser_buffer->set_timestamp(current_pts);
stream_parser_buffer->SetDecodeTimestamp(
DecodeTimestamp::FromPresentationTime(current_pts));
stream_parser_buffer->set_duration(frame_duration); stream_parser_buffer->set_duration(frame_duration);
emit_buffer_cb_.Run(stream_parser_buffer); emit_buffer_cb_.Run(stream_parser_buffer);
......
...@@ -218,6 +218,9 @@ void Mp2tStreamParser::Flush() { ...@@ -218,6 +218,9 @@ void Mp2tStreamParser::Flush() {
// Reset the selected PIDs. // Reset the selected PIDs.
selected_audio_pid_ = -1; selected_audio_pid_ = -1;
selected_video_pid_ = -1; selected_video_pid_ = -1;
// Reset the timestamp unroller.
timestamp_unroller_.Reset();
} }
bool Mp2tStreamParser::Parse(const uint8* buf, int size) { bool Mp2tStreamParser::Parse(const uint8* buf, int size) {
...@@ -368,7 +371,7 @@ void Mp2tStreamParser::RegisterPes(int pmt_pid, ...@@ -368,7 +371,7 @@ void Mp2tStreamParser::RegisterPes(int pmt_pid,
// Create the PES state here. // Create the PES state here.
DVLOG(1) << "Create a new PES state"; DVLOG(1) << "Create a new PES state";
scoped_ptr<TsSection> pes_section_parser( scoped_ptr<TsSection> pes_section_parser(
new TsSectionPes(es_parser.Pass())); new TsSectionPes(es_parser.Pass(), &timestamp_unroller_));
PidState::PidType pid_type = PidState::PidType pid_type =
is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes;
scoped_ptr<PidState> pes_pid_state( scoped_ptr<PidState> pes_pid_state(
...@@ -531,10 +534,6 @@ void Mp2tStreamParser::OnEmitAudioBuffer( ...@@ -531,10 +534,6 @@ void Mp2tStreamParser::OnEmitAudioBuffer(
<< stream_parser_buffer->timestamp().InMilliseconds() << stream_parser_buffer->timestamp().InMilliseconds()
<< " dur=" << " dur="
<< stream_parser_buffer->duration().InMilliseconds(); << stream_parser_buffer->duration().InMilliseconds();
stream_parser_buffer->set_timestamp(
stream_parser_buffer->timestamp() - time_offset_);
stream_parser_buffer->SetDecodeTimestamp(
stream_parser_buffer->GetDecodeTimestamp() - time_offset_);
// Ignore the incoming buffer if it is not associated with any config. // Ignore the incoming buffer if it is not associated with any config.
if (buffer_queue_chain_.empty()) { if (buffer_queue_chain_.empty()) {
...@@ -562,10 +561,6 @@ void Mp2tStreamParser::OnEmitVideoBuffer( ...@@ -562,10 +561,6 @@ void Mp2tStreamParser::OnEmitVideoBuffer(
<< stream_parser_buffer->duration().InMilliseconds() << stream_parser_buffer->duration().InMilliseconds()
<< " IsKeyframe=" << " IsKeyframe="
<< stream_parser_buffer->IsKeyframe(); << stream_parser_buffer->IsKeyframe();
stream_parser_buffer->set_timestamp(
stream_parser_buffer->timestamp() - time_offset_);
stream_parser_buffer->SetDecodeTimestamp(
stream_parser_buffer->GetDecodeTimestamp() - time_offset_);
// Ignore the incoming buffer if it is not associated with any config. // Ignore the incoming buffer if it is not associated with any config.
if (buffer_queue_chain_.empty()) { if (buffer_queue_chain_.empty()) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "media/base/media_export.h" #include "media/base/media_export.h"
#include "media/base/stream_parser.h" #include "media/base/stream_parser.h"
#include "media/base/video_decoder_config.h" #include "media/base/video_decoder_config.h"
#include "media/formats/mp2t/timestamp_unroller.h"
namespace media { namespace media {
...@@ -123,7 +124,11 @@ class MEDIA_EXPORT Mp2tStreamParser : public StreamParser { ...@@ -123,7 +124,11 @@ class MEDIA_EXPORT Mp2tStreamParser : public StreamParser {
// Indicate whether a segment was started. // Indicate whether a segment was started.
bool segment_started_; bool segment_started_;
base::TimeDelta time_offset_;
// Timestamp unroller.
// Timestamps in PES packets must be unrolled using the same offset.
// So the unroller is global between PES pids.
TimestampUnroller timestamp_unroller_;
DISALLOW_COPY_AND_ASSIGN(Mp2tStreamParser); DISALLOW_COPY_AND_ASSIGN(Mp2tStreamParser);
}; };
......
// 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/timestamp_unroller.h"
#include "base/logging.h"
namespace media {
namespace mp2t {
TimestampUnroller::TimestampUnroller()
: is_previous_timestamp_valid_(false),
previous_unrolled_timestamp_(0) {
}
TimestampUnroller::~TimestampUnroller() {
}
int64 TimestampUnroller::GetUnrolledTimestamp(int64 timestamp) {
// Mpeg2 TS timestamps have an accuracy of 33 bits.
const int nbits = 33;
// |timestamp| has a precision of |nbits|
// so make sure the highest bits are set to 0.
DCHECK_EQ((timestamp >> nbits), 0);
if (!is_previous_timestamp_valid_) {
previous_unrolled_timestamp_ = timestamp;
is_previous_timestamp_valid_ = true;
return timestamp;
}
// |timestamp| is known modulo 2^33, so estimate the highest bits
// to minimize the discontinuity with the previous unrolled timestamp.
// Three possibilities are considered to estimate the missing high bits
// of |timestamp|. If the bits of the previous unrolled timestamp are
// {b63, b62, ..., b0} and bits of |timestamp| are {0, ..., 0, a32, ..., a0}
// then the 3 possibilities are:
// 1) t1 = {b63, ..., b33, a32, ..., a0} (apply the same offset multiple
// of 2^33 as the one used for the previous timestamp)
// 2) t0 = t1 - 2^33
// 3) t2 = t1 + 2^33
//
// A few remarks:
// - the purpose of the timestamp unroller is only to unroll timestamps
// in such a way timestamp continuity is satisfied. It can generate negative
// values during that process.
// - possible overflows are not considered here since 64 bits on a 90kHz
// timescale is way enough to represent several years of playback.
int64 previous_unrolled_time_high =
(previous_unrolled_timestamp_ >> nbits);
int64 time0 = ((previous_unrolled_time_high - 1) << nbits) | timestamp;
int64 time1 = ((previous_unrolled_time_high + 0) << nbits) | timestamp;
int64 time2 = ((previous_unrolled_time_high + 1) << nbits) | timestamp;
// Select the min absolute difference with the current time
// so as to ensure time continuity.
int64 diff0 = time0 - previous_unrolled_timestamp_;
int64 diff1 = time1 - previous_unrolled_timestamp_;
int64 diff2 = time2 - previous_unrolled_timestamp_;
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;
// Update the state of the timestamp unroller.
previous_unrolled_timestamp_ = unrolled_time;
return unrolled_time;
}
void TimestampUnroller::Reset() {
is_previous_timestamp_valid_ = false;
previous_unrolled_timestamp_ = 0;
}
} // namespace mp2t
} // namespace media
// 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.
#ifndef MEDIA_FORMATS_MP2T_TIMESTAMP_UNROLLER_H_
#define MEDIA_FORMATS_MP2T_TIMESTAMP_UNROLLER_H_
#include "base/basictypes.h"
#include "base/macros.h"
#include "media/base/media_export.h"
namespace media {
namespace mp2t {
class MEDIA_EXPORT TimestampUnroller {
public:
TimestampUnroller();
~TimestampUnroller();
// Given that |timestamp| is coded using 33 bits (accuracy of MPEG-2 TS
// timestamps), GetUnrolledTimestamp returns the corresponding unrolled
// timestamp.
// The unrolled timestamp is defined by:
// |timestamp| + k * (2 ^ 33)
// where k is estimated so that the unrolled timestamp is as close as
// possible to the previous unrolled timestamp returned by this function
// (if this function has not been called before, it will return the timestamp
// unmodified).
int64 GetUnrolledTimestamp(int64 timestamp);
// Reset the TimestampUnroller to its initial state.
void Reset();
private:
// Indicate whether the value of |previous_unrolled_timestamp_| is valid.
bool is_previous_timestamp_valid_;
// This is the last output of GetUnrolledTimestamp.
int64 previous_unrolled_timestamp_;
DISALLOW_COPY_AND_ASSIGN(TimestampUnroller);
};
} // namespace mp2t
} // namespace media
#endif // MEDIA_FORMATS_MP2T_TIMESTAMP_UNROLLER_H_
// 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 <vector>
#include "base/logging.h"
#include "base/port.h"
#include "base/test/perf_test_suite.h"
#include "media/formats/mp2t/timestamp_unroller.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace mp2t {
static std::vector<int64> TruncateTimestamps(
const std::vector<int64>& timestamps) {
const int nbits = 33;
int64 truncate_mask = (GG_INT64_C(1) << nbits) - 1;
std::vector<int64> truncated_timestamps(timestamps.size());
for (size_t k = 0; k < timestamps.size(); k++)
truncated_timestamps[k] = timestamps[k] & truncate_mask;
return truncated_timestamps;
}
static void RunUnrollTest(const std::vector<int64>& timestamps) {
std::vector<int64> truncated_timestamps = TruncateTimestamps(timestamps);
TimestampUnroller timestamp_unroller;
for (size_t k = 0; k < timestamps.size(); k++) {
int64 unrolled_timestamp =
timestamp_unroller.GetUnrolledTimestamp(truncated_timestamps[k]);
EXPECT_EQ(timestamps[k], unrolled_timestamp);
}
}
TEST(TimestampUnrollerTest, SingleStream) {
// Array of 64 bit timestamps.
// This is the expected result from unrolling these timestamps
// truncated to 33 bits.
int64 timestamps[] = {
GG_INT64_C(0x0000000000000000),
GG_INT64_C(-190), // - 190
GG_INT64_C(0x00000000aaaaa9ed), // + 0xaaaaaaab
GG_INT64_C(0x0000000155555498), // + 0xaaaaaaab
GG_INT64_C(0x00000001ffffff43), // + 0xaaaaaaab
GG_INT64_C(0x00000002aaaaa9ee), // + 0xaaaaaaab
GG_INT64_C(0x0000000355555499), // + 0xaaaaaaab
GG_INT64_C(0x00000003ffffff44), // + 0xaaaaaaab
};
std::vector<int64> timestamps_vector(
timestamps, timestamps + arraysize(timestamps));
RunUnrollTest(timestamps_vector);
}
} // namespace mp2t
} // namespace media
...@@ -10,57 +10,10 @@ ...@@ -10,57 +10,10 @@
#include "media/base/buffers.h" #include "media/base/buffers.h"
#include "media/formats/mp2t/es_parser.h" #include "media/formats/mp2t/es_parser.h"
#include "media/formats/mp2t/mp2t_common.h" #include "media/formats/mp2t/mp2t_common.h"
#include "media/formats/mp2t/timestamp_unroller.h"
static const int kPesStartCode = 0x000001; static const int kPesStartCode = 0x000001;
// Given that |time| is coded using 33 bits,
// UnrollTimestamp returns the corresponding unrolled timestamp.
// The unrolled timestamp is defined by:
// |time| + k * (2 ^ 33)
// where k is estimated so that the unrolled timestamp
// is as close as possible to |previous_unrolled_time|.
static int64 UnrollTimestamp(int64 previous_unrolled_time, int64 time) {
// Mpeg2 TS timestamps have an accuracy of 33 bits.
const int nbits = 33;
// |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;
return unrolled_time;
}
static bool IsTimestampSectionValid(int64 timestamp_section) { static bool IsTimestampSectionValid(int64 timestamp_section) {
// |pts_section| has 40 bits: // |pts_section| has 40 bits:
// - starting with either '0010' or '0011' or '0001' // - starting with either '0010' or '0011' or '0001'
...@@ -82,14 +35,13 @@ static int64 ConvertTimestampSectionToTimestamp(int64 timestamp_section) { ...@@ -82,14 +35,13 @@ static int64 ConvertTimestampSectionToTimestamp(int64 timestamp_section) {
namespace media { namespace media {
namespace mp2t { namespace mp2t {
TsSectionPes::TsSectionPes(scoped_ptr<EsParser> es_parser) TsSectionPes::TsSectionPes(scoped_ptr<EsParser> es_parser,
TimestampUnroller* timestamp_unroller)
: es_parser_(es_parser.release()), : es_parser_(es_parser.release()),
wait_for_pusi_(true), wait_for_pusi_(true),
previous_pts_valid_(false), timestamp_unroller_(timestamp_unroller) {
previous_pts_(0),
previous_dts_valid_(false),
previous_dts_(0) {
DCHECK(es_parser_); DCHECK(es_parser_);
DCHECK(timestamp_unroller_);
} }
TsSectionPes::~TsSectionPes() { TsSectionPes::~TsSectionPes() {
...@@ -138,12 +90,6 @@ void TsSectionPes::Flush() { ...@@ -138,12 +90,6 @@ void TsSectionPes::Flush() {
void TsSectionPes::Reset() { void TsSectionPes::Reset() {
ResetPesState(); ResetPesState();
previous_pts_valid_ = false;
previous_pts_ = 0;
previous_dts_valid_ = false;
previous_dts_ = 0;
es_parser_->Reset(); es_parser_->Reset();
} }
...@@ -269,19 +215,13 @@ bool TsSectionPes::ParseInternal(const uint8* raw_pes, int raw_pes_size) { ...@@ -269,19 +215,13 @@ bool TsSectionPes::ParseInternal(const uint8* raw_pes, int raw_pes_size) {
base::TimeDelta media_pts(kNoTimestamp()); base::TimeDelta media_pts(kNoTimestamp());
DecodeTimestamp media_dts(kNoDecodeTimestamp()); DecodeTimestamp media_dts(kNoDecodeTimestamp());
if (is_pts_valid) { if (is_pts_valid) {
int64 pts = ConvertTimestampSectionToTimestamp(pts_section); int64 pts = timestamp_unroller_->GetUnrolledTimestamp(
if (previous_pts_valid_) ConvertTimestampSectionToTimestamp(pts_section));
pts = UnrollTimestamp(previous_pts_, pts);
previous_pts_ = pts;
previous_pts_valid_ = true;
media_pts = base::TimeDelta::FromMicroseconds((1000 * pts) / 90); media_pts = base::TimeDelta::FromMicroseconds((1000 * pts) / 90);
} }
if (is_dts_valid) { if (is_dts_valid) {
int64 dts = ConvertTimestampSectionToTimestamp(dts_section); int64 dts = timestamp_unroller_->GetUnrolledTimestamp(
if (previous_dts_valid_) ConvertTimestampSectionToTimestamp(dts_section));
dts = UnrollTimestamp(previous_dts_, dts);
previous_dts_ = dts;
previous_dts_valid_ = true;
media_dts = DecodeTimestamp::FromMicroseconds((1000 * dts) / 90); media_dts = DecodeTimestamp::FromMicroseconds((1000 * dts) / 90);
} }
......
...@@ -15,10 +15,12 @@ namespace media { ...@@ -15,10 +15,12 @@ namespace media {
namespace mp2t { namespace mp2t {
class EsParser; class EsParser;
class TimestampUnroller;
class TsSectionPes : public TsSection { class TsSectionPes : public TsSection {
public: public:
explicit TsSectionPes(scoped_ptr<EsParser> es_parser); TsSectionPes(scoped_ptr<EsParser> es_parser,
TimestampUnroller* timestamp_unroller);
virtual ~TsSectionPes(); virtual ~TsSectionPes();
// TsSection implementation. // TsSection implementation.
...@@ -49,10 +51,7 @@ class TsSectionPes : public TsSection { ...@@ -49,10 +51,7 @@ class TsSectionPes : public TsSection {
bool wait_for_pusi_; bool wait_for_pusi_;
// Used to unroll PTS and DTS. // Used to unroll PTS and DTS.
bool previous_pts_valid_; TimestampUnroller* const timestamp_unroller_;
int64 previous_pts_;
bool previous_dts_valid_;
int64 previous_dts_;
DISALLOW_COPY_AND_ASSIGN(TsSectionPes); DISALLOW_COPY_AND_ASSIGN(TsSectionPes);
}; };
......
...@@ -941,6 +941,8 @@ ...@@ -941,6 +941,8 @@
'formats/mp2t/mp2t_common.h', 'formats/mp2t/mp2t_common.h',
'formats/mp2t/mp2t_stream_parser.cc', 'formats/mp2t/mp2t_stream_parser.cc',
'formats/mp2t/mp2t_stream_parser.h', 'formats/mp2t/mp2t_stream_parser.h',
'formats/mp2t/timestamp_unroller.cc',
'formats/mp2t/timestamp_unroller.h',
'formats/mp2t/ts_packet.cc', 'formats/mp2t/ts_packet.cc',
'formats/mp2t/ts_packet.h', 'formats/mp2t/ts_packet.h',
'formats/mp2t/ts_section.h', 'formats/mp2t/ts_section.h',
...@@ -1330,6 +1332,7 @@ ...@@ -1330,6 +1332,7 @@
'formats/mp2t/es_parser_test_base.cc', 'formats/mp2t/es_parser_test_base.cc',
'formats/mp2t/es_parser_test_base.h', 'formats/mp2t/es_parser_test_base.h',
'formats/mp2t/mp2t_stream_parser_unittest.cc', 'formats/mp2t/mp2t_stream_parser_unittest.cc',
'formats/mp2t/timestamp_unroller_unittest.cc',
'formats/mp4/aac_unittest.cc', 'formats/mp4/aac_unittest.cc',
'formats/mp4/avc_unittest.cc', 'formats/mp4/avc_unittest.cc',
'formats/mp4/box_reader_unittest.cc', 'formats/mp4/box_reader_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment