blob: e18e3069146876ea67cb665c161e1ac3083833b4 [file] [log] [blame]
// Copyright 2018 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 "garnet/bin/mediaplayer/fidl/buffer_set.h"
#include "garnet/bin/mediaplayer/graph/formatting.h"
namespace media_player {
// static
fbl::RefPtr<BufferSet> BufferSet::Create(
const fuchsia::media::StreamBufferSettings& settings,
uint64_t buffer_lifetime_ordinal, bool single_vmo) {
return fbl::MakeRefCounted<BufferSet>(settings, buffer_lifetime_ordinal,
single_vmo);
}
BufferSet::BufferSet(
const fuchsia::media::StreamBufferSettings& settings,
uint64_t buffer_lifetime_ordinal, bool single_vmo)
: settings_(settings),
single_vmo_(single_vmo),
buffers_(settings_.packet_count_for_server +
settings_.packet_count_for_client) {
free_buffer_count_ = buffers_.size();
settings_.buffer_lifetime_ordinal = buffer_lifetime_ordinal;
}
BufferSet::~BufferSet() {
// Release all the |PayloadBuffers| before |buffers_| is deleted.
ReleaseAllDecoderOwnedBuffers();
}
fuchsia::media::StreamBuffer BufferSet::GetBufferDescriptor(
uint32_t buffer_index, bool writeable,
const PayloadVmos& payload_vmos) const {
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(buffer_index < buffers_.size());
fuchsia::media::StreamBuffer buffer;
buffer.buffer_lifetime_ordinal = settings_.buffer_lifetime_ordinal;
buffer.buffer_index = buffer_index;
fbl::RefPtr<PayloadVmo> payload_vmo = BufferVmo(buffer_index, payload_vmos);
FXL_DCHECK(payload_vmo);
fuchsia::media::StreamBufferDataVmo buffer_data_vmo;
buffer_data_vmo.vmo_handle = payload_vmo->Duplicate(
ZX_RIGHT_READ | ZX_RIGHT_MAP | ZX_RIGHT_TRANSFER | ZX_RIGHT_DUPLICATE |
(writeable ? ZX_RIGHT_WRITE : 0));
buffer_data_vmo.vmo_usable_start =
single_vmo_ ? (buffer_index * settings_.per_packet_buffer_bytes) : 0;
buffer_data_vmo.vmo_usable_size = settings_.per_packet_buffer_bytes;
buffer.data.set_vmo(std::move(buffer_data_vmo));
return buffer;
}
fbl::RefPtr<PayloadBuffer> BufferSet::AllocateBuffer(
uint64_t size, const PayloadVmos& payload_vmos) {
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(size <= settings_.per_packet_buffer_bytes);
FXL_DCHECK(free_buffer_count_ != 0);
FXL_DCHECK(suggest_next_to_allocate_ < buffers_.size());
std::vector<fbl::RefPtr<PayloadVmo>> vmos = payload_vmos.GetVmos();
FXL_DCHECK(single_vmo_ ? (vmos.size() == 1)
: (vmos.size() == buffers_.size()));
uint32_t index = suggest_next_to_allocate_;
while (!buffers_[index].free_) {
index = (index + 1) % buffers_.size();
if (index == suggest_next_to_allocate_) {
FXL_LOG(WARNING) << "AllocateBuffer: ran out of buffers";
return nullptr;
}
}
FXL_DCHECK(buffers_[index].decoder_ref_ == nullptr);
FXL_DCHECK(buffers_[index].free_);
buffers_[index].free_ = false;
suggest_next_to_allocate_ = (index + 1) % buffers_.size();
return CreateBuffer(index, vmos);
}
void BufferSet::CreateBufferForDecoder(uint32_t buffer_index,
const PayloadVmos& payload_vmos) {
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(buffer_index < buffers_.size());
FXL_DCHECK(buffers_[buffer_index].free_);
FXL_DCHECK(!buffers_[buffer_index].decoder_ref_);
buffers_[buffer_index].free_ = false;
buffers_[buffer_index].decoder_ref_ =
CreateBuffer(buffer_index, payload_vmos.GetVmos());
}
void BufferSet::AddRefBufferForDecoder(
uint32_t buffer_index, fbl::RefPtr<PayloadBuffer> payload_buffer) {
FXL_DCHECK(payload_buffer);
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(buffer_index < buffers_.size());
FXL_DCHECK(!buffers_[buffer_index].free_);
FXL_DCHECK(!buffers_[buffer_index].decoder_ref_);
buffers_[buffer_index].decoder_ref_ = payload_buffer;
}
fbl::RefPtr<PayloadBuffer> BufferSet::TakeBufferFromDecoder(
uint32_t buffer_index) {
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(buffer_index < buffers_.size());
FXL_DCHECK(!buffers_[buffer_index].free_);
FXL_DCHECK(buffers_[buffer_index].decoder_ref_);
auto result = buffers_[buffer_index].decoder_ref_;
buffers_[buffer_index].decoder_ref_ = nullptr;
return result;
}
fbl::RefPtr<PayloadBuffer> BufferSet::GetDecoderOwnedBuffer(
uint32_t buffer_index) {
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(buffer_index < buffers_.size());
// Buffer must already be owned by the decoder.
FXL_DCHECK(!buffers_[buffer_index].free_);
FXL_DCHECK(buffers_[buffer_index].decoder_ref_);
return buffers_[buffer_index].decoder_ref_;
}
void BufferSet::AllocateAllBuffersForDecoder(const PayloadVmos& payload_vmos) {
std::lock_guard<std::mutex> locker(mutex_);
for (uint32_t index = 0; index < buffers_.size(); ++index) {
FXL_DCHECK(buffers_[index].free_);
FXL_DCHECK(!buffers_[index].decoder_ref_);
buffers_[index].free_ = false;
buffers_[index].decoder_ref_ = CreateBuffer(index, payload_vmos.GetVmos());
}
free_buffer_count_ = 0;
}
void BufferSet::ReleaseAllDecoderOwnedBuffers() {
std::vector<fbl::RefPtr<PayloadBuffer>> buffers_to_release_;
{
std::lock_guard<std::mutex> locker(mutex_);
for (uint32_t index = 0; index < buffers_.size(); ++index) {
if (buffers_[index].decoder_ref_) {
buffers_to_release_.push_back(buffers_[index].decoder_ref_);
buffers_[index].decoder_ref_ = nullptr;
}
}
}
// Buffers get released here (with the lock not taken) when
// |buffers_to_release_| goes out of scope.
}
bool BufferSet::HasFreeBuffer(fit::closure callback) {
std::lock_guard<std::mutex> locker(mutex_);
if (free_buffer_count_ != 0) {
return true;
}
free_buffer_callback_ = std::move(callback);
return false;
}
void BufferSet::Decommission() {
// This was probably taken care of by the decoder, but let's make sure. Any
// decoder-owned buffers left behind will cause this |BufferSet| to leak.
ReleaseAllDecoderOwnedBuffers();
std::lock_guard<std::mutex> locker(mutex_);
free_buffer_callback_ = nullptr;
}
fbl::RefPtr<PayloadVmo> BufferSet::BufferVmo(
size_t buffer_index, const PayloadVmos& payload_vmos) const {
FXL_DCHECK(buffer_index < buffers_.size());
const std::vector<fbl::RefPtr<PayloadVmo>>& vmos = payload_vmos.GetVmos();
if (single_vmo_) {
FXL_DCHECK(vmos.size() == 1);
return vmos[0];
} else {
FXL_DCHECK(vmos.size() >= buffers_.size());
return vmos[buffer_index];
}
}
fbl::RefPtr<PayloadBuffer> BufferSet::CreateBuffer(
uint32_t buffer_index,
const std::vector<fbl::RefPtr<PayloadVmo>>& payload_vmos) {
fbl::RefPtr<PayloadVmo> payload_vmo =
(single_vmo_ ? payload_vmos[0] : payload_vmos[buffer_index]);
uint64_t offset_in_vmo =
single_vmo_ ? buffer_index * settings_.per_packet_buffer_bytes : 0;
// The recycler used here captures an |fbl::RefPtr| to |this| in case this
// buffer set is no longer current when the buffer is recycled.
fbl::RefPtr<PayloadBuffer> payload_buffer = PayloadBuffer::Create(
settings_.per_packet_buffer_bytes, payload_vmo->at_offset(offset_in_vmo),
payload_vmo, offset_in_vmo,
[this, buffer_index,
this_ref = fbl::WrapRefPtr(this)](PayloadBuffer* payload_buffer) {
fit::closure free_buffer_callback;
{
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(buffer_index < buffers_.size());
FXL_DCHECK(!buffers_[buffer_index].free_);
FXL_DCHECK(!buffers_[buffer_index].decoder_ref_);
buffers_[buffer_index].free_ = true;
++free_buffer_count_;
free_buffer_callback = std::move(free_buffer_callback_);
}
if (free_buffer_callback) {
free_buffer_callback();
}
});
payload_buffer->SetId(buffer_index);
payload_buffer->SetBufferConfig(settings_.buffer_lifetime_ordinal);
--free_buffer_count_;
return payload_buffer;
}
void BufferSetManager::ApplyConstraints(
const fuchsia::media::StreamBufferConstraints& constraints,
bool prefer_single_vmo) {
FXL_DCHECK_CREATION_THREAD_IS_CURRENT(thread_checker_);
uint64_t lifetime_ordinal = 1;
if (current_set_) {
lifetime_ordinal = current_set_->lifetime_ordinal() + 2;
current_set_->Decommission();
}
current_set_ = BufferSet::Create(
constraints.default_settings, lifetime_ordinal,
prefer_single_vmo && constraints.single_buffer_mode_allowed);
}
void BufferSetManager::ReleaseBufferForDecoder(uint64_t lifetime_ordinal,
uint32_t index) {
FXL_DCHECK_CREATION_THREAD_IS_CURRENT(thread_checker_);
if (current_set_ && lifetime_ordinal == current_set_->lifetime_ordinal()) {
// Release the buffer from the current set.
current_set_->TakeBufferFromDecoder(index);
return;
}
// The buffer is from an old set and has already been released for the
// decoder.
}
} // namespace media_player