| // Copyright 2016 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/audio_core/audio_link_packet_source.h" |
| |
| #include "src/lib/fxl/logging.h" |
| #include "src/media/audio/audio_core/audio_object.h" |
| #include "src/media/audio/audio_core/audio_renderer_format_info.h" |
| #include "src/media/audio/audio_core/audio_renderer_impl.h" |
| |
| namespace media::audio { |
| |
| AudioLinkPacketSource::AudioLinkPacketSource( |
| fbl::RefPtr<AudioObject> source, fbl::RefPtr<AudioObject> dest, |
| fbl::RefPtr<AudioRendererFormatInfo> format_info) |
| : AudioLink(SourceType::Packet, std::move(source), std::move(dest)), |
| format_info_(std::move(format_info)) {} |
| |
| AudioLinkPacketSource::~AudioLinkPacketSource() { |
| pending_flush_packet_queue_.clear(); |
| pending_packet_queue_.clear(); |
| pending_flush_token_queue_.clear(); |
| } |
| |
| // static |
| fbl::RefPtr<AudioLinkPacketSource> AudioLinkPacketSource::Create( |
| fbl::RefPtr<AudioObject> source, fbl::RefPtr<AudioObject> dest) { |
| FXL_DCHECK(source); |
| FXL_DCHECK(dest); |
| |
| // TODO(mpuryear): Relax this when other audio objects can be packet sources. |
| if (source->type() != AudioObject::Type::AudioRenderer) { |
| FXL_LOG(ERROR) << "Cannot create packet source link; packet sources must " |
| "be AudioRenderers"; |
| return nullptr; |
| } |
| |
| auto& audio_renderer = *fbl::RefPtr<AudioRendererImpl>::Downcast(source); |
| |
| FXL_DCHECK(audio_renderer.format_info_valid()); |
| return fbl::AdoptRef(new AudioLinkPacketSource( |
| std::move(source), std::move(dest), audio_renderer.format_info())); |
| } |
| |
| void AudioLinkPacketSource::PushToPendingQueue( |
| const fbl::RefPtr<AudioPacketRef>& packet) { |
| std::lock_guard<std::mutex> locker(pending_mutex_); |
| pending_packet_queue_.emplace_back(std::move(packet)); |
| } |
| |
| void AudioLinkPacketSource::FlushPendingQueue( |
| const fbl::RefPtr<PendingFlushToken>& flush_token) { |
| std::deque<fbl::RefPtr<AudioPacketRef>> flushed_packets; |
| |
| { |
| std::lock_guard<std::mutex> locker(pending_mutex_); |
| |
| flushed_ = true; |
| |
| if (processing_in_progress_) { |
| // Is the sink currently mixing? If so, the flush cannot complete until |
| // the mix operation has finished. Move the 'waiting to be rendered' |
| // packets to the back of the 'waiting to be flushed queue', and append |
| // our flush token (if any) to the pending flush token queue. The sink's |
| // thread will take are of releasing these objects back to the service |
| // thread for cleanup when it has finished it's current job. |
| for (auto& packet : pending_packet_queue_) { |
| pending_flush_packet_queue_.emplace_back(std::move(packet)); |
| } |
| pending_packet_queue_.clear(); |
| |
| if (flush_token != nullptr) { |
| pending_flush_token_queue_.emplace_back(std::move(flush_token)); |
| } |
| |
| return; |
| } else { |
| // If the sink is not currently mixing, then we just swap the contents the |
| // pending packet queues with out local queue and release the packets in |
| // the proper order once we have left the pending mutex lock. |
| FXL_DCHECK(pending_flush_packet_queue_.empty()); |
| FXL_DCHECK(pending_flush_token_queue_.empty()); |
| flushed_packets.swap(pending_packet_queue_); |
| } |
| } |
| |
| // Release the packets, front to back. |
| for (auto& ptr : flushed_packets) { |
| ptr.reset(); |
| } |
| } |
| |
| void AudioLinkPacketSource::CopyPendingQueue( |
| const fbl::RefPtr<AudioLinkPacketSource>& other) { |
| FXL_DCHECK(other != nullptr); |
| FXL_DCHECK(this != other.get()); |
| |
| std::lock_guard<std::mutex> source_locker(other->pending_mutex_); |
| if (other->pending_packet_queue_.empty()) |
| return; |
| |
| std::lock_guard<std::mutex> locker(pending_mutex_); |
| FXL_DCHECK(pending_packet_queue_.empty()); |
| pending_packet_queue_ = other->pending_packet_queue_; |
| } |
| |
| fbl::RefPtr<AudioPacketRef> AudioLinkPacketSource::LockPendingQueueFront( |
| bool* was_flushed) { |
| FXL_DCHECK(was_flushed); |
| std::lock_guard<std::mutex> locker(pending_mutex_); |
| |
| FXL_DCHECK(!processing_in_progress_); |
| processing_in_progress_ = true; |
| |
| *was_flushed = flushed_; |
| flushed_ = false; |
| |
| if (pending_packet_queue_.size()) { |
| return pending_packet_queue_.front(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void AudioLinkPacketSource::UnlockPendingQueueFront(bool release_packet) { |
| { |
| std::lock_guard<std::mutex> locker(pending_mutex_); |
| FXL_DCHECK(processing_in_progress_); |
| processing_in_progress_ = false; |
| |
| // Did a flush take place while we were working? If so release each of the |
| // packets waiting to be flushed back to the service thread, then release |
| // each of the flush tokens. |
| if (!pending_flush_packet_queue_.empty() || |
| !pending_flush_token_queue_.empty()) { |
| for (auto& ptr : pending_flush_packet_queue_) { |
| ptr.reset(); |
| } |
| |
| for (auto& ptr : pending_flush_token_queue_) { |
| ptr.reset(); |
| } |
| |
| pending_flush_packet_queue_.clear(); |
| pending_flush_token_queue_.clear(); |
| |
| return; |
| } |
| |
| // If the sink wants us to release the front of the pending queue, and no |
| // flush operation happened while they were processing, then there had |
| // better be a packet at the front of the queue to release. |
| |
| // Assert that the user either got no packet when they locked the queue |
| // (because the queue was empty), or that they got the front of the queue |
| // and that the front of the queue has not changed. |
| FXL_DCHECK(!release_packet || !pending_packet_queue_.empty()); |
| if (release_packet) { |
| pending_packet_queue_.pop_front(); |
| } |
| } |
| } |
| |
| } // namespace media::audio |