blob: 642c6864f7817a417714580fd7000a3f866d5665 [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.
#include "base/logging.h"
#include "services/media/common/media_pipe_base.h"
namespace mojo {
namespace media {
MediaPipeBase::MediaPipeBase()
: binding_(this) {
}
MediaPipeBase::~MediaPipeBase() {
}
MojoResult MediaPipeBase::Init(InterfaceRequest<MediaPipe> request,
uint64_t shared_buffer_size) {
// Double init?
if (IsInitialized()) {
return MOJO_RESULT_ALREADY_EXISTS;
}
// Valid size?
DCHECK_GT(shared_buffer_size, 0u);
DCHECK_LE(shared_buffer_size, MediaPipeState::kMaxPayloadLen);
if (!shared_buffer_size ||
(shared_buffer_size > MediaPipeState::kMaxPayloadLen)) {
return MOJO_RESULT_INVALID_ARGUMENT;
}
DCHECK(!buffer_);
buffer_ = MappedSharedBuffer::Create(shared_buffer_size);
if (buffer_ == nullptr) {
return MOJO_RESULT_UNKNOWN;
}
binding_.Bind(request.Pass());
binding_.set_connection_error_handler([this]() -> void {
Reset();
});
return MOJO_RESULT_OK;
}
bool MediaPipeBase::IsInitialized() const {
DCHECK((!binding_.is_bound() && (buffer_ == nullptr)) ||
(binding_.is_bound() && (buffer_ != nullptr)));
return !!buffer_;
}
void MediaPipeBase::Reset() {
if (binding_.is_bound()) {
binding_.Close();
}
buffer_ = nullptr;
}
void MediaPipeBase::GetState(const GetStateCallback& cbk) {
static const MojoDuplicateBufferHandleOptions options = {
.struct_size = sizeof(*this),
.flags = MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE,
};
MediaPipeStatePtr state_ptr(MediaPipeState::New());
// If we have not been successfully initialized, send back an invalid handle
// and a zero length.
if (!buffer_) {
state_ptr->payload_buffer_len = 0;
} else {
MojoResult res = DuplicateBuffer(buffer_->handle().get(),
&options,
&state_ptr->payload_buffer);
state_ptr->payload_buffer_len = buffer_->size();
DCHECK(MOJO_RESULT_OK == res);
}
cbk.Run(state_ptr.Pass());
}
void MediaPipeBase::SendPacket(MediaPacketPtr packet,
const SendPacketCallback& cbk) {
// If we have not been successfully initialized, then we should not be getting
// packets pushed to us.
if (!buffer_) {
LOG(ERROR) << "SendPacket called with no shared buffer established!";
Reset();
return;
}
DCHECK(buffer_->size());
// The offset(s) and size(s) of this payload must to reside within the space
// of the shared buffer. If any does not, this send operation is not valid.
const MediaPacketRegionPtr* r = &packet->payload;
size_t i = 0;
size_t extra_payload_size =
packet->extra_payload.is_null() ? 0 : packet->extra_payload.size();
while (true) {
if ((*r).is_null()) {
LOG(ERROR) << "Missing region structure at index " << i
<< " during SendPacket";
Reset();
return;
}
auto offset = (*r)->offset;
auto length = (*r)->length;
if ((offset > buffer_->size()) || (length > (buffer_->size() - offset))) {
LOG(ERROR) << "Region [" << offset << "," << (offset + length)
<< ") at index " << i
<< " is out of range for shared buffer of size "
<< buffer_->size()
<< " during SendPacket.";
Reset();
return;
}
if (i >= extra_payload_size) {
break;
}
DCHECK(packet->extra_payload);
r = &packet->extra_payload[i++];
}
// Looks good, send this packet up to the implementation layer.
MediaPacketStatePtr ptr(new MediaPacketState(packet.Pass(), buffer_, cbk));
OnPacketReceived(std::move(ptr));
}
void MediaPipeBase::Flush(const FlushCallback& cbk) {
// If we have not been successfully initialized, then we should not be getting
// packets pushed to us.
if (!buffer_) {
LOG(ERROR) << "Flush called with no shared buffer established!";
Reset();
return;
}
// Pass the flush request up to the implementation layer. If something goes
// fatally wrong up there, close the connection.
if (!OnFlushRequested(cbk)) {
Reset();
}
}
MediaPipeBase::MediaPacketState::MediaPacketState(
MediaPacketPtr packet,
const MappedSharedBufferPtr& buffer,
const SendPacketCallback& cbk)
: packet_(packet.Pass()),
buffer_(buffer),
cbk_(cbk),
result_(MediaPipe::SendResult::CONSUMED) {
DCHECK(packet_);
DCHECK(packet_->payload);
}
MediaPipeBase::MediaPacketState::~MediaPacketState() {
cbk_.Run(result_);
}
void MediaPipeBase::MediaPacketState::SetResult(MediaPipe::SendResult result) {
MediaPipe::SendResult tmp = MediaPipe::SendResult::CONSUMED;
result_.compare_exchange_strong(tmp, result);
}
MediaPipeBase::MappedSharedBufferPtr MediaPipeBase::MappedSharedBuffer::Create(
size_t size) {
MappedSharedBufferPtr ret(new MappedSharedBuffer(size));
return ret->base() ? ret : nullptr;
}
MediaPipeBase::MappedSharedBuffer::~MappedSharedBuffer() {
if (nullptr != base_) {
MojoResult res = UnmapBuffer(base_);
CHECK(res == MOJO_RESULT_OK);
}
}
MediaPipeBase::MappedSharedBuffer::MappedSharedBuffer(size_t size)
: size_(size) {
static const MojoCreateSharedBufferOptions opt {
.struct_size = sizeof(MojoDuplicateBufferHandleOptions),
.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE,
};
MojoResult res;
res = CreateSharedBuffer(&opt, size_, &handle_);
if (MOJO_RESULT_OK == res) {
// TODO(johngro) : We really only need read access to this buffer. Ideally,
// we could request that using flags, but there does not seem to be a way to
// do this right now.
res = MapBuffer(handle_.get(),
0u,
size_,
&base_,
MOJO_MAP_BUFFER_FLAG_NONE);
if (MOJO_RESULT_OK != res) {
LOG(ERROR) << "Failed to map shared buffer of size " << size_
<< " (res " << res << ")";
DCHECK(base_ == nullptr);
handle_.reset();
}
} else {
LOG(ERROR) << "Failed to create shared buffer of size " << size_
<< " (res " << res << ")";
DCHECK(!handle_.is_valid());
}
}
} // namespace media
} // namespace mojo