| // 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_ |