1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/formats/mp2t/timestamp_unroller.h"
6
7#include "base/logging.h"
8
9namespace media {
10namespace mp2t {
11
12TimestampUnroller::TimestampUnroller()
13    : is_previous_timestamp_valid_(false),
14      previous_unrolled_timestamp_(0) {
15}
16
17TimestampUnroller::~TimestampUnroller() {
18}
19
20int64 TimestampUnroller::GetUnrolledTimestamp(int64 timestamp) {
21  // Mpeg2 TS timestamps have an accuracy of 33 bits.
22  const int nbits = 33;
23
24  // |timestamp| has a precision of |nbits|
25  // so make sure the highest bits are set to 0.
26  DCHECK_EQ((timestamp >> nbits), 0);
27
28  if (!is_previous_timestamp_valid_) {
29    previous_unrolled_timestamp_ = timestamp;
30    is_previous_timestamp_valid_ = true;
31    return timestamp;
32  }
33
34  // |timestamp| is known modulo 2^33, so estimate the highest bits
35  // to minimize the discontinuity with the previous unrolled timestamp.
36  // Three possibilities are considered to estimate the missing high bits
37  // of |timestamp|. If the bits of the previous unrolled timestamp are
38  // {b63, b62, ..., b0} and bits of |timestamp| are {0, ..., 0, a32, ..., a0}
39  // then the 3 possibilities are:
40  // 1) t1 = {b63, ..., b33, a32, ..., a0} (apply the same offset multiple
41  //    of 2^33 as the one used for the previous timestamp)
42  // 2) t0 = t1 - 2^33
43  // 3) t2 = t1 + 2^33
44  //
45  // A few remarks:
46  // - the purpose of the timestamp unroller is only to unroll timestamps
47  // in such a way timestamp continuity is satisfied. It can generate negative
48  // values during that process.
49  // - possible overflows are not considered here since 64 bits on a 90kHz
50  // timescale is way enough to represent several years of playback.
51  int64 previous_unrolled_time_high =
52      (previous_unrolled_timestamp_ >> nbits);
53  int64 time0 = ((previous_unrolled_time_high - 1) << nbits) | timestamp;
54  int64 time1 = ((previous_unrolled_time_high + 0) << nbits) | timestamp;
55  int64 time2 = ((previous_unrolled_time_high + 1) << nbits) | timestamp;
56
57  // Select the min absolute difference with the current time
58  // so as to ensure time continuity.
59  int64 diff0 = time0 - previous_unrolled_timestamp_;
60  int64 diff1 = time1 - previous_unrolled_timestamp_;
61  int64 diff2 = time2 - previous_unrolled_timestamp_;
62  if (diff0 < 0)
63    diff0 = -diff0;
64  if (diff1 < 0)
65    diff1 = -diff1;
66  if (diff2 < 0)
67    diff2 = -diff2;
68
69  int64 unrolled_time;
70  int64 min_diff;
71  if (diff1 < diff0) {
72    unrolled_time = time1;
73    min_diff = diff1;
74  } else {
75    unrolled_time = time0;
76    min_diff = diff0;
77  }
78  if (diff2 < min_diff)
79    unrolled_time = time2;
80
81  // Update the state of the timestamp unroller.
82  previous_unrolled_timestamp_ = unrolled_time;
83
84  return unrolled_time;
85}
86
87void TimestampUnroller::Reset() {
88  is_previous_timestamp_valid_ = false;
89  previous_unrolled_timestamp_ = 0;
90}
91
92}  // namespace mp2t
93}  // namespace media
94