/* * Copyright (c) 2025, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Media { // Retrieves coded data from a demuxer and decodes it asynchronously into audio samples to push to an AudioSink. class MEDIA_API AudioDataProvider : public AtomicRefCounted { class ThreadData; public: static constexpr size_t QUEUE_CAPACITY = 16; using AudioQueue = Queue; using ErrorHandler = Function; using BlockEndTimeHandler = Function; using SeekCompletionHandler = Function; static DecoderErrorOr> try_create(NonnullRefPtr const& main_thread_event_loop, NonnullRefPtr const& demuxer, Track const& track); AudioDataProvider(NonnullRefPtr const&); ~AudioDataProvider(); void set_error_handler(ErrorHandler&&); void set_block_end_time_handler(BlockEndTimeHandler&&); void set_output_sample_specification(Audio::SampleSpecification); void start(); void suspend(); void resume(); AudioBlock retrieve_block(); void seek(AK::Duration timestamp, SeekCompletionHandler&& = nullptr); private: class ThreadData final : public AtomicRefCounted { public: ThreadData(NonnullRefPtr const& main_thread_event_loop, NonnullRefPtr const&, Track const&, NonnullOwnPtr&&); ~ThreadData(); void set_error_handler(ErrorHandler&&); void set_block_end_time_handler(BlockEndTimeHandler&&); void set_output_sample_specification(Audio::SampleSpecification); void start(); DecoderErrorOr create_decoder(); void suspend(); void resume(); void exit(); void wait_for_start(); bool should_thread_exit_while_locked() const; bool should_thread_exit() const; bool handle_suspension(); template void invoke_on_main_thread_while_locked(Invokee); template void invoke_on_main_thread(Invokee); void dispatch_block_end_time(AudioBlock const&); void queue_block(AudioBlock&&); void flush_decoder(); DecoderErrorOr retrieve_next_block(AudioBlock&); bool handle_seek(); template void process_seek_on_main_thread(u32 seek_id, Callback); void resolve_seek(u32 seek_id); void push_data_and_decode_a_block(); void seek(AK::Duration timestamp, SeekCompletionHandler&&); [[nodiscard]] Threading::MutexLocker take_lock() const { return Threading::MutexLocker(m_mutex); } void wake() const { m_wait_condition.broadcast(); } AudioDecoder const& decoder() const { return *m_decoder; } AudioQueue& queue() { return m_queue; } private: enum class RequestedState : u8 { None, Running, Suspended, Exit, }; NonnullRefPtr m_main_thread_event_loop; mutable Threading::Mutex m_mutex; mutable Threading::ConditionVariable m_wait_condition { m_mutex }; RequestedState m_requested_state { RequestedState::None }; NonnullRefPtr m_demuxer; Track m_track; OwnPtr m_decoder; bool m_decoder_needs_keyframe_next_seek { false }; NonnullOwnPtr m_converter; i64 m_last_sample { NumericLimits::min() }; size_t m_queue_max_size { 8 }; AudioQueue m_queue; BlockEndTimeHandler m_frame_end_time_handler; ErrorHandler m_error_handler; bool m_is_in_error_state { false }; u32 m_last_processed_seek_id { 0 }; Atomic m_seek_id { 0 }; SeekCompletionHandler m_seek_completion_handler; AK::Duration m_seek_timestamp; }; NonnullRefPtr m_thread_data; }; }