blob: cf1b049966910d4ba1f3c4c4260c7465446af19e [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.
#ifndef GARNET_BIN_MEDIAPLAYER_FIDL_BUFFER_SET_H_
#define GARNET_BIN_MEDIAPLAYER_FIDL_BUFFER_SET_H_
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fuchsia/mediacodec/cpp/fidl.h>
#include "garnet/bin/mediaplayer/graph/payloads/payload_allocator.h"
#include "garnet/bin/mediaplayer/graph/payloads/payload_buffer.h"
#include "lib/fxl/synchronization/thread_annotations.h"
#include "lib/fxl/synchronization/thread_checker.h"
namespace media_player {
// A set of buffers associated with a specific StreamBufferSettings and
// buffer lifetime ordinal.
//
// This class is thread-safe.
class BufferSet : public fbl::RefCounted<BufferSet> {
public:
// Creates a buffer set with the specified settings and lifetime ordinal.
// |single_vmo| indicates whether the buffers should be allocated from a
// single VMO (true) or a VMO per buffer.
static fbl::RefPtr<BufferSet> Create(
const fuchsia::media::StreamBufferSettings& settings,
uint64_t lifetime_ordinal, bool single_vmo);
BufferSet(const fuchsia::media::StreamBufferSettings& settings,
uint64_t lifetime_ordinal, bool single_vmo);
~BufferSet();
// Gets the settings for this buffer set. The |buffer_lifetime_ordinal| of
// settings is set to the |lifetime_ordinal| value passed into the
// constructor.
const fuchsia::media::StreamBufferSettings& settings() const {
std::lock_guard<std::mutex> locker(mutex_);
return settings_;
}
// Sets the value passed into the constructor as |single_vmo|.
bool single_vmo() const {
std::lock_guard<std::mutex> locker(mutex_);
return single_vmo_;
}
// Returns the buffer lifetime ordinal passed to the constructor.
uint64_t lifetime_ordinal() const {
std::lock_guard<std::mutex> locker(mutex_);
return settings_.buffer_lifetime_ordinal;
}
// Returns the size in bytes of the buffers in this set.
uint32_t buffer_size() const {
std::lock_guard<std::mutex> locker(mutex_);
return settings_.per_packet_buffer_bytes;
}
// Returns the number of buffers in the set.
uint32_t buffer_count() const {
std::lock_guard<std::mutex> locker(mutex_);
return buffers_.size();
}
// Returns the number of free buffers.
uint32_t free_buffer_count() const {
std::lock_guard<std::mutex> locker(mutex_);
return free_buffer_count_;
}
// Returns a |StreamBuffer| struct for the specified buffer. |writeable|
// determines whether the vmo handle in the descriptor should have write
// permission.
fuchsia::media::StreamBuffer GetBufferDescriptor(
uint32_t buffer_index, bool writeable,
const PayloadVmos& payload_vmos) const;
// Allocates a buffer.
fbl::RefPtr<PayloadBuffer> AllocateBuffer(uint64_t size,
const PayloadVmos& payload_vmos);
// Creates a payload buffer on behalf of the outboard decoder and stores
// a reference to it. The reference may be released with
// |ReleaseBufferForDecoder| or |ReleaseAllDecoderOwnedBuffers|.
void CreateBufferForDecoder(uint32_t buffer_index,
const PayloadVmos& payload_vmos);
// Adds a reference to the payload buffer on behalf of the outboard decoder.
// |payload_buffer| cannot be null. This version is used when the client
// has a reference to the |PayloadBuffer| already.
void AddRefBufferForDecoder(uint32_t buffer_index,
fbl::RefPtr<PayloadBuffer> payload_buffer);
// Takes a reference to the payload buffer previously added using
// |AddRefBufferForDecoder| or |AllocateAllBuffersForDecoder| and returns a
// reference to the |PayloadBuffer|.
fbl::RefPtr<PayloadBuffer> TakeBufferFromDecoder(uint32_t buffer_index);
// Gets a new reference to a buffer already owned by the outboard decoder.
fbl::RefPtr<PayloadBuffer> GetDecoderOwnedBuffer(uint32_t buffer_index);
// Allocates all buffers for the outboard decoder. All buffers must be free
// when this method is called.
void AllocateAllBuffersForDecoder(const PayloadVmos& payload_vmos);
// Releases all buffers currently owned by the output decoder.
void ReleaseAllDecoderOwnedBuffers();
// Returns true if there's a free buffer, otherwise calls |callback| on an
// arbirary thread when one becomes free. The pending action can be cancelled
// by calling |CancelPendingFreeBufferCallback|.
bool HasFreeBuffer(fit::closure callback);
// Indicates that this |BufferSet| has been parked in favor of a new one.
// After decommissioning and When all its buffers have been recycled, the
// buffer set will be deleted.
void Decommission();
private:
// The current state of a buffer in the set.
struct BufferInfo {
// Indicates whether the buffer is free. |decoder_ref_| must be false if
// this field is true.
bool free_ = true;
// This field is non-null for buffers that are currently owned by the
// outboard decoder.
fbl::RefPtr<PayloadBuffer> decoder_ref_;
};
// Gets the |PayloadVmo| for the specified index.
fbl::RefPtr<PayloadVmo> BufferVmo(size_t buffer_index,
const PayloadVmos& payload_vmos) const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Creates a |PayloadBuffer| for the indicated |buffer_index|.
fbl::RefPtr<PayloadBuffer> CreateBuffer(
uint32_t buffer_index,
const std::vector<fbl::RefPtr<PayloadVmo>>& payload_vmos)
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable std::mutex mutex_;
fuchsia::media::StreamBufferSettings settings_ FXL_GUARDED_BY(mutex_);
bool single_vmo_ FXL_GUARDED_BY(mutex_);
std::vector<BufferInfo> buffers_ FXL_GUARDED_BY(mutex_);
// |suggest_next_to_allocate_| suggests the next buffer to allocate. When
// allocating a buffer, a sequential search for a free buffer starts at this
// index, and this index is left referring to the buffer after the allocated
// buffer (with wraparound). Given the normally FIFO behavior of the caller,
// only one increment is typically required per allocation. This approach
// tends to allocate the buffers in a round-robin fashion.
uint32_t suggest_next_to_allocate_ FXL_GUARDED_BY(mutex_) = 0;
uint32_t free_buffer_count_ FXL_GUARDED_BY(mutex_) = 0;
fit::closure free_buffer_callback_ FXL_GUARDED_BY(mutex_);
// Disallow copy and assign.
BufferSet(const BufferSet&) = delete;
BufferSet& operator=(const BufferSet&) = delete;
};
// Manages a sequence of buffer sets.
//
// This class is not thread-safe. The constructor, desctructor and all methods
// must be called on the same thread.
class BufferSetManager {
public:
BufferSetManager() = default;
~BufferSetManager() {
FXL_DCHECK_CREATION_THREAD_IS_CURRENT(thread_checker_);
};
// Determines whether this has a current buffer set.
bool has_current_set() const {
FXL_DCHECK_CREATION_THREAD_IS_CURRENT(thread_checker_);
return !!current_set_;
}
// The current buffer set. Do not call this method when |has_current| returns
// false.
BufferSet& current_set() {
FXL_DCHECK_CREATION_THREAD_IS_CURRENT(thread_checker_);
FXL_DCHECK(current_set_);
return *current_set_;
}
// Applies the specified constraints, creating a new buffer set. If
// |single_vmo_preferred| and |single_buffer_mode_allowed| are true, one vmo
// will be used for all the new buffers. Otherwise, each new buffer will have
// its own vmo. The resulting set's |single_vmo| method with return true in
// former case, false in the latter.
void ApplyConstraints(
const fuchsia::media::StreamBufferConstraints& constraints,
bool single_vmo_preferred);
// Releases a reference to the payload buffer previously added using
// |BufferSet::AddRefBufferForDecoder| or
// |BufferSet::AllocateAllBuffersForDecoder|.
void ReleaseBufferForDecoder(uint64_t lifetime_ordinal,
uint32_t buffer_index);
private:
FXL_DECLARE_THREAD_CHECKER(thread_checker_);
// The current |BufferSet| this is only null when |ApplyConstraints| has
// never been called. It's important not to clear this arbitrarily, because
// that would prevent the buffer lifetime ordinals from progressing correctly.
fbl::RefPtr<BufferSet> current_set_;
// Disallow copy and assign.
BufferSetManager(const BufferSetManager&) = delete;
BufferSetManager& operator=(const BufferSetManager&) = delete;
};
} // namespace media_player
#endif // GARNET_BIN_MEDIAPLAYER_FIDL_BUFFER_SET_H_