Chromium Code Reviews| Index: chrome/gpu/gpu_arc_video_encode_accelerator.cc |
| diff --git a/chrome/gpu/gpu_arc_video_encode_accelerator.cc b/chrome/gpu/gpu_arc_video_encode_accelerator.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7430e8c687f7780d280258aa64dcf25c1c3bd533 |
| --- /dev/null |
| +++ b/chrome/gpu/gpu_arc_video_encode_accelerator.cc |
| @@ -0,0 +1,224 @@ |
| +// Copyright 2017 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 "chrome/gpu/gpu_arc_video_encode_accelerator.h" |
| + |
| +#include <utility> |
| + |
| +#include "base/logging.h" |
| +#include "base/sys_info.h" |
| +#include "media/base/video_types.h" |
| +#include "media/gpu/gpu_video_encode_accelerator_factory.h" |
| +#include "mojo/public/cpp/bindings/strong_binding.h" |
| +#include "mojo/public/cpp/bindings/type_converter.h" |
| +#include "mojo/public/cpp/system/platform_handle.h" |
| + |
| +#define DVLOGF(x) DVLOG(x) << __func__ << "(): " |
| + |
| +namespace chromeos { |
| +namespace arc { |
| + |
| +GpuArcVideoEncodeAccelerator::GpuArcVideoEncodeAccelerator( |
| + const gpu::GpuPreferences& gpu_preferences) |
| + : gpu_preferences_(gpu_preferences) {} |
| + |
| +GpuArcVideoEncodeAccelerator::~GpuArcVideoEncodeAccelerator() = default; |
| + |
| +// VideoEncodeAccelerator::Client implementation. |
| +void GpuArcVideoEncodeAccelerator::RequireBitstreamBuffers( |
| + unsigned int input_count, |
| + const gfx::Size& coded_size, |
| + size_t output_buffer_size) { |
| + DVLOGF(2) << "input_count=" << input_count |
| + << ", coded_size=" << coded_size.ToString() |
| + << ", output_buffer_size=" << output_buffer_size; |
| + DCHECK(client_); |
| + coded_size_ = coded_size; |
| + client_->RequireBitstreamBuffers(input_count, coded_size, output_buffer_size); |
| +} |
| + |
| +void GpuArcVideoEncodeAccelerator::BitstreamBufferReady( |
| + int32_t bitstream_buffer_id, |
| + size_t payload_size, |
| + bool key_frame, |
| + base::TimeDelta timestamp) { |
| + DVLOGF(2) << "id=" << bitstream_buffer_id; |
| + DCHECK(client_); |
| + client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame, |
| + timestamp.InMicroseconds()); |
| +} |
| + |
| +void GpuArcVideoEncodeAccelerator::NotifyError(Error error) { |
| + DVLOGF(2) << "error=" << error; |
| + DCHECK(client_); |
| + client_->NotifyError(error); |
| +} |
| + |
| +// ::arc::mojom::VideoEncodeAccelerator implementation. |
| +void GpuArcVideoEncodeAccelerator::GetSupportedProfiles( |
| + const GetSupportedProfilesCallback& callback) { |
| + callback.Run(media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles( |
| + gpu_preferences_)); |
| +} |
| + |
| +void GpuArcVideoEncodeAccelerator::Initialize( |
| + VideoPixelFormat input_format, |
| + const gfx::Size& visible_size, |
| + VideoEncodeAccelerator::StorageType input_storage, |
| + VideoCodecProfile output_profile, |
| + uint32_t initial_bitrate, |
| + VideoEncodeClientPtr client, |
| + const InitializeCallback& callback) { |
| + DVLOGF(2) << "visible_size=" << visible_size.ToString() |
| + << ", profile=" << output_profile; |
| + if (!client) { |
|
dcheng
2017/06/21 09:42:29
Does Mojo actually allow a null interface pointer
Owen Lin
2017/06/22 07:43:14
You're right. I tested it, it failed in the IPC me
|
| + LOG(ERROR) << "Invalid client"; |
| + callback.Run(false); |
| + return; |
| + } |
| + |
| + input_pixel_format_ = static_cast<media::VideoPixelFormat>(input_format); |
|
dcheng
2017/06/21 09:42:28
Do we need these casts? Seems like they're the sam
Owen Lin
2017/06/22 07:43:14
Removed. It should be removed after we type mappin
|
| + visible_size_ = visible_size; |
| + accelerator_ = media::GpuVideoEncodeAcceleratorFactory::CreateVEA( |
| + input_pixel_format_, visible_size_, |
| + static_cast<media::VideoCodecProfile>(output_profile), initial_bitrate, |
|
dcheng
2017/06/21 09:42:29
Similarly, I'm not sure why this requires a cast.
Owen Lin
2017/06/22 07:43:14
Done.
|
| + this, gpu_preferences_); |
| + if (accelerator_ == nullptr) { |
| + LOG(ERROR) << "Failed to create a VideoEncodeAccelerator."; |
|
dcheng
2017/06/21 09:42:29
Would personally suggest DLOG for everything that
Owen Lin
2017/06/22 07:43:14
Those messages should only be logged when errors h
dcheng
2017/06/22 20:05:13
Right, but is it useful to non-developers? Using L
Owen Lin
2017/06/23 01:37:54
OK. I got your point. We would like to use LOG sim
dcheng
2017/06/23 07:38:44
I don't think we should be bloating official build
|
| + callback.Run(false); |
| + return; |
| + } |
| + client_ = std::move(client); |
| + callback.Run(true); |
| +} |
| + |
| +static void DropSharedMemory(std::unique_ptr<base::SharedMemory> shm) { |
| + // Just let |shm| fall out of scope. |
| +} |
| + |
| +void GpuArcVideoEncodeAccelerator::Encode( |
| + mojo::ScopedHandle handle, |
| + std::vector<::arc::VideoFramePlane> planes, |
| + int64_t timestamp, |
| + bool force_keyframe) { |
| + DVLOGF(2) << "timestamp=" << timestamp; |
| + if (!accelerator_) { |
| + LOG(ERROR) << "Accelerator is not initialized."; |
| + return; |
| + } |
| + |
| + if (planes.empty()) { // EOS |
| + accelerator_->Encode(media::VideoFrame::CreateEOSFrame(), force_keyframe); |
| + return; |
| + } |
| + |
| + base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(handle)); |
| + if (!fd.is_valid()) |
| + return; |
| + |
| + size_t allocation_size = |
| + media::VideoFrame::AllocationSize(input_pixel_format_, coded_size_); |
| + |
| + // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. |
| + // TODO(rockot): This fd comes from a mojo::ScopedHandle in |
| + // GpuArcVideoService::BindSharedMemory. That should be passed through, |
| + // rather than pulling out the fd. https://crbug.com/713763. |
| + // TODO(rockot): Pass through a real size rather than |0|. |
| + base::UnguessableToken guid = base::UnguessableToken::Create(); |
| + base::SharedMemoryHandle shm_handle(base::FileDescriptor(fd.release(), true), |
| + 0u, guid); |
| + auto shm = base::MakeUnique<base::SharedMemory>(shm_handle, true); |
| + |
| + base::CheckedNumeric<off_t> map_offset = planes[0].offset; |
| + base::CheckedNumeric<size_t> map_size = allocation_size; |
| + const uint32_t aligned_offset = |
| + planes[0].offset % base::SysInfo::VMAllocationGranularity(); |
| + map_offset -= aligned_offset; |
| + map_size += aligned_offset; |
| + |
| + if (!map_offset.IsValid() || !map_size.IsValid()) { |
| + LOG(ERROR) << "Invalid map_offset or map_size"; |
| + client_->NotifyError(Error::kInvalidArgumentError); |
| + return; |
| + } |
| + if (!shm->MapAt(map_offset.ValueOrDie(), map_size.ValueOrDie())) { |
| + LOG(ERROR) << "Failed to map memory."; |
| + client_->NotifyError(Error::kPlatformFailureError); |
| + return; |
| + } |
| + |
| + uint8_t* shm_memory = reinterpret_cast<uint8_t*>(shm->memory()); |
| + auto frame = media::VideoFrame::WrapExternalSharedMemory( |
| + input_pixel_format_, coded_size_, gfx::Rect(visible_size_), visible_size_, |
| + shm_memory + aligned_offset, allocation_size, shm_handle, |
| + planes[0].offset, base::TimeDelta::FromMicroseconds(timestamp)); |
| + |
| + // Wrap |shm| in a callback and add it as a destruction observer, so it |
| + // stays alive and mapped until |frame| goes out of scope. |
| + frame->AddDestructionObserver( |
| + base::Bind(&DropSharedMemory, base::Passed(&shm))); |
| + accelerator_->Encode(frame, force_keyframe); |
| +} |
| + |
| +void GpuArcVideoEncodeAccelerator::UseOutputBitstreamBuffer( |
| + int32_t bitstream_buffer_id, |
| + mojo::ScopedHandle shmem_fd, |
| + uint32_t offset, |
| + uint32_t size) { |
| + DVLOGF(2) << "id=" << bitstream_buffer_id; |
| + if (!accelerator_) { |
| + LOG(ERROR) << "Accelerator is not initialized."; |
| + return; |
| + } |
| + |
| + base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(shmem_fd)); |
| + if (!fd.is_valid()) |
| + return; |
| + |
| + // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. |
| + // TODO(rockot): This fd comes from a mojo::ScopedHandle in |
| + // GpuArcVideoService::BindSharedMemory. That should be passed through, |
| + // rather than pulling out the fd. https://crbug.com/713763. |
| + // TODO(rockot): Pass through a real size rather than |0|. |
| + base::UnguessableToken guid = base::UnguessableToken::Create(); |
| + base::SharedMemoryHandle shm_handle(base::FileDescriptor(fd.release(), true), |
| + 0u, guid); |
|
dcheng
2017/06/21 09:42:29
What is the behavior if we just pass 0 here? Does
Owen Lin
2017/06/22 07:43:14
AFAIK, this argument is not used yet. rockot@ shou
|
| + accelerator_->UseOutputBitstreamBuffer( |
| + media::BitstreamBuffer(bitstream_buffer_id, shm_handle, size, offset)); |
| +} |
| + |
| +void GpuArcVideoEncodeAccelerator::RequestEncodingParametersChange( |
| + uint32_t bitrate, |
| + uint32_t framerate) { |
| + DVLOGF(2) << "bitrate=" << bitrate << ", framerate=" << framerate; |
| + if (!accelerator_) { |
| + LOG(ERROR) << "Accelerator is not initialized."; |
| + return; |
| + } |
| + accelerator_->RequestEncodingParametersChange(bitrate, framerate); |
| +} |
| + |
| +base::ScopedFD GpuArcVideoEncodeAccelerator::UnwrapFdFromMojoHandle( |
| + mojo::ScopedHandle handle) { |
| + DCHECK(client_); |
| + if (!handle.is_valid()) { |
| + LOG(ERROR) << "handle is invalid."; |
| + client_->NotifyError(Error::kInvalidArgumentError); |
| + return base::ScopedFD(); |
| + } |
| + |
| + base::PlatformFile platform_file; |
| + MojoResult mojo_result = |
| + mojo::UnwrapPlatformFile(std::move(handle), &platform_file); |
| + if (mojo_result != MOJO_RESULT_OK) { |
| + LOG(ERROR) << "UnwrapPlatformFile failed: " << mojo_result; |
| + client_->NotifyError(Error::kPlatformFailureError); |
| + return base::ScopedFD(); |
| + } |
| + |
| + return base::ScopedFD(platform_file); |
| +} |
| + |
| +} // namespace arc |
| +} // namespace chromeos |