Shaka Packager SDK
Loading...
Searching...
No Matches
timestamp_util.cc
1// Copyright 2025 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/media/base/timestamp_util.h>
8
9#include <absl/log/check.h>
10#include <absl/log/log.h>
11
12namespace shaka {
13namespace media {
14
15int64_t SignedPtsDiff(int64_t a, int64_t b) {
16 // Compute difference in 33-bit space (modulo 2^33)
17 int64_t diff = (a - b) & (kPtsWrapAround - 1);
18
19 // Convert to signed range: (-2^32, 2^32)
20 // If diff >= 2^32, it represents a negative value in 33-bit two's complement
21 if (diff >= kPtsHalfWrapAround) {
22 diff -= kPtsWrapAround;
23 }
24
25 return diff;
26}
27
28bool PtsIsBefore(int64_t a, int64_t b) {
29 return SignedPtsDiff(a, b) < 0;
30}
31
32bool PtsIsBeforeOrEqual(int64_t a, int64_t b) {
33 return SignedPtsDiff(a, b) <= 0;
34}
35
36int64_t PtsUnwrapper::Unwrap(int64_t wrapped_pts) {
37 // Mask to 33-bit range in case input exceeds limit
38 // (Some muxers may produce values > 2^33)
39 wrapped_pts = wrapped_pts & (kPtsWrapAround - 1);
40
41 DCHECK_GE(wrapped_pts, 0);
42 DCHECK_LT(wrapped_pts, kPtsWrapAround);
43
44 if (!initialized_) {
45 // First timestamp - use as-is
46 last_wrapped_ = wrapped_pts;
47 initialized_ = true;
48 DVLOG(3) << "PtsUnwrapper: Initialized with PTS " << wrapped_pts;
49 return wrapped_pts;
50 }
51
52 // Compute signed difference from last timestamp
53 int64_t diff = SignedPtsDiff(wrapped_pts, last_wrapped_);
54
55 // Detect wrap-around: If we see a large negative jump (more than half the
56 // wrap range), it means we wrapped forward from a high value to a low value
57 // Example: last=8589934500, current=100 => diff=-8589934400
58 // But in 33-bit signed space: diff = 192 (wrapped forward)
59 //
60 // Note: SignedPtsDiff already handles this correctly, so if diff is negative,
61 // it means we genuinely went backward in time (e.g., stream discontinuity)
62
63 if (diff < 0) {
64 DVLOG(2) << "PtsUnwrapper: Detected backward jump from " << last_wrapped_
65 << " to " << wrapped_pts << " (diff=" << diff << ")";
66 // This shouldn't happen in normal operation (timestamps should be
67 // monotonic) But handle it gracefully by not adjusting offset
68 }
69
70 // Check for wrap-around: last_wrapped > current, but diff is positive
71 // This happens when: last=8589934500, current=100 => diff=692
72 if (wrapped_pts < last_wrapped_ && diff > 0) {
73 // Wrapped around - add 2^33 to offset
74 unwrapped_offset_ += kPtsWrapAround;
75 DVLOG(2) << "PtsUnwrapper: Detected wrap-around from " << last_wrapped_
76 << " to " << wrapped_pts << ", new offset=" << unwrapped_offset_;
77 }
78
79 last_wrapped_ = wrapped_pts;
80 int64_t unwrapped = wrapped_pts + unwrapped_offset_;
81
82 DVLOG(3) << "PtsUnwrapper: Unwrap(" << wrapped_pts << ") = " << unwrapped
83 << " (offset=" << unwrapped_offset_ << ")";
84
85 return unwrapped;
86}
87
89 initialized_ = false;
90 last_wrapped_ = 0;
91 unwrapped_offset_ = 0;
92 DVLOG(2) << "PtsUnwrapper: Reset";
93}
94
95} // namespace media
96} // namespace shaka
int64_t Unwrap(int64_t wrapped_pts)
void Reset()
Resets the unwrapper state (for stream discontinuities).
All the methods that are virtual are virtual for mocking.