blob: 48158f235f238f3922b4a4b04f1d1d3c5645bb21 [file] [log] [blame]
// Copyright 2015 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.
#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_CIRCULAR_BUFFER_MEDIA_PIPE_ADAPTER_H_
#define MOJO_SERVICES_MEDIA_COMMON_CPP_CIRCULAR_BUFFER_MEDIA_PIPE_ADAPTER_H_
#include <atomic>
#include <deque>
#include <functional>
#include <limits>
#include "mojo/public/cpp/bindings/callback.h"
#include "mojo/public/cpp/environment/logging.h"
#include "mojo/services/media/common/interfaces/media_common.mojom.h"
#include "mojo/services/media/common/interfaces/media_transport.mojom.h"
namespace mojo {
namespace media {
// A class to help producers of media with the bookkeeping involved in using the
// shared buffer provided by a MediaConsumer mojo interface in a circular buffer
// fashion.
//
class CircularBufferMediaPipeAdapter {
public:
class MappedPacket {
public:
static constexpr size_t kMaxRegions = 2;
MappedPacket();
~MappedPacket();
const MediaPacketPtr& packet() const { return packet_; }
void* data(size_t index) const {
MOJO_DCHECK(index < MOJO_ARRAYSIZE(data_));
return data_[index];
}
uint64_t length(size_t index) const {
MOJO_DCHECK(index < MOJO_ARRAYSIZE(length_));
return length_[index];
}
private:
friend class CircularBufferMediaPipeAdapter;
void Reset() {
packet_.reset();
data_[0] = data_[1] = nullptr;
length_[0] = length_[1] = 0;
cancel_wr_ = 0;
}
MediaPacketPtr packet_;
void* data_[2];
uint64_t length_[2];
uint64_t cancel_wr_;
uint32_t flush_generation_;
};
/**
* A callback definition, registered by users of the adapter. The callback
* will be called any time the adapter is in the signalled state, either
* because there is room for more data in the pipe, or because the pipe has
* entered into a fatal, unrecoverable state.
*
* @param state The current state of the adapter. If the state is anything
* but MediaResult::OK, the pipe is in a fatal, unrecoverable error state.
*/
using SignalCbk = std::function<void(MediaResult state)>;
/**
* Constructor
*
* Create an adapter which will take ownership of the provided MediaConsumer
* interface and assist in the process of generating MediaPackets and
* marshalling them to the other side of the MediaConsumer.
*
* @param pipe A pointer to the MediaConsumer interface which will be used as
* the target for MediaPackets.
*/
explicit CircularBufferMediaPipeAdapter(MediaConsumerPtr pipe);
/**
* Destructor
*/
~CircularBufferMediaPipeAdapter();
/**
* Init
*
* Allocate a shared memory buffer of the specified size and begin the process
* of marshalling it to the other side of the MediaConsumer.
*
* @param size The size in bytes of the shared memory buffer to allocate.
*/
void Init(uint64_t size);
/**
* Set the signal callback for this media pipe adapter. This callback will be
* called when the adapter transitions from un-signalled to signalled and has
* a valid callback, or when a valid callback is assigned (via a call to
* Setllback) and the adapter is currently in the signalled state.
*
* Any callbacks in flight are guaranteed to be canceled or executed to
* completion when SetCallback returns.
*
* It is the adapter's user's responsibility to manage the lifetime of the
* callback and the adapter, and ensure that the callback is valid as long as
* it is assigned to a adapter instance.
*
* @param cbk A reference to the callback to execute when the adapter becomes
* signalled. Pass nullptr to cancel any pending callbacks.
*/
void SetSignalCallback(SignalCbk cbk);
/**
* Clear any existing signal callback. Callbacks will no longer be made, even
* if the adapter is in the signalled state.
*/
void ResetSignalCallback() { SetSignalCallback(nullptr); }
/**
* Set the water marks (in bytes) for determining the signalled/un-signalled
* state of the pipe.
*
* When the amount of data pending consumption in the pipe drops below the low
* water mark (pending < mark), the pipe becomes signalled and any valid
* callback which was previously set will be scheduled on the current run
* queue.
*
* When the amount of data pending consumption in the pipe drops grows beyond
* the high water mark (pending >= mark), the pipe becomes un-signalled no
* further callbacks will be made until the amount of pending data falls below
* the low water mark again.
*
* The adapter becomes un-signalled when the amount of pending data in it
* exceeds the high water mark. It will become signalled when the amount of
* pending data in it is less than or equal to the low water mark.
*
* It is an error to set a hi/lo water mark pair such that lo > hi, and will
* be ignored in non-debug builds.
*
* @param hi_water_mark The new value, in bytes, of the hi water mark to set.
* @param lo_water_mark The new value, in bytes, of the lo water mark to set.
*/
void SetWatermarks(uint64_t hi_water_mark, uint64_t lo_water_mark);
/**
* Attempt to create a media packet, reserving memory in the circular buffer
* in the process. Note, once created, packets must be sent in the same order
* they were created, or canceled in they inverse order they were created.
* Failure to follow these guidelines will result in an internal bookkeeping
* error, and the pipe needing to reset/flush its state in order to recover.
*
* @param [in] size The size, in bytes of the packet to create.
* @param [in] no_wrap When true, guarantees that the created packet will be
* offset in way which guarantees that it does not wrap
* around the internal circular buffer boundary.
* @param [out] packet A structure which, upon success, will contain up to two
* pointer and lengths describing the packet's payload in
* shared memory, as well as a pointer to the packet
* itself.
* @return A media result indicating the success or failure of the operation.
*
* Possible failure codes include...
*
* MediaResult::BAD_STATE :
* The pipe is in a faulted state.
*
* MediaResult::BUSY :
* The pipe is in the middle of a flush operation, or waiting to fetch its
* initial state.
*
* MediaResult::INSUFFICIENT_RESOURCES :
* There is currently not enough space in the buffer to accommodate the
* request.
*/
MediaResult CreateMediaPacket(uint64_t size,
bool no_wrap,
MappedPacket* packet);
/**
* Send a previously created media packet across the pipe to the consumer.
* @param [in] packet The pointer to the structure which describes the packet
* to be sent.
* @return A media result indicating the success or failure of the operation.
*/
MediaResult SendMediaPacket(
MappedPacket* packet,
const MediaConsumer::SendPacketCallback& cbk =
MediaConsumer::SendPacketCallback());
/**
* Cancel a packet previously created using CreateMediaPacket.
* @note The packet canceled must not have been sent, and must be the most
* recent non-canceled packet created using CreateMediaPacket.
* @param [in] packet The pointer to the structure which describes the packet
* to be canceled.
* @return A media result indicating the success or failure of the operation.
*/
MediaResult CancelMediaPacket(MappedPacket* packet);
/**
* Flush the media pipe. Cancels all in flight payloads and resets the
* internal bookkeeping.
*
* @note The media pipe will be un-signalled and unavailable for Create/Send
* operations during the flush.
*/
MediaResult Flush();
uint64_t GetPending() const;
uint64_t GetBufferSize() const;
uint64_t AboveHiWater() const { return GetPending() >= hi_water_mark_; }
uint64_t BelowLoWater() const { return GetPending() < lo_water_mark_; }
private:
struct PacketState {
PacketState(uint64_t post_consume_rd,
uint32_t seq_num,
const MediaConsumer::SendPacketCallback& cbk);
~PacketState();
uint64_t post_consume_rd_;
uint32_t seq_num_;
MediaConsumer::SendPacketCallback cbk_;
};
using PacketStateQueue = std::deque<PacketState>;
void HandleSendPacket(uint32_t seq_num, MediaConsumer::SendResult result);
void HandleFlush();
void HandleSignalCallback();
void UpdateSignalled();
void Fault(MediaResult reason);
void Cleanup();
bool Faulted() const {
return (MediaResult::OK != internal_state_);
}
bool Busy() const {
return flush_in_progress_;
}
// Pipe interface callbacks
MediaConsumerPtr pipe_;
MediaConsumer::FlushCallback pipe_flush_cbk_;
Closure signalled_callback_;
// A small helper which lets us nerf callbacks we may have directly scheduled
// on the main run loop which may be in flight as we get destroyed.
std::shared_ptr<CircularBufferMediaPipeAdapter*> thiz_;
// State for managing signalled/un-signalled status and signal callbacks.
SignalCbk signal_cbk_;
bool flush_in_progress_ = false;
bool fault_cbk_made_ = false;
bool cbk_scheduled_ = false;
uint64_t hi_water_mark_ = 0u;
uint64_t lo_water_mark_ = 0u;
bool signalled_ = false;
MediaResult internal_state_ = MediaResult::OK;
ScopedSharedBufferHandle buffer_handle_;
void* buffer_ = nullptr;
uint64_t buffer_size_ = 0;
uint64_t rd_, wr_;
// Packet queue state
std::atomic<uint32_t> flush_generation_;
std::atomic<uint32_t> seq_num_gen_;
PacketStateQueue in_flight_queue_;
};
} // namespace media
} // namespace mojo
#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_CIRCULAR_BUFFER_MEDIA_PIPE_ADAPTER_H_