| /* |
| * 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 "cros-camera/common.h" |
| #include "cros-camera/future.h" |
| #include "cros-camera/ipc_util.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); |
| |
| std::unique_ptr<JpegEncodeAccelerator> JpegEncodeAccelerator::CreateInstance() { |
| return base::WrapUnique<JpegEncodeAccelerator>( |
| new JpegEncodeAcceleratorImpl()); |
| } |
| |
| JpegEncodeAcceleratorImpl::JpegEncodeAcceleratorImpl() |
| : ipc_thread_("JeaIpcThread"), buffer_id_(0) { |
| VLOGF_ENTER(); |
| |
| mojo_channel_manager_ = CameraMojoChannelManager::CreateInstance(); |
| } |
| |
| JpegEncodeAcceleratorImpl::~JpegEncodeAcceleratorImpl() { |
| VLOGF_ENTER(); |
| if (ipc_thread_.IsRunning()) { |
| ipc_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&JpegEncodeAcceleratorImpl::DestroyOnIpcThread, |
| base::Unretained(this))); |
| ipc_thread_.Stop(); |
| } |
| VLOGF_EXIT(); |
| } |
| |
| bool JpegEncodeAcceleratorImpl::Start() { |
| VLOGF_ENTER(); |
| |
| if (!ipc_thread_.IsRunning()) { |
| if (!ipc_thread_.Start()) { |
| LOGF(ERROR) << "Failed to start IPC thread"; |
| return false; |
| } |
| } |
| |
| cancellation_relay_ = std::make_unique<CancellationRelay>(); |
| auto is_initialized = Future<bool>::Create(cancellation_relay_.get()); |
| ipc_thread_.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&JpegEncodeAcceleratorImpl::InitializeOnIpcThread, |
| base::Unretained(this), |
| GetFutureCallback(is_initialized))); |
| if (!is_initialized->Wait()) { |
| return false; |
| } |
| |
| VLOGF_EXIT(); |
| |
| return is_initialized->Get(); |
| } |
| |
| void JpegEncodeAcceleratorImpl::InitializeOnIpcThread( |
| base::Callback<void(bool)> callback) { |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| |
| if (jea_ptr_.is_bound()) { |
| callback.Run(true); |
| return; |
| } |
| |
| auto request = mojo::MakeRequest(&jea_ptr_); |
| |
| mojo_channel_manager_->CreateJpegEncodeAccelerator(std::move(request)); |
| jea_ptr_.set_connection_error_handler( |
| base::Bind(&JpegEncodeAcceleratorImpl::OnJpegEncodeAcceleratorError, |
| base::Unretained(this))); |
| |
| jea_ptr_->Initialize(callback); |
| VLOGF_EXIT(); |
| } |
| |
| void JpegEncodeAcceleratorImpl::DestroyOnIpcThread() { |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| jea_ptr_.reset(); |
| input_shm_map_.clear(); |
| exif_shm_map_.clear(); |
| cancellation_relay_ = nullptr; |
| VLOGF_EXIT(); |
| } |
| |
| void JpegEncodeAcceleratorImpl::OnJpegEncodeAcceleratorError() { |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| VLOGF_ENTER(); |
| LOGF(ERROR) << "There is a mojo error for JpegEncodeAccelerator"; |
| VLOGF_EXIT(); |
| DestroyOnIpcThread(); |
| } |
| |
| 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 buffer_id = buffer_id_; |
| // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. |
| buffer_id_ = (buffer_id_ + 1) & 0x3FFFFFFF; |
| |
| auto future = Future<int>::Create(cancellation_relay_.get()); |
| auto callback = base::Bind(&JpegEncodeAcceleratorImpl::EncodeSyncCallback, |
| base::Unretained(this), GetFutureCallback(future), |
| output_data_size); |
| |
| ipc_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&JpegEncodeAcceleratorImpl::EncodeOnIpcThread, |
| base::Unretained(this), buffer_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 (!jea_ptr_.is_bound()) { |
| 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(); |
| } |
| |
| void JpegEncodeAcceleratorImpl::EncodeOnIpcThread( |
| int32_t buffer_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_thread_.task_runner()->BelongsToCurrentThread()); |
| DCHECK_EQ(input_shm_map_.count(buffer_id), 0); |
| DCHECK_EQ(exif_shm_map_.count(buffer_id), 0); |
| |
| if (!jea_ptr_.is_bound()) { |
| callback.Run(buffer_id, 0, TRY_START_AGAIN); |
| } |
| |
| std::unique_ptr<base::SharedMemory> input_shm = |
| base::WrapUnique(new base::SharedMemory); |
| if (!input_shm->CreateAndMapAnonymous(input_buffer_size)) { |
| LOGF(WARNING) << "CreateAndMapAnonymous for input failed, size=" |
| << input_buffer_size; |
| callback.Run(buffer_id, 0, SHARED_MEMORY_FAIL); |
| return; |
| } |
| // Copy content from input buffer or file descriptor to shared memory. |
| if (input_buffer) { |
| memcpy(input_shm->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(buffer_id, 0, MMAP_FAIL); |
| return; |
| } |
| |
| memcpy(input_shm->memory(), mmap_buf, input_buffer_size); |
| munmap(mmap_buf, input_buffer_size); |
| } |
| |
| // Create SharedMemory for Exif buffer and copy data into it. |
| std::unique_ptr<base::SharedMemory> exif_shm = |
| base::WrapUnique(new base::SharedMemory); |
| // Create a dummy |exif_shm| even if |exif_buffer_size| is 0. |
| uint32_t exif_shm_size = std::max(exif_buffer_size, 1u); |
| if (!exif_shm->CreateAndMapAnonymous(exif_shm_size)) { |
| LOGF(WARNING) << "CreateAndMapAnonymous for exif failed, size=" |
| << exif_shm_size; |
| callback.Run(buffer_id, 0, SHARED_MEMORY_FAIL); |
| return; |
| } |
| if (exif_buffer_size) { |
| memcpy(exif_shm->memory(), exif_buffer, exif_buffer_size); |
| } |
| |
| int dup_input_fd = dup(input_shm->handle().fd); |
| int dup_exif_fd = dup(exif_shm->handle().fd); |
| int dup_output_fd = dup(output_fd); |
| |
| mojo::ScopedHandle input_handle = WrapPlatformHandle(dup_input_fd); |
| mojo::ScopedHandle exif_handle = WrapPlatformHandle(dup_exif_fd); |
| mojo::ScopedHandle output_handle = |
| WrapPlatformHandle(dup_output_fd); |
| |
| input_shm_map_[buffer_id] = std::move(input_shm); |
| exif_shm_map_[buffer_id] = std::move(exif_shm); |
| |
| jea_ptr_->EncodeWithFD(buffer_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::OnEncodeAck, |
| base::Unretained(this), callback)); |
| } |
| |
| void JpegEncodeAcceleratorImpl::EncodeSyncCallback( |
| base::Callback<void(int)> callback, uint32_t* output_data_size, |
| int32_t buffer_id, uint32_t output_size, int status) { |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| *output_data_size = output_size; |
| callback.Run(status); |
| } |
| |
| void JpegEncodeAcceleratorImpl::OnEncodeAck(EncodeWithFDCallback callback, |
| int32_t buffer_id, |
| uint32_t output_size, |
| mojom::EncodeStatus status) { |
| DCHECK(ipc_thread_.task_runner()->BelongsToCurrentThread()); |
| DCHECK_EQ(input_shm_map_.count(buffer_id), 1u); |
| DCHECK_EQ(exif_shm_map_.count(buffer_id), 1u); |
| input_shm_map_.erase(buffer_id); |
| exif_shm_map_.erase(buffer_id); |
| callback.Run(buffer_id, output_size, static_cast<int>(status)); |
| } |
| |
| } // namespace cros |