blob: 1bc5d4e85a5378fe90a562c9d1d29b2432778bd6 [file] [log] [blame] [edit]
// Copyright 2022 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 "src/media/audio/lib/clock/real_clock.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/clock.h>
#include <cmath>
#include <string>
namespace media_audio {
// static
std::shared_ptr<RealClock> RealClock::Create(std::string_view name, zx::clock clock,
uint32_t domain, bool adjustable) {
zx_info_handle_basic_t info;
auto status = clock.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
FX_CHECK(status == ZX_OK) << "clock.get_info failed, status is " << status;
// The monotonic domain is not adjustable.
if (domain == kMonotonicDomain) {
FX_CHECK(!adjustable) << "the system monotonic clock domain is not adjustable";
}
// Adjustable clocks must be writable.
if (adjustable && (info.rights & ZX_RIGHT_WRITE) == 0) {
FX_CHECK(false) << "adjustable clock does not have ZX_RIGHT_WRITE, rights are 0x" << std::hex
<< info.rights;
}
// If we can read the clock now, we will always be able to.
zx_time_t unused;
status = clock.read(&unused);
FX_CHECK(status == ZX_OK) << "clock.read failed, status is " << status;
struct MakePublicCtor : RealClock {
MakePublicCtor(std::string_view name, zx::clock clock, zx_koid_t koid, uint32_t domain,
bool adjustable)
: RealClock(name, std::move(clock), koid, domain, adjustable) {}
};
return std::make_shared<MakePublicCtor>(name, std::move(clock), info.koid, domain, adjustable);
}
// static
std::shared_ptr<RealClock> RealClock::CreateFromMonotonic(std::string_view name, uint32_t domain,
bool adjustable) {
zx::clock clock;
auto status = zx::clock::create(
ZX_CLOCK_OPT_AUTO_START | ZX_CLOCK_OPT_MONOTONIC | ZX_CLOCK_OPT_CONTINUOUS, nullptr, &clock);
FX_DCHECK(status == ZX_OK) << "clock.create failed, status is " << status;
zx_rights_t rights = ZX_RIGHT_TRANSFER | ZX_RIGHT_DUPLICATE | ZX_RIGHT_READ;
if (adjustable) {
rights |= ZX_RIGHT_WRITE;
}
status = clock.replace(rights, &clock);
FX_DCHECK(status == ZX_OK) << "clock.replace failed, status is " << status;
return Create(name, std::move(clock), domain, adjustable);
}
zx::time RealClock::now() const {
// Create checked that we can call read(), so this should never fail.
zx::time t;
auto status = clock_.read(t.get_address());
FX_CHECK(status == ZX_OK) << "clock.read failed, status is " << status;
return t;
}
Clock::ToClockMonoSnapshot RealClock::to_clock_mono_snapshot() const {
// Create checked that we can call read().
// If we can call read(), we can call get_details(), so this should never fail.
zx_clock_details_v1_t details;
auto status = clock_.get_details(&details);
FX_CHECK(status == ZX_OK) << "clock.get_details failed, status is " << status;
// get_details gives us mono-to-reference, so invert that to get reference-to-mono.
return {
.to_clock_mono = media::TimelineFunction(details.mono_to_synthetic.reference_offset,
details.mono_to_synthetic.synthetic_offset,
details.mono_to_synthetic.rate.reference_ticks,
details.mono_to_synthetic.rate.synthetic_ticks),
.generation = details.generation_counter,
};
}
void RealClock::SetRate(int32_t rate_adjust_ppm) {
FX_CHECK(adjustable()) << "cannot SetRate on unadjustable clocks";
// Create verified that the clock has ZX_RIGHT_WRITE, so this should never fail.
zx::clock::update_args args;
args.reset().set_rate_adjust(rate_adjust_ppm);
auto status = clock_.update(args);
FX_CHECK(status == ZX_OK) << "clock.update failed on adjustable clock, status is " << status;
}
std::optional<zx::clock> RealClock::DuplicateZxClockReadOnly() const {
zx_rights_t rights = ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER | ZX_RIGHT_READ;
zx::clock dup;
if (auto status = clock_.duplicate(rights, &dup); status != ZX_OK) {
FX_LOGS(ERROR) << "RealClock.DuplicateZxClockReadOnly failed with status " << status;
return std::nullopt;
}
return dup;
}
} // namespace media_audio