blob: c931cceabd0d117ecba0277ff7c6d9364f967f5a [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 "src/media/playback/mediaplayer/fidl/buffer_set.h"
#include "src/media/playback/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) {
if (!settings.has_buffer_constraints_version_ordinal()) {
FX_LOGS(ERROR) << "Settings missing buffer_constraints_version_ordinal.";
return nullptr;
}
if (!settings.has_single_buffer_mode()) {
FX_LOGS(ERROR) << "Settings missing single_buffer_mode.";
return nullptr;
}
if (!settings.has_packet_count_for_client()) {
FX_LOGS(ERROR) << "Settings missing packet_count_for_client.";
return nullptr;
}
if (!settings.has_packet_count_for_server()) {
FX_LOGS(ERROR) << "Settings missing packet_count_for_server.";
return nullptr;
}
if (!settings.has_per_packet_buffer_bytes()) {
FX_LOGS(ERROR) << "Settings missing per_packet_buffer_bytes.";
return nullptr;
}
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)
: lifetime_ordinal_(buffer_lifetime_ordinal),
single_vmo_(single_vmo),
// The existence of these values in |settings| was checked in |BufferSet::Create|.
buffer_constraints_version_ordinal_(settings.buffer_constraints_version_ordinal()),
single_buffer_mode_(settings.single_buffer_mode()),
packet_count_for_server_(settings.packet_count_for_server()),
packet_count_for_client_(settings.packet_count_for_client()),
buffer_size_(settings.per_packet_buffer_bytes()) {}
BufferSet::~BufferSet() {
// Release all the |PayloadBuffers| before |buffers_| is deleted.
ReleaseAllProcessorOwnedBuffers();
}
void BufferSet::SetBufferCount(uint32_t buffer_count) {
FX_DCHECK(buffer_count > 0);
std::lock_guard<std::mutex> locker(mutex_);
buffers_.resize(buffer_count);
free_buffer_count_ = buffer_count;
}
fuchsia::media::StreamBufferPartialSettings BufferSet::PartialSettings(
fuchsia::sysmem::BufferCollectionTokenPtr token) const {
FX_DCHECK(token);
std::lock_guard<std::mutex> locker(mutex_);
fuchsia::media::StreamBufferPartialSettings partial_settings;
partial_settings.set_buffer_lifetime_ordinal(lifetime_ordinal_);
partial_settings.set_buffer_constraints_version_ordinal(buffer_constraints_version_ordinal_);
partial_settings.set_single_buffer_mode(single_buffer_mode_);
partial_settings.set_packet_count_for_server(packet_count_for_server_);
partial_settings.set_packet_count_for_client(packet_count_for_client_);
partial_settings.set_sysmem_token(std::move(token));
return partial_settings;
}
fbl::RefPtr<PayloadBuffer> BufferSet::AllocateBuffer(uint64_t size,
const PayloadVmos& payload_vmos) {
std::lock_guard<std::mutex> locker(mutex_);
FX_DCHECK(!buffers_.empty());
FX_DCHECK(size <= buffer_size_);
FX_DCHECK(free_buffer_count_ != 0);
FX_DCHECK(suggest_next_to_allocate_ < buffers_.size());
std::vector<fbl::RefPtr<PayloadVmo>> vmos = payload_vmos.GetVmos();
FX_DCHECK(vmos.size() == buffers_.size());
FX_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_) {
FX_LOGS(WARNING) << "AllocateBuffer: ran out of buffers";
return nullptr;
}
}
FX_DCHECK(buffers_[index].processor_ref_ == nullptr);
FX_DCHECK(buffers_[index].free_);
buffers_[index].free_ = false;
suggest_next_to_allocate_ = (index + 1) % buffers_.size();
return CreateBuffer(index, vmos);
}
void BufferSet::AddRefBufferForProcessor(uint32_t buffer_index,
fbl::RefPtr<PayloadBuffer> payload_buffer) {
FX_DCHECK(payload_buffer);
std::lock_guard<std::mutex> locker(mutex_);
FX_DCHECK(buffer_index < buffers_.size());
FX_DCHECK(!buffers_[buffer_index].free_);
FX_DCHECK(!buffers_[buffer_index].processor_ref_);
buffers_[buffer_index].processor_ref_ = payload_buffer;
}
fbl::RefPtr<PayloadBuffer> BufferSet::TakeBufferFromProcessor(uint32_t buffer_index) {
std::lock_guard<std::mutex> locker(mutex_);
FX_DCHECK(buffer_index < buffers_.size());
FX_DCHECK(!buffers_[buffer_index].free_);
FX_DCHECK(buffers_[buffer_index].processor_ref_);
auto result = buffers_[buffer_index].processor_ref_;
buffers_[buffer_index].processor_ref_ = nullptr;
return result;
}
fbl::RefPtr<PayloadBuffer> BufferSet::GetProcessorOwnedBuffer(uint32_t buffer_index) {
std::lock_guard<std::mutex> locker(mutex_);
FX_DCHECK(buffer_index < buffers_.size());
// Buffer must already be owned by the processor.
FX_DCHECK(!buffers_[buffer_index].free_);
FX_DCHECK(buffers_[buffer_index].processor_ref_);
return buffers_[buffer_index].processor_ref_;
}
void BufferSet::AllocateAllBuffersForProcessor(const PayloadVmos& payload_vmos) {
std::lock_guard<std::mutex> locker(mutex_);
FX_DCHECK(!buffers_.empty());
std::vector<fbl::RefPtr<PayloadVmo>> vmos = payload_vmos.GetVmos();
FX_DCHECK(vmos.size() == buffers_.size());
for (size_t index = 0; index < buffers_.size(); ++index) {
FX_DCHECK(buffers_[index].free_);
FX_DCHECK(!buffers_[index].processor_ref_);
buffers_[index].free_ = false;
buffers_[index].processor_ref_ = CreateBuffer(index, vmos);
}
free_buffer_count_ = 0;
}
void BufferSet::ReleaseAllProcessorOwnedBuffers() {
std::vector<fbl::RefPtr<PayloadBuffer>> buffers_to_release_;
{
std::lock_guard<std::mutex> locker(mutex_);
for (size_t index = 0; index < buffers_.size(); ++index) {
if (buffers_[index].processor_ref_) {
buffers_to_release_.push_back(buffers_[index].processor_ref_);
buffers_[index].processor_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 processor, but let's make sure. Any
// processor-owned buffers left behind will cause this |BufferSet| to leak.
ReleaseAllProcessorOwnedBuffers();
std::lock_guard<std::mutex> locker(mutex_);
free_buffer_callback_ = nullptr;
}
fbl::RefPtr<PayloadBuffer> BufferSet::CreateBuffer(
uint32_t buffer_index, const std::vector<fbl::RefPtr<PayloadVmo>>& payload_vmos) {
FX_DCHECK(single_vmo_ ? (payload_vmos.size() == 1) : (buffer_index < payload_vmos.size()));
fbl::RefPtr<PayloadVmo> payload_vmo =
(single_vmo_ ? payload_vmos[0] : payload_vmos[buffer_index]);
uint64_t offset_in_vmo = single_vmo_ ? buffer_index * buffer_size_ : 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(
buffer_size_, payload_vmo->at_offset(offset_in_vmo), payload_vmo, offset_in_vmo,
[this, buffer_index, this_ref = fbl::RefPtr(this)](PayloadBuffer* payload_buffer) {
fit::closure free_buffer_callback;
{
std::lock_guard<std::mutex> locker(mutex_);
FX_DCHECK(buffer_index < buffers_.size());
FX_DCHECK(!buffers_[buffer_index].free_);
FX_DCHECK(!buffers_[buffer_index].processor_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(lifetime_ordinal_);
--free_buffer_count_;
return payload_buffer;
}
bool BufferSetManager::ApplyConstraints(const fuchsia::media::StreamBufferConstraints& constraints,
bool prefer_single_vmo) {
FIT_DCHECK_IS_THREAD_VALID(thread_checker_);
if (!constraints.has_default_settings()) {
FX_LOGS(ERROR) << "FIDL buffer constraints do not have default settings.";
return false;
}
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());
if (current_set_ == nullptr) {
FX_LOGS(ERROR) << "Could not create bufferset from FIDL buffer settings.";
return false;
}
return true;
}
void BufferSetManager::ReleaseBufferForProcessor(uint64_t lifetime_ordinal, uint32_t index) {
FIT_DCHECK_IS_THREAD_VALID(thread_checker_);
if (current_set_ && lifetime_ordinal == current_set_->lifetime_ordinal()) {
// Release the buffer from the current set.
current_set_->TakeBufferFromProcessor(index);
return;
}
// The buffer is from an old set and has already been released for the
// processor.
}
} // namespace media_player