blob: f5516b39b3ca45b0ce32ae47f6fba6b8daadaeb8 [file] [log] [blame]
// 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/packet_queue.h"
#include <trace/event.h>
#include "src/lib/syslog/cpp/logger.h"
#include "src/media/audio/audio_core/audio_object.h"
#include "src/media/audio/audio_core/format.h"
namespace media::audio {
PacketQueue::PacketQueue(Format format) : PacketQueue(format, nullptr) {}
PacketQueue::PacketQueue(Format format, fbl::RefPtr<VersionedTimelineFunction> timeline_function)
: Stream(std::move(format)), timeline_function_(std::move(timeline_function)) {}
PacketQueue::~PacketQueue() {
pending_flush_packet_queue_.clear();
pending_packet_queue_.clear();
pending_flush_token_queue_.clear();
}
void PacketQueue::PushPacket(const fbl::RefPtr<Packet>& packet) {
TRACE_DURATION("audio", "PacketQueue::PushPacket");
std::lock_guard<std::mutex> locker(pending_mutex_);
pending_packet_queue_.emplace_back(std::move(packet));
}
void PacketQueue::Flush(const fbl::RefPtr<PendingFlushToken>& flush_token) {
TRACE_DURATION("audio", "PacketQueue::Flush");
std::deque<fbl::RefPtr<Packet>> 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.
FX_DCHECK(pending_flush_packet_queue_.empty());
FX_DCHECK(pending_flush_token_queue_.empty());
flushed_packets.swap(pending_packet_queue_);
}
}
// Release the packets, front to back.
for (auto& ptr : flushed_packets) {
ptr = nullptr;
}
}
std::optional<Stream::Buffer> PacketQueue::LockBuffer() {
TRACE_DURATION("audio", "PacketQueue::LockBuffer");
std::lock_guard<std::mutex> locker(pending_mutex_);
FX_DCHECK(!processing_in_progress_);
processing_in_progress_ = true;
if (pending_packet_queue_.size()) {
auto packet = pending_packet_queue_.front();
bool is_continuous = !flushed_;
flushed_ = false;
return {Stream::Buffer(packet->start(), packet->length(), packet->payload(), is_continuous)};
} else {
return std::nullopt;
}
}
void PacketQueue::UnlockBuffer(bool release_buffer) {
TRACE_DURATION("audio", "PacketQueue::UnlockBuffer");
{
std::lock_guard<std::mutex> locker(pending_mutex_);
FX_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 = nullptr;
}
for (auto& ptr : pending_flush_token_queue_) {
ptr = nullptr;
}
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 user either got no packet when they locked the queue (because queue was empty),
// or that they got the front of the queue and that front of the queue has not changed.
FX_DCHECK(!release_buffer || !pending_packet_queue_.empty());
if (release_buffer) {
pending_packet_queue_.pop_front();
}
}
}
std::pair<TimelineFunction, uint32_t> PacketQueue::ReferenceClockToFractionalFrames() const {
if (!timeline_function_) {
return std::make_pair(TimelineFunction(), kInvalidGenerationId);
}
return timeline_function_->get();
}
} // namespace media::audio