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 <cstdint>
10
11#include <absl/log/check.h>
12#include <absl/log/log.h>
13
14namespace shaka {
15namespace media {
16
17int64_t SignedPtsDiff(int64_t a, int64_t b) {
18 // Compute difference in 33-bit space (modulo 2^33)
19 int64_t diff = (a - b) & (kPtsWrapAround - 1);
20
21 // Convert to signed range: (-2^32, 2^32)
22 // If diff >= 2^32, it represents a negative value in 33-bit two's complement
23 if (diff >= kPtsHalfWrapAround) {
24 diff -= kPtsWrapAround;
25 }
26
27 return diff;
28}
29
30bool PtsIsBefore(int64_t a, int64_t b) {
31 return SignedPtsDiff(a, b) < 0;
32}
33
34bool PtsIsBeforeOrEqual(int64_t a, int64_t b) {
35 return SignedPtsDiff(a, b) <= 0;
36}
37
38int64_t PtsUnwrapper::Unwrap(int64_t wrapped_pts) {
39 // Mask to 33-bit range in case input exceeds limit
40 // (Some muxers may produce values > 2^33)
41 wrapped_pts = wrapped_pts & (kPtsWrapAround - 1);
42
43 DCHECK_GE(wrapped_pts, 0);
44 DCHECK_LT(wrapped_pts, kPtsWrapAround);
45
46 if (!initialized_) {
47 // First timestamp - use as-is
48 last_wrapped_ = wrapped_pts;
49 initialized_ = true;
50 DVLOG(3) << "PtsUnwrapper: Initialized with PTS " << wrapped_pts;
51 return wrapped_pts;
52 }
53
54 // Compute signed difference from last timestamp
55 int64_t diff = SignedPtsDiff(wrapped_pts, last_wrapped_);
56
57 // Detect wrap-around: If we see a large negative jump (more than half the
58 // wrap range), it means we wrapped forward from a high value to a low value
59 // Example: last=8589934500, current=100 => diff=-8589934400
60 // But in 33-bit signed space: diff = 192 (wrapped forward)
61 //
62 // Note: SignedPtsDiff already handles this correctly, so if diff is negative,
63 // it means we genuinely went backward in time (e.g., stream discontinuity)
64
65 if (diff < 0) {
66 DVLOG(2) << "PtsUnwrapper: Detected backward jump from " << last_wrapped_
67 << " to " << wrapped_pts << " (diff=" << diff << ")";
68 // This shouldn't happen in normal operation (timestamps should be
69 // monotonic) But handle it gracefully by not adjusting offset
70 }
71
72 // Check for wrap-around: last_wrapped > current, but diff is positive
73 // This happens when: last=8589934500, current=100 => diff=692
74 if (wrapped_pts < last_wrapped_ && diff > 0) {
75 // Wrapped around - add 2^33 to offset
76 unwrapped_offset_ += kPtsWrapAround;
77 DVLOG(2) << "PtsUnwrapper: Detected wrap-around from " << last_wrapped_
78 << " to " << wrapped_pts << ", new offset=" << unwrapped_offset_;
79 }
80
81 last_wrapped_ = wrapped_pts;
82 int64_t unwrapped = wrapped_pts + unwrapped_offset_;
83
84 DVLOG(3) << "PtsUnwrapper: Unwrap(" << wrapped_pts << ") = " << unwrapped
85 << " (offset=" << unwrapped_offset_ << ")";
86
87 return unwrapped;
88}
89
91 initialized_ = false;
92 last_wrapped_ = 0;
93 unwrapped_offset_ = 0;
94 DVLOG(2) << "PtsUnwrapper: Reset";
95}
96
97} // namespace media
98} // 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.