blob: 2367a8d3f91580aad4f6d862a36e2ec2cf985db3 [file] [log] [blame]
// Copyright 2021 The Chromium 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 "media/fuchsia/common/vmo_buffer_writer_queue.h"
#include <zircon/rights.h>
#include <algorithm>
#include "base/bits.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/process/process_metrics.h"
#include "media/base/decoder_buffer.h"
namespace media {
struct VmoBufferWriterQueue::PendingBuffer {
PendingBuffer(scoped_refptr<DecoderBuffer> buffer) : buffer(buffer) {
DCHECK(buffer);
}
~PendingBuffer() = default;
PendingBuffer(PendingBuffer&& other) = default;
PendingBuffer& operator=(PendingBuffer&& other) = default;
const uint8_t* data() const { return buffer->data() + buffer_pos; }
size_t bytes_left() const { return buffer->data_size() - buffer_pos; }
void AdvanceCurrentPos(size_t bytes) {
DCHECK_LE(bytes, bytes_left());
buffer_pos += bytes;
}
scoped_refptr<DecoderBuffer> buffer;
size_t buffer_pos = 0;
// Set to true when the consumer has finished processing the buffer and it can
// be released.
bool is_complete = false;
// Index of the last buffer in the sysmem buffer collection that was used to
// send this input buffer. Should be set only when |bytes_left()==0|.
absl::optional<size_t> tail_sysmem_buffer_index;
};
VmoBufferWriterQueue::VmoBufferWriterQueue() {
DETACH_FROM_THREAD(thread_checker_);
}
VmoBufferWriterQueue::~VmoBufferWriterQueue() = default;
void VmoBufferWriterQueue::EnqueueBuffer(scoped_refptr<DecoderBuffer> buffer) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
pending_buffers_.push_back(PendingBuffer(buffer));
PumpPackets();
}
void VmoBufferWriterQueue::Start(std::vector<VmoBuffer> buffers,
SendPacketCB send_packet_cb,
EndOfStreamCB end_of_stream_cb) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(buffers_.empty());
DCHECK(!buffers.empty());
buffers_ = std::move(buffers);
send_packet_cb_ = std::move(send_packet_cb);
end_of_stream_cb_ = std::move(end_of_stream_cb);
// Initialize |unused_buffers_|.
unused_buffers_.reserve(buffers_.size());
for (size_t i = 0; i < buffers_.size(); ++i) {
unused_buffers_.push_back(i);
}
PumpPackets();
}
bool VmoBufferWriterQueue::IsBlocked() const {
return unused_buffers_.empty();
}
void VmoBufferWriterQueue::PumpPackets() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto weak_this = weak_factory_.GetWeakPtr();
while (!buffers_.empty() && !is_paused_ &&
input_queue_position_ < pending_buffers_.size()) {
PendingBuffer* current_buffer = &pending_buffers_[input_queue_position_];
if (current_buffer->buffer->end_of_stream()) {
pending_buffers_.pop_front();
end_of_stream_cb_.Run();
if (!weak_this)
return;
continue;
}
if (unused_buffers_.empty()) {
// No input buffer available.
return;
}
size_t buffer_index = unused_buffers_.back();
unused_buffers_.pop_back();
size_t bytes_filled = buffers_[buffer_index].Write(
base::make_span(current_buffer->data(), current_buffer->bytes_left()));
current_buffer->AdvanceCurrentPos(bytes_filled);
bool buffer_end = current_buffer->bytes_left() == 0;
auto packet = StreamProcessorHelper::IoPacket(
buffer_index, /*offset=*/0, bytes_filled,
current_buffer->buffer->timestamp(), buffer_end,
base::BindOnce(&VmoBufferWriterQueue::ReleaseBuffer,
weak_factory_.GetWeakPtr(), buffer_index));
if (buffer_end) {
current_buffer->tail_sysmem_buffer_index = buffer_index;
input_queue_position_ += 1;
}
send_packet_cb_.Run(current_buffer->buffer.get(), std::move(packet));
if (!weak_this)
return;
}
}
void VmoBufferWriterQueue::ResetQueue() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
pending_buffers_.clear();
input_queue_position_ = 0;
is_paused_ = false;
}
void VmoBufferWriterQueue::ResetBuffers() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
buffers_.clear();
send_packet_cb_ = SendPacketCB();
end_of_stream_cb_ = EndOfStreamCB();
// Invalidate weak pointers, so ReleaseBuffer() is not called for the old
// buffers.
weak_factory_.InvalidateWeakPtrs();
}
void VmoBufferWriterQueue::ResetPositionAndPause() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto& buffer : pending_buffers_) {
buffer.buffer_pos = 0;
buffer.is_complete = false;
// All packets that were pending will need to be resent. Reset
// |tail_sysmem_buffer_index| to ensure that these packets are not removed
// from the queue in ReleaseBuffer().
buffer.tail_sysmem_buffer_index = absl::nullopt;
}
input_queue_position_ = 0;
is_paused_ = true;
}
void VmoBufferWriterQueue::Unpause() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(is_paused_);
is_paused_ = false;
PumpPackets();
}
void VmoBufferWriterQueue::ReleaseBuffer(size_t buffer_index) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!buffers_.empty());
// Mark the input buffer as complete.
for (size_t i = 0; i < input_queue_position_; ++i) {
if (pending_buffers_[i].tail_sysmem_buffer_index == buffer_index)
pending_buffers_[i].is_complete = true;
}
// Remove all complete buffers from the head of the queue since we no longer
// need them. Note that currently StreamProcessor doesn't guarantee that input
// buffers are released in the same order they were sent (see
// https://fuchsia.googlesource.com/fuchsia/+/3b12c8c5/sdk/fidl/fuchsia.media/stream_processor.fidl#1646
// ). This means that some complete buffers will need to stay in the queue
// until all preceding packets are released as well.
while (!pending_buffers_.empty() && pending_buffers_.front().is_complete) {
pending_buffers_.pop_front();
DCHECK_GT(input_queue_position_, 0U);
input_queue_position_--;
}
unused_buffers_.push_back(buffer_index);
PumpPackets();
}
size_t VmoBufferWriterQueue::num_buffers() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return buffers_.size();
}
} // namespace media