blob: 4ce621343baae7ea9b80dee849737dcce343078b [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "pts_manager.h"
#include <inttypes.h>
#include <zircon/assert.h>
#include "macros.h"
#ifndef AMLOGIC_PTS_DLOG_ENABLE
#define AMLOGIC_PTS_DLOG_ENABLE 0
#endif
#define PTS_DLOG(fmt, ...) \
do { \
if (AMLOGIC_PTS_DLOG_ENABLE) { \
LOG(INFO, fmt, ##__VA_ARGS__); \
} \
} while (0)
// h264 has HW stream offset counter with 0xfffffff max - 28 bit - 256 MiB cycle period
// vp9 has a 32 bit stream offset counter.
void PtsManager::SetLookupBitWidth(uint32_t lookup_bit_width) {
PTS_DLOG("SetLookupBitWidth() lookup_bit_width: %u", lookup_bit_width);
std::lock_guard<std::mutex> lock(lock_);
ZX_DEBUG_ASSERT(lookup_bit_width_ == 64 && lookup_bit_width != 64);
lookup_bit_width_ = lookup_bit_width;
}
void PtsManager::InsertPts(uint64_t offset, bool has_pts, uint64_t pts) {
PTS_DLOG("InsertPts() offset: %" PRIx64 " has_pts: %d pts: %" PRIx64, offset, has_pts, pts);
std::lock_guard<std::mutex> lock(lock_);
ZX_DEBUG_ASSERT(has_pts || !pts);
// caller should not insert duplicates
ZX_DEBUG_ASSERT(offset_to_result_.find(offset) == offset_to_result_.end());
// caller should set offsets in order
ZX_DEBUG_ASSERT(offset_to_result_.empty() || offset > offset_to_result_.rbegin()->first);
offset_to_result_.emplace(std::make_pair(offset, LookupResult(false, has_pts, pts)));
// Erase the oldest PTSes. See the definition of kMaxEntriesToKeep for how we know this will be
// enough entries.
while (offset_to_result_.size() > kMaxEntriesToKeep) {
offset_to_result_.erase(offset_to_result_.begin());
}
}
void PtsManager::SetEndOfStreamOffset(uint64_t end_of_stream_offset) {
PTS_DLOG("SetEndOfStreamOffset() end_of_stream_offset: %" PRIx64, end_of_stream_offset);
std::lock_guard<std::mutex> lock(lock_);
// caller should not insert duplicates
ZX_DEBUG_ASSERT(offset_to_result_.find(end_of_stream_offset) == offset_to_result_.end());
// caller should set offsets in order
ZX_DEBUG_ASSERT(offset_to_result_.empty() ||
end_of_stream_offset > (*offset_to_result_.rbegin()).first);
// caller should only set end of stream offset once
ZX_DEBUG_ASSERT(offset_to_result_.empty() ||
!(*offset_to_result_.rbegin()).second.is_end_of_stream());
offset_to_result_.emplace(std::make_pair(end_of_stream_offset, LookupResult(true, false, 0)));
}
const PtsManager::LookupResult PtsManager::Lookup(uint64_t offset) {
std::lock_guard<std::mutex> lock(lock_);
ZX_DEBUG_ASSERT(offset < (static_cast<uint64_t>(1) << lookup_bit_width_));
// last_inserted_offset is known-good in the sense that it's known to be a valid full-width
// uint64_t input stream offset. We prefer to anchor on this value rather than incrementally
// anchoring on the last bit-extended offset passed in as a query, since we know with higher
// certainty that this value is correct (and both those options are fairly near the bit-extended
// form of the logical offset coming into this method).
uint64_t last_inserted_offset = GetLastInsertedOffset();
// Basically we're determining whether offset is logically above or logically below
// last_inserted_offset.
//
// Shift up to the top bits of the uint64_t, so we can exploit subtraction that underflows to
// compute distance regardless of recent overflow of a and/or b. We could probably also do this
// by chopping off some top order bits after subtraction, but somehow this makes more sense to me.
// This way, we're sorta just creating a and b which are each 64 bit counters with 64 bit natural
// overflow, so we can figure out the logical above/below relationship between offset and
// last_inserted_offset.
uint64_t a = last_inserted_offset << (64 - lookup_bit_width_);
uint64_t b = offset << (64 - lookup_bit_width_);
// Is the distance between a and b smaller if we assume b is logically above a, or if we assume
// a is logically above b. We want to assume the option which has a and b closer together in
// distance on a mod ring, as we don't generally know whether offset will be logically above or
// logically below last_inserted_offset.
//
// One of these will be relatively small, and the other will be huge (or both 0). Another way to
// do this is to check if b - a is < 0x8000000000000000.
if (b - a <= a - b) {
// offset is logically above (or equal to) last_inserted_offset
offset = last_inserted_offset + ((b - a) >> (64 - lookup_bit_width_));
} else {
// offset is logically below last_inserted_offset
offset = last_inserted_offset - ((a - b) >> (64 - lookup_bit_width_));
}
auto it = offset_to_result_.upper_bound(offset);
// Check if this offset is < any element in the list.
if (it == offset_to_result_.begin()) {
PTS_DLOG("it == offset_to_result_.begin() -- offset: %" PRIx64, offset);
return PtsManager::LookupResult(false, false, 0);
}
// Decrement to find the pts corresponding to the last offset <= |offset|.
--it;
if (AMLOGIC_PTS_DLOG_ENABLE) {
const PtsManager::LookupResult& result = it->second;
if (result.is_end_of_stream()) {
PTS_DLOG("Lookup() offset: %" PRIx64 " EOS", offset);
} else {
PTS_DLOG("Lookup() offset: %" PRIx64 " has_pts: %d pts: %" PRIx64, offset, result.has_pts(),
result.pts());
}
}
return it->second;
}
// The last inserted offset is offset_to_result_.rbegin()->first, unless empty() in which case
// logically 0.
uint64_t PtsManager::GetLastInsertedOffset() {
if (offset_to_result_.empty()) {
return 0;
}
return offset_to_result_.rbegin()->first;
}