blob: 1cb3c019377ef4ed0dbc614d22a4516bf788c19e [file] [log] [blame]
/*
* Copyright 2018 The Chromium OS 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 "common/jpeg/jpeg_encode_accelerator_impl.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <utility>
#include <algorithm>
#include <base/bind.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <base/posix/eintr_wrapper.h>
#include <base/run_loop.h>
#include <mojo/public/c/system/buffer.h>
#include <mojo/public/cpp/system/buffer.h>
#include <mojo/public/cpp/system/platform_handle.h>
#include "cros-camera/common.h"
#include "cros-camera/future.h"
#include "cros-camera/ipc_util.h"
#include "mojo/gpu/dmabuf.mojom.h"
#define STATIC_ASSERT_ENUM(name) \
static_assert(static_cast<int>(JpegEncodeAccelerator::name) == \
static_cast<int>(mojom::EncodeStatus::name), \
"mismatching enum: " #name)
namespace cros {
STATIC_ASSERT_ENUM(ENCODE_OK);
STATIC_ASSERT_ENUM(HW_JPEG_ENCODE_NOT_SUPPORTED);
STATIC_ASSERT_ENUM(THREAD_CREATION_FAILED);
STATIC_ASSERT_ENUM(INVALID_ARGUMENT);
STATIC_ASSERT_ENUM(INACCESSIBLE_OUTPUT_BUFFER);
STATIC_ASSERT_ENUM(PARSE_IMAGE_FAILED);
STATIC_ASSERT_ENUM(PLATFORM_FAILURE);
// static
std::unique_ptr<JpegEncodeAccelerator> JpegEncodeAccelerator::CreateInstance(
CameraMojoChannelManager* mojo_manager) {
return base::WrapUnique<JpegEncodeAccelerator>(
new JpegEncodeAcceleratorImpl(mojo_manager));
}
JpegEncodeAcceleratorImpl::JpegEncodeAcceleratorImpl(
CameraMojoChannelManager* mojo_manager)
: task_id_(0),
mojo_manager_(mojo_manager),
cancellation_relay_(new CancellationRelay),
ipc_bridge_(new IPCBridge(mojo_manager, cancellation_relay_.get())) {
VLOGF_ENTER();
}
JpegEncodeAcceleratorImpl::~JpegEncodeAcceleratorImpl() {
VLOGF_ENTER();
bool result = mojo_manager_->GetIpcTaskRunner()->DeleteSoon(
FROM_HERE, std::move(ipc_bridge_));
DCHECK(result);
cancellation_relay_ = nullptr;
VLOGF_EXIT();
}
bool JpegEncodeAcceleratorImpl::Start() {
VLOGF_ENTER();
auto is_initialized = Future<bool>::Create(cancellation_relay_.get());
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE, base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::Start,
ipc_bridge_->GetWeakPtr(),
cros::GetFutureCallback(is_initialized)));
if (!is_initialized->Wait()) {
return false;
}
VLOGF_EXIT();
return is_initialized->Get();
}
int JpegEncodeAcceleratorImpl::EncodeSync(int input_fd,
const uint8_t* input_buffer,
uint32_t input_buffer_size,
int32_t coded_size_width,
int32_t coded_size_height,
const uint8_t* exif_buffer,
uint32_t exif_buffer_size,
int output_fd,
uint32_t output_buffer_size,
uint32_t* output_data_size) {
int32_t task_id = task_id_;
// Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
task_id_ = (task_id_ + 1) & 0x3FFFFFFF;
auto future = Future<int>::Create(cancellation_relay_.get());
auto callback =
base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::EncodeSyncCallback,
ipc_bridge_->GetWeakPtr(), GetFutureCallback(future),
output_data_size, task_id);
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::EncodeLegacy,
ipc_bridge_->GetWeakPtr(), task_id, input_fd, input_buffer,
input_buffer_size, coded_size_width, coded_size_height,
exif_buffer, exif_buffer_size, output_fd, output_buffer_size,
std::move(callback)));
if (!future->Wait()) {
if (!ipc_bridge_->IsReady()) {
LOGF(WARNING) << "There may be an mojo channel error.";
return TRY_START_AGAIN;
}
LOGF(WARNING) << "There is no encode response from JEA mojo channel.";
return NO_ENCODE_RESPONSE;
}
VLOGF_EXIT();
return future->Get();
}
int JpegEncodeAcceleratorImpl::EncodeSync(
uint32_t input_format,
const std::vector<JpegCompressor::DmaBufPlane>& input_planes,
const std::vector<JpegCompressor::DmaBufPlane>& output_planes,
const uint8_t* exif_buffer,
uint32_t exif_buffer_size,
int coded_size_width,
int coded_size_height,
uint32_t* output_data_size) {
int32_t task_id = task_id_;
// Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
task_id_ = (task_id_ + 1) & 0x3FFFFFFF;
auto future = Future<int>::Create(cancellation_relay_.get());
auto callback =
base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::EncodeSyncCallback,
ipc_bridge_->GetWeakPtr(), GetFutureCallback(future),
output_data_size, task_id);
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE, base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::Encode,
ipc_bridge_->GetWeakPtr(), task_id, input_format,
std::move(input_planes), std::move(output_planes),
exif_buffer, exif_buffer_size, coded_size_width,
coded_size_height, std::move(callback)));
if (!future->Wait()) {
if (!ipc_bridge_->IsReady()) {
LOGF(WARNING) << "There may be an mojo channel error.";
return TRY_START_AGAIN;
}
LOGF(WARNING) << "There is no encode response from JEA mojo channel.";
return NO_ENCODE_RESPONSE;
}
VLOGF_EXIT();
return future->Get();
}
JpegEncodeAcceleratorImpl::IPCBridge::IPCBridge(
CameraMojoChannelManager* mojo_manager,
CancellationRelay* cancellation_relay)
: mojo_manager_(mojo_manager),
cancellation_relay_(cancellation_relay),
ipc_task_runner_(mojo_manager_->GetIpcTaskRunner()) {}
JpegEncodeAcceleratorImpl::IPCBridge::~IPCBridge() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
Destroy();
}
void JpegEncodeAcceleratorImpl::IPCBridge::Start(
base::Callback<void(bool)> callback) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
if (jea_ptr_.is_bound()) {
std::move(callback).Run(true);
return;
}
auto request = mojo::MakeRequest(&jea_ptr_);
jea_ptr_.set_connection_error_handler(base::Bind(
&JpegEncodeAcceleratorImpl::IPCBridge::OnJpegEncodeAcceleratorError,
GetWeakPtr()));
mojo_manager_->CreateJpegEncodeAccelerator(
std::move(request),
base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::Initialize,
GetWeakPtr(), std::move(callback)),
base::Bind(
&JpegEncodeAcceleratorImpl::IPCBridge::OnJpegEncodeAcceleratorError,
GetWeakPtr()));
VLOGF_EXIT();
}
void JpegEncodeAcceleratorImpl::IPCBridge::Destroy() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
jea_ptr_.reset();
}
void JpegEncodeAcceleratorImpl::IPCBridge::EncodeLegacy(
int32_t task_id,
int input_fd,
const uint8_t* input_buffer,
uint32_t input_buffer_size,
int32_t coded_size_width,
int32_t coded_size_height,
const uint8_t* exif_buffer,
uint32_t exif_buffer_size,
int output_fd,
uint32_t output_buffer_size,
EncodeWithFDCallback callback) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
if (!jea_ptr_.is_bound()) {
callback.Run(0, TRY_START_AGAIN);
}
base::WritableSharedMemoryRegion input_shm_region =
base::WritableSharedMemoryRegion::Create(input_buffer_size);
if (!input_shm_region.IsValid()) {
LOGF(WARNING) << "Create shared memory region for input failed, size="
<< input_buffer_size;
callback.Run(0, SHARED_MEMORY_FAIL);
return;
}
base::WritableSharedMemoryMapping input_shm_mapping = input_shm_region.Map();
if (!input_shm_mapping.IsValid()) {
LOGF(WARNING) << "Create mapping for input failed, size="
<< input_buffer_size;
callback.Run(0, SHARED_MEMORY_FAIL);
return;
}
// Copy content from input buffer or file descriptor to shared memory.
if (input_buffer) {
memcpy(input_shm_mapping.memory(), input_buffer, input_buffer_size);
} else {
uint8_t* mmap_buf = static_cast<uint8_t*>(
mmap(NULL, input_buffer_size, PROT_READ, MAP_SHARED, input_fd, 0));
if (mmap_buf == MAP_FAILED) {
LOGF(WARNING) << "MMAP for input_fd:" << input_fd << " Failed.";
callback.Run(0, MMAP_FAIL);
return;
}
memcpy(input_shm_mapping.memory(), mmap_buf, input_buffer_size);
munmap(mmap_buf, input_buffer_size);
}
// Create WritableSharedMemory{Region,Mapping} for Exif buffer and copy data
// into it.
// Create a dummy |exif_shm| even if |exif_buffer_size| is 0.
uint32_t exif_shm_size = std::max(exif_buffer_size, 1u);
base::WritableSharedMemoryRegion exif_shm_region =
base::WritableSharedMemoryRegion::Create(exif_shm_size);
base::WritableSharedMemoryMapping exif_shm_mapping = exif_shm_region.Map();
if (!exif_shm_mapping.IsValid()) {
LOGF(WARNING) << "Create and Map for exif failed, size=" << exif_shm_size;
callback.Run(0, SHARED_MEMORY_FAIL);
return;
}
if (exif_buffer_size) {
memcpy(exif_shm_mapping.memory(), exif_buffer, exif_buffer_size);
}
base::subtle::PlatformSharedMemoryRegion input_platform_shm =
base::WritableSharedMemoryRegion::TakeHandleForSerialization(
std::move(input_shm_region));
base::subtle::PlatformSharedMemoryRegion exif_platform_shm =
base::WritableSharedMemoryRegion::TakeHandleForSerialization(
std::move(exif_shm_region));
int dup_output_fd = HANDLE_EINTR(dup(output_fd));
mojo::ScopedHandle input_handle = mojo::WrapPlatformFile(
std::move(input_platform_shm.PassPlatformHandle().fd));
mojo::ScopedHandle exif_handle = mojo::WrapPlatformFile(
std::move(exif_platform_shm.PassPlatformHandle().fd));
mojo::ScopedHandle output_handle =
mojo::WrapPlatformFile(base::ScopedPlatformFile(dup_output_fd));
jea_ptr_->EncodeWithFD(
task_id, std::move(input_handle), input_buffer_size, coded_size_width,
coded_size_height, std::move(exif_handle), exif_buffer_size,
std::move(output_handle), output_buffer_size,
base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::OnEncodeAck,
GetWeakPtr(), callback));
}
void JpegEncodeAcceleratorImpl::IPCBridge::Encode(
int32_t task_id,
uint32_t input_format,
const std::vector<JpegCompressor::DmaBufPlane>& input_planes,
const std::vector<JpegCompressor::DmaBufPlane>& output_planes,
const uint8_t* exif_buffer,
uint32_t exif_buffer_size,
int coded_size_width,
int coded_size_height,
EncodeWithDmaBufCallback callback) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
if (!jea_ptr_.is_bound()) {
callback.Run(0, TRY_START_AGAIN);
return;
}
// Create SharedMemory for Exif buffer and copy data into it.
// Create a dummy |exif_shm| even if |exif_buffer_size| is 0.
uint32_t exif_shm_size = std::max(exif_buffer_size, 1u);
base::WritableSharedMemoryRegion exif_shm_region =
base::WritableSharedMemoryRegion::Create(exif_shm_size);
if (!exif_shm_region.IsValid()) {
LOGF(WARNING) << "Create shared memory region for exif failed, size="
<< exif_shm_size;
callback.Run(0, SHARED_MEMORY_FAIL);
return;
}
base::WritableSharedMemoryMapping exif_shm_mapping = exif_shm_region.Map();
if (!exif_shm_mapping.IsValid()) {
LOGF(WARNING) << "Create mapping for exif failed, size=" << exif_shm_size;
callback.Run(0, SHARED_MEMORY_FAIL);
return;
}
if (exif_buffer_size) {
memcpy(exif_shm_mapping.memory(), exif_buffer, exif_buffer_size);
}
base::subtle::PlatformSharedMemoryRegion exif_platform_shm =
base::WritableSharedMemoryRegion::TakeHandleForSerialization(
std::move(exif_shm_region));
mojo::ScopedHandle exif_handle = mojo::WrapPlatformFile(
std::move(exif_platform_shm.PassPlatformHandle().fd));
auto WrapToMojoPlanes =
[](const std::vector<JpegCompressor::DmaBufPlane>& planes) {
std::vector<cros::mojom::DmaBufPlanePtr> mojo_planes;
for (auto plane : planes) {
auto mojo_plane = cros::mojom::DmaBufPlane::New();
mojo_plane->fd_handle = mojo::WrapPlatformFile(
base::ScopedPlatformFile(HANDLE_EINTR(dup(plane.fd))));
mojo_plane->stride = plane.stride;
mojo_plane->offset = plane.offset;
mojo_plane->size = plane.size;
mojo_planes.push_back(std::move(mojo_plane));
}
return mojo_planes;
};
std::vector<cros::mojom::DmaBufPlanePtr> mojo_input_planes =
WrapToMojoPlanes(input_planes);
std::vector<cros::mojom::DmaBufPlanePtr> mojo_output_planes =
WrapToMojoPlanes(output_planes);
jea_ptr_->EncodeWithDmaBuf(
task_id, input_format, std::move(mojo_input_planes),
std::move(mojo_output_planes), std::move(exif_handle), exif_buffer_size,
coded_size_width, coded_size_height,
base::Bind(&JpegEncodeAcceleratorImpl::IPCBridge::OnEncodeDmaBufAck,
GetWeakPtr(), callback));
}
void JpegEncodeAcceleratorImpl::IPCBridge::EncodeSyncCallback(
base::Callback<void(int)> callback,
uint32_t* output_data_size,
int32_t task_id,
uint32_t output_size,
int status) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
*output_data_size = output_size;
callback.Run(status);
}
base::WeakPtr<JpegEncodeAcceleratorImpl::IPCBridge>
JpegEncodeAcceleratorImpl::IPCBridge::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
bool JpegEncodeAcceleratorImpl::IPCBridge::IsReady() {
return jea_ptr_.is_bound();
}
void JpegEncodeAcceleratorImpl::IPCBridge::Initialize(
base::Callback<void(bool)> callback) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
jea_ptr_->Initialize(std::move(callback));
}
void JpegEncodeAcceleratorImpl::IPCBridge::OnJpegEncodeAcceleratorError() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
LOGF(ERROR) << "There is a mojo error for JpegEncodeAccelerator";
cancellation_relay_->CancelAllFutures();
Destroy();
VLOGF_EXIT();
}
void JpegEncodeAcceleratorImpl::IPCBridge::OnEncodeAck(
EncodeWithFDCallback callback,
int32_t task_id,
uint32_t output_size,
mojom::EncodeStatus status) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
callback.Run(output_size, static_cast<int>(status));
}
void JpegEncodeAcceleratorImpl::IPCBridge::OnEncodeDmaBufAck(
EncodeWithDmaBufCallback callback,
uint32_t output_size,
mojom::EncodeStatus status) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
callback.Run(output_size, static_cast<int>(status));
}
} // namespace cros