blob: 82efcb39bdd69e7591be06a22afb3200ede22090 [file] [log] [blame]
// Copyright 2019 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.
#ifndef SRC_MEDIA_AUDIO_AUDIO_CORE_STREAM_H_
#define SRC_MEDIA_AUDIO_AUDIO_CORE_STREAM_H_
#include <fuchsia/media/cpp/fidl.h>
#include <lib/fit/result.h>
#include <lib/zx/time.h>
#include <optional>
#include "src/media/audio/audio_core/audio_clock.h"
#include "src/media/audio/audio_core/packet.h"
#include "src/media/audio/audio_core/stream_usage.h"
#include "src/media/audio/lib/format/format.h"
#include "src/media/audio/lib/timeline/timeline_function.h"
namespace media::audio {
class BaseStream {
public:
BaseStream(Format format) : format_(format) {}
virtual ~BaseStream() = default;
// Format of data generated by this stream.
// TODO(fxbug.dev/58740): make sure this is accurate in all implementations.
const Format& format() const { return format_; }
// A snapshot of a |TimelineFunction| with an associated |generation|. If |generation| is equal
// between two subsequent calls to |ref_time_to_fract_presentation_frame|, then the
// |timeline_function| is guaranteed to be unchanged.
struct TimelineFunctionSnapshot {
TimelineFunction timeline_function;
uint32_t generation;
};
// This function translates from a timestamp to the corresponding fixed-point frame number that
// will be presented at that time. The timestamp is relative to the stream's reference clock.
virtual TimelineFunctionSnapshot ref_time_to_frac_presentation_frame() const = 0;
virtual AudioClock& reference_clock() = 0;
// Common shorthands to convert between PTS and frame numbers.
Fixed FracPresentationFrameAtRefTime(zx::time ref_time) const {
return Fixed::FromRaw(
ref_time_to_frac_presentation_frame().timeline_function.Apply(ref_time.get()));
}
zx::time RefTimeAtFracPresentationFrame(Fixed frame) const {
return zx::time(
ref_time_to_frac_presentation_frame().timeline_function.ApplyInverse(frame.raw_value()));
}
// The presentation delay is defined to be the absolute difference between a frame's
// presentation timestamp and the frame's safe read/write timestamp. This is always a
// positive number. Ideally this should be the exact delay, if known, and otherwise a
// true upper-bound of the delay, however in practice it is sometimes a best-effort
// estimate that can be either low or high.
//
// For render pipelines, this represents the delay between reading a frame with
// ReadLock and actually rendering the frame at an output device. This is also known as
// the "min lead time".
//
// For capture pipelines, this represents the delay between capturing a frame at
// an input device and reading that frame with ReadLock.
zx::duration GetPresentationDelay() const { return presentation_delay_.load(); }
// Presentation delays are propagated from destination streams to source streams. The
// delay passed to the source stream is typically external_delay + intrinsic_delay.
// The default implementation is sufficient for pipeline stages that do not introduce
// extra delay.
virtual void SetPresentationDelay(zx::duration external_delay) {
presentation_delay_.store(external_delay);
}
private:
Format format_;
std::atomic<zx::duration> presentation_delay_{zx::duration(0)};
};
// A read-only stream of audio data.
class ReadableStream : public BaseStream {
public:
ReadableStream(Format format) : BaseStream(format) {}
virtual ~ReadableStream() = default;
class Buffer {
public:
using DestructorT = fit::callback<void(bool fully_consumed)>;
Buffer(Fixed start_frame, Fixed length_in_frames, void* payload, bool is_continuous,
StreamUsageMask usage_mask, float gain_db, DestructorT dtor = nullptr)
: dtor_(std::move(dtor)),
payload_(payload),
start_(start_frame),
length_(length_in_frames),
is_continuous_(is_continuous),
usage_mask_(usage_mask),
gain_db_(gain_db) {}
~Buffer() {
if (dtor_) {
dtor_(is_fully_consumed_);
}
}
Buffer(Buffer&& rhs) = default;
Buffer& operator=(Buffer&& rhs) = default;
Buffer(const Buffer& rhs) = delete;
Buffer& operator=(const Buffer& rhs) = delete;
Fixed start() const { return start_; }
Fixed end() const { return start_ + length_; }
Fixed length() const { return length_; }
void* payload() const { return payload_; }
// Indicates this packet is continuous with a packet previously returned from an immediately
// preceding |ReadLock| call.
//
// Buffers may become discontinuous if, for example, and AudioRenderer is flushed and new
// packets are provided; these new packets will not be assumed to be continuous with the
// preceeding ones. Each |ReadableStream| implementation is reponsible for reporting any
// discontinuity so that stream processors (ex: the mixer) may clear any intermediate state
// based on the continuity of the stream.
bool is_continuous() const { return is_continuous_; }
// Call this to indicate whether the buffer was fully consumed.
// By default, we assume this is true.
void set_is_fully_consumed(bool fully_consumed) { is_fully_consumed_ = fully_consumed; }
StreamUsageMask usage_mask() const { return usage_mask_; }
float gain_db() const { return gain_db_; }
private:
DestructorT dtor_;
void* payload_;
Fixed start_;
Fixed length_;
bool is_continuous_;
bool is_fully_consumed_ = true;
StreamUsageMask usage_mask_;
float gain_db_;
};
// ReadableStream is implemented by audio pipeline stages that consume zero or more
// source streams and produce a destination stream. ReadLock acquires a readlock on
// the destination stream. The parameters |dest_frame| and |frame_count| represent a
// range of frames on the destination stream's frame timeline.
//
// If no data is available for that frame range, ReadLock returns std::nullopt.
// Otherwise, ReadLock returns a buffer representing all or part of the requested range.
// If |start()| on the returned buffer is greater than |dest_frame|, then the stream
// has no data for those frames and it may be treated as silence. Conversely, if |end()|
// on the returned buffer is less than |dest_frame + frame_count|, this does not indicate
// silence for those frames. Instead it indicates the full frame range is not available
// on a single contiguous buffer. Clients should call |ReadLock| again and provide the
// |end()| of the previous buffer as |dest_frame| to query if the stream has more frames.
//
// The returned buffer must not refer to frames outside of the range [floor(dest_frame),
// ceiling(dest_frame) + frame_count).
//
// The buffer will remain locked until it is destructed. It is illegal to call ReadLock
// again until the lock has been released.
//
// TODO(fxbug.dev/50669): Implementations must return std::nullopt if they have no frames for the
// requested range. This requirement is not enforced by all implementations (e.g., PacketQueue).
virtual std::optional<Buffer> ReadLock(Fixed dest_frame, size_t frame_count) = 0;
// Trims the stream by releasing any frames before the given frame. When invoked,
// the caller is making a promise that they will not try to ReadLock any frame before
// dest_frame. If the stream has allocated buffers for the trimmed range, it can free
// those buffers now.
virtual void Trim(Fixed dest_frame) = 0;
// Hooks to log [Partial] Underflow events.
// TODO(fxbug.dev/58614): convert this to use PTS instead of frame numbers
virtual void ReportUnderflow(Fixed frac_source_start, Fixed frac_source_mix_point,
zx::duration underflow_duration) {}
virtual void ReportPartialUnderflow(Fixed frac_source_offset, int64_t dest_mix_offset) {}
};
// A write-only stream of audio data.
class WritableStream : public BaseStream {
public:
WritableStream(Format format) : BaseStream(format) {}
virtual ~WritableStream() = default;
// PTS is relative to to parent stream's reference clock.
class Buffer {
public:
using DestructorT = fit::callback<void()>;
Buffer(Fixed start_frame, Fixed length_in_frames, void* payload, DestructorT dtor = nullptr)
: dtor_(std::move(dtor)),
payload_(payload),
start_(start_frame),
length_(length_in_frames) {}
~Buffer() {
if (dtor_) {
dtor_();
}
}
Buffer(Buffer&& rhs) = default;
Buffer& operator=(Buffer&& rhs) = default;
Buffer(const Buffer& rhs) = delete;
Buffer& operator=(const Buffer& rhs) = delete;
Fixed start() const { return start_; }
Fixed end() const { return start_ + length_; }
Fixed length() const { return length_; }
void* payload() const { return payload_; }
private:
DestructorT dtor_;
void* payload_;
Fixed start_;
Fixed length_;
};
// WritableStream is implemented by audio sinks. WriteLock acquires a write lock on the
// stream. The parameters |frame| and |frame_count| represent a range of frames on the
// stream's frame timeline.
//
// If data cannot be written to that frame range, WriteLock returns std::nullopt.
// Otherwise, WriteLock returns a buffer representing all or part of the requested range.
// If |start()| on the returned buffer is greater than |dest_frame|, then no frames before
// |start()| must be written. Conversely, if |end()| on the returned buffer is less than
// |dest_frame + frame_count|, this does not indicate those frames can be omitted. Instead
// it indicates the full frame range is not available on a single contiguous buffer. Clients
// should call |WriteLock| again and provide the |end()| of the previous buffer as |dest_frame|
// to query if the stream has more frames.
//
// The returned buffer must not refer to frames outside of the range [frame, frame + frame_count).
//
// Callers can write directly to the payload. The buffer will remain locked until it is
// destructed. It is illegal to call WriteLock again until the lock has been released.
virtual std::optional<Buffer> WriteLock(int64_t frame, size_t frame_count) = 0;
};
} // namespace media::audio
#endif // SRC_MEDIA_AUDIO_AUDIO_CORE_STREAM_H_