blob: 1816c0994af08ef565d680cdd14d38d373f9884b [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 SRC_MEDIA_PLAYBACK_MEDIAPLAYER_GRAPH_PAYLOADS_PAYLOAD_MANAGER_H_
#define SRC_MEDIA_PLAYBACK_MEDIAPLAYER_GRAPH_PAYLOADS_PAYLOAD_MANAGER_H_
#include <mutex>
#include "src/lib/fxl/synchronization/thread_annotations.h"
#include "src/media/playback/mediaplayer/graph/payloads/local_memory_payload_allocator.h"
#include "src/media/playback/mediaplayer/graph/payloads/payload_allocator.h"
#include "src/media/playback/mediaplayer/graph/payloads/payload_config.h"
#include "src/media/playback/mediaplayer/graph/payloads/vmo_payload_allocator.h"
namespace media_player {
// DESIGN
//
// |PayloadManager| manages payload allocation for a connection. Its
// responsibilities are:
//
// 1) Assemble the right configuration of allocators based on the
// |PayloadConfig|s from the output and input.
// 2) Initialize the allocators prior to use by the output and input.
// 3) Expose the right capabilities to the output and input.
// 4) Arrange for payload copying when needed.
//
// |PayloadConfig| is described in detail in payload_config.h.
//
// The term 'connector' is used to refer to either the output or input.
//
// The allocator configuration may include zero, one or two allocators, and
// and there are two kinds of allocators, |LocalMemoryPayloadAllocator| and
// |VmoPayloadAllocator|. We associate a particular allocator with either the
// output or the input, though in some cases, both parties can access the same
// VMO allocator. In such cases, the allocator is associated with:
// 1) the connector supplying VMOs to the allocator, if there is one, otherwise
// 2) the connector that needs VMO access, if only one does, otherwise
// 3) the input.
// Associating the allocator with the input in the last case is arbitrary, in
// some respects, but it simplifies the code that deals with the input's
// requirement to perform allocations itself. See the |allocate_callback|
// parameter of |ApplyInputConfiguration|.
//
// When copying is performed, payloads produced by the output are copied to
// memory allocated from the input's allocator.
//
// In most cases, the correct allocator configuration can be established when
// both the output and the input have supplied their |PayloadConfig|s. There
// are other cases in which incompatibility is detected when VMOs are provided
// by the input or output, in which case the allocator configuration must be
// changed to have two allocators.
// Manages payload allocation for a connection, selecting and implementing the
// correct allocation strategy based on the constraints expressed by the
// output and input.
//
// |PayloadManager| is thread-safe. All of its methods may be called on any
// thread.
class PayloadManager {
public:
// Function type used by clients who want to implement buffer allocation
// themselves.
//
// uint64_t size - size in bytes of the buffer
// const PayloadVmos& vmos - the VMO collection from which to allocate
// (result) - A |PayloadBuffer| whose size is >= to the
// requested size, or nullptr if the allocation
// failed.
//
// The allocator callback is called on an arbitrary thread.
//
// The supplied VMOs are the same ones available on the node via
// |Node::UseOutputVmos| or |Node::UseInputVmos|. They're passed to the
// callback, because the callback may not call back into the node.
using AllocateCallback =
fit::function<fbl::RefPtr<PayloadBuffer>(uint64_t, const PayloadVmos&)>;
// Dumps this |PayloadManager|'s state to |os|.
void Dump(std::ostream& os) const;
// Applies the output configuration supplied in |config|. |bti_handle| must
// be provided if and only if |config.physically_contiguous| is true.
void ApplyOutputConfiguration(const PayloadConfig& config,
zx::handle bti_handle);
// Applies the input configuration supplied in |config|.
//
// |allocate_callback| may be supplied for VMO modes only. It allows the node
// to perform the actual allocations against the VMOs. The allocator callback
// will never be asked to allocate from VMOs provided by the output.
// |allocate_callback| is called on an arbitrary thread, and may not reenter
// this |PayloadManager|.
//
// |bti_handle| must be provided if and only if |config.physically_contiguous|
// is true.
void ApplyInputConfiguration(const PayloadConfig& config,
zx::handle bti_handle,
AllocateCallback allocate_callback);
// Indicates whether the connection manager is ready for allocator access.
bool ready() const;
// Allocates and returns a |PayloadBuffer| for the output with the specified
// size. Returns nullptr if the allocation fails.
fbl::RefPtr<PayloadBuffer> AllocatePayloadBufferForOutput(
uint64_t size) const;
// Gets the |PayloadVmos| interface for the input. This method should only be
// called if this |PayloadManager| is ready and the input mode is |kUsesVmos|
// or |kProvidesVmos|.
PayloadVmos& input_vmos() const;
// Gets the |PayloadVmoProvision| interface for the input. This method should
// only be called if this |PayloadManager| is ready and the input mode is
// |kProvidesVmos|.
PayloadVmoProvision& input_external_vmos() const;
// Gets the |PayloadVmos| interface for the output. This method should only be
// called if this |PayloadManager| is ready and the output mode is |kUsesVmos|
// or |kProvidesVmos|.
PayloadVmos& output_vmos() const;
// Gets the |PayloadVmoProvision| interface for the output. This method should
// only be called if this |PayloadManager| is ready and the output mode is
// |kProvidesVmos|.
PayloadVmoProvision& output_external_vmos() const;
// Indicates whether copying is required and maybe provides a copy destination
// payload buffer. This method returns true if and only if copying is required
// for this connection. If copying is required and |size| is non-zero, this
// method will attempt to allocate a payload buffer into which |size| bytes
// of payload may be copied. If this method returns true, |size| is non-zero
// and |*payload_buffer_out| is nullptr after the method returns, this
// indicates that payload memory for this purpose is exhausted.
//
// |payload_buffer_out| may only be nullptr if |size| is also zero.
bool MaybeAllocatePayloadBufferForCopy(
uint64_t size, fbl::RefPtr<PayloadBuffer>* payload_buffer_out) const;
// Signals that the output and input are disconnected.
void OnDisconnect();
private:
// State relating to output or input.
struct Connector {
PayloadConfig config_;
zx::handle bti_handle_;
fbl::RefPtr<LocalMemoryPayloadAllocator> local_memory_allocator_;
fbl::RefPtr<VmoPayloadAllocator> vmo_allocator_;
// Ensure that this |Connector| has no allocators.
void EnsureNoAllocator();
// Ensure that this |Connector| has only a local memory allocator.
void EnsureLocalMemoryAllocator();
// Ensure that this |Connector| has only a VMO allocator. Returns a raw
// pointer to the VMO allocator.
VmoPayloadAllocator* EnsureVmoAllocator();
// Return a |PayloadAllocator| implemented by this connector, if there is
// one, nullptr otherwise.
PayloadAllocator* payload_allocator() const;
};
void DumpInternal(std::ostream& os) const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Indicates whether the connection manager is ready for allocator access.
bool ready_locked() const FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Updates the allocators based in the current configs.
void UpdateAllocators() FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Determines whether the output and input configuration are compatible.
// The |mode_| values are not examined and are assumed to be compatible.
// When |kProvidesVmos| mode is used, incompatibility may not be detected
// until VMOs are supplied.
bool ConfigsAreCompatible() const FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Determines whether the output and input configuration modes are compatible.
// This method is only used for a DCHECK in |ConfigsAreCompatible|.
bool ConfigModesAreCompatible() const FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns a |VmoAllocation| value that satisfies both output and input,
// either |kSingleVmo| or |kVmoPerBuffer|. The output and input must have
// compatible |config_.vmo_allocation_| values.
VmoAllocation CombinedVmoAllocation() const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Creates VMOs for an allocator shared by the input and output and adds them
// to |allocator|. The VMOs created will satisfy the requirements of both the
// output and the input.
void ProvideVmosForSharedAllocator(VmoPayloadAllocator* allocator) const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Creates VMOs and adds them to |allocator|. The VMOs created will satisfy
// the specified configuration.
//
// This method is used in two cases:
// 1) The allocator is associated with only the output or the input, in which
// case |config| is the configuration for that output or input.
// 2) When an allocator is shared between the output and input, in which case
// |config| is the merged configuration of the output and the input.
// |ProvideVmosForSharedAllocator| merges the configurations and calls
// this method.
//
// The larger of |max_payload_size| and |config.max_payload_size_| will be
// used. When providing VMOs for an input, |max_payload_size| should be the
// max payload size from the output's config. Otherwise, it should be zero.
// |bti_handle| is provided to indicate that the VMOs must be physically
// contiguous.
void ProvideVmos(VmoPayloadAllocator* allocator, const PayloadConfig& config,
uint64_t max_payload_size,
const zx::handle* bti_handle) const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Prepares |allocator| for external VMOs by settings its |VmoAllocation|
// setting based on |config|. This method is used when |allocator| is
// associated with only the output or the input (not both). |config| is the
// configuration for that output or input.
void PrepareForExternalVmos(VmoPayloadAllocator* allocator,
const PayloadConfig& config) const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Prepares |allocator| for external VMOs by settings its |VmoAllocation|
// setting based on the requirements of both the output and the input. This
// method is used when |allocator| is shared by the output and input.
void PrepareSharedAllocatorForExternalVmos(VmoPayloadAllocator* allocator)
const FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Allocates and returns a |PayloadBuffer| using the allocator callback.
// Returns nullptr if the allocation fails.
fbl::RefPtr<PayloadBuffer> AllocateUsingAllocateCallback(uint64_t size) const
FXL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable std::mutex mutex_;
Connector output_ FXL_GUARDED_BY(mutex_);
Connector input_ FXL_GUARDED_BY(mutex_);
// Optionally provided by the input to perform allocations against the input
// VMOS.
AllocateCallback allocate_callback_;
// Indicates whether copying must occur. If this field is true, the input
// will have an allocator.
bool copy_ FXL_GUARDED_BY(mutex_) = false;
};
} // namespace media_player
#endif // SRC_MEDIA_PLAYBACK_MEDIAPLAYER_GRAPH_PAYLOADS_PAYLOAD_MANAGER_H_