| // 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 "media/mojo/clients/mojo_video_encode_accelerator.h" |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "gpu/ipc/client/gpu_channel_host.h" |
| #include "media/base/video_frame.h" |
| #include "media/gpu/gpu_video_accelerator_util.h" |
| #include "media/mojo/common/mojo_shared_buffer_video_frame.h" |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| // Does nothing but keeping |frame| alive. |
| void KeepVideoFrameAlive(const scoped_refptr<VideoFrame>& frame) {} |
| |
| // File-static mojom::VideoEncodeAcceleratorClient implementation to trampoline |
| // method calls to its |client_|. Note that this class is thread hostile when |
| // bound. |
| class VideoEncodeAcceleratorClient |
| : public mojom::VideoEncodeAcceleratorClient { |
| public: |
| VideoEncodeAcceleratorClient( |
| VideoEncodeAccelerator::Client* client, |
| mojom::VideoEncodeAcceleratorClientRequest request); |
| ~VideoEncodeAcceleratorClient() override = default; |
| |
| // mojom::VideoEncodeAcceleratorClient impl. |
| void RequireBitstreamBuffers(uint32_t input_count, |
| const gfx::Size& input_coded_size, |
| uint32_t output_buffer_size) override; |
| void BitstreamBufferReady( |
| int32_t bitstream_buffer_id, |
| const media::BitstreamBufferMetadata& metadata) override; |
| void NotifyError(VideoEncodeAccelerator::Error error) override; |
| |
| private: |
| VideoEncodeAccelerator::Client* client_; |
| mojo::Binding<mojom::VideoEncodeAcceleratorClient> binding_; |
| |
| DISALLOW_COPY_AND_ASSIGN(VideoEncodeAcceleratorClient); |
| }; |
| |
| VideoEncodeAcceleratorClient::VideoEncodeAcceleratorClient( |
| VideoEncodeAccelerator::Client* client, |
| mojom::VideoEncodeAcceleratorClientRequest request) |
| : client_(client), binding_(this, std::move(request)) { |
| DCHECK(client_); |
| } |
| |
| void VideoEncodeAcceleratorClient::RequireBitstreamBuffers( |
| uint32_t input_count, |
| const gfx::Size& input_coded_size, |
| uint32_t output_buffer_size) { |
| DVLOG(2) << __func__ << " input_count= " << input_count |
| << " input_coded_size= " << input_coded_size.ToString() |
| << " output_buffer_size=" << output_buffer_size; |
| client_->RequireBitstreamBuffers(input_count, input_coded_size, |
| output_buffer_size); |
| } |
| |
| void VideoEncodeAcceleratorClient::BitstreamBufferReady( |
| int32_t bitstream_buffer_id, |
| const media::BitstreamBufferMetadata& metadata) { |
| DVLOG(2) << __func__ << " bitstream_buffer_id=" << bitstream_buffer_id |
| << ", payload_size=" << metadata.payload_size_bytes |
| << "B, key_frame=" << metadata.key_frame; |
| client_->BitstreamBufferReady(bitstream_buffer_id, metadata); |
| } |
| |
| void VideoEncodeAcceleratorClient::NotifyError( |
| VideoEncodeAccelerator::Error error) { |
| DVLOG(2) << __func__; |
| client_->NotifyError(error); |
| } |
| |
| } // anonymous namespace |
| |
| MojoVideoEncodeAccelerator::MojoVideoEncodeAccelerator( |
| mojom::VideoEncodeAcceleratorPtr vea, |
| const gpu::VideoEncodeAcceleratorSupportedProfiles& supported_profiles) |
| : vea_(std::move(vea)), supported_profiles_(supported_profiles) { |
| DVLOG(1) << __func__; |
| DCHECK(vea_); |
| } |
| |
| VideoEncodeAccelerator::SupportedProfiles |
| MojoVideoEncodeAccelerator::GetSupportedProfiles() { |
| DVLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| return GpuVideoAcceleratorUtil::ConvertGpuToMediaEncodeProfiles( |
| supported_profiles_); |
| } |
| |
| bool MojoVideoEncodeAccelerator::Initialize(const Config& config, |
| Client* client) { |
| DVLOG(2) << __func__ << " " << config.AsHumanReadableString(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!client) |
| return false; |
| |
| // Get a mojom::VideoEncodeAcceleratorClient bound to a local implementation |
| // (VideoEncodeAcceleratorClient) and send the pointer remotely. |
| mojom::VideoEncodeAcceleratorClientPtr vea_client_ptr; |
| vea_client_ = std::make_unique<VideoEncodeAcceleratorClient>( |
| client, mojo::MakeRequest(&vea_client_ptr)); |
| |
| bool result = false; |
| vea_->Initialize(config, std::move(vea_client_ptr), &result); |
| return result; |
| } |
| |
| void MojoVideoEncodeAccelerator::Encode(const scoped_refptr<VideoFrame>& frame, |
| bool force_keyframe) { |
| DVLOG(2) << __func__ << " tstamp=" << frame->timestamp(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_EQ(PIXEL_FORMAT_I420, frame->format()); |
| DCHECK_EQ(VideoFrame::STORAGE_SHMEM, frame->storage_type()); |
| DCHECK(frame->shared_memory_handle().IsValid()); |
| |
| // Oftentimes |frame|'s underlying planes will be aligned and not tightly |
| // packed, so don't use VideoFrame::AllocationSize(). |
| const size_t allocation_size = frame->shared_memory_handle().GetSize(); |
| |
| // WrapSharedMemoryHandle() takes ownership of the handle passed to it, but we |
| // don't have ownership of frame->shared_memory_handle(), so Duplicate() it. |
| // |
| // TODO(https://crbug.com/793446): This should be changed to wrap the frame |
| // buffer handle as read-only, but VideoFrame does not seem to guarantee that |
| // its shared_memory_handle() is in fact read-only. |
| mojo::ScopedSharedBufferHandle handle = mojo::WrapSharedMemoryHandle( |
| frame->shared_memory_handle().Duplicate(), allocation_size, |
| mojo::UnwrappedSharedMemoryHandleProtection::kReadWrite); |
| |
| const size_t y_offset = frame->shared_memory_offset(); |
| const size_t u_offset = y_offset + frame->data(VideoFrame::kUPlane) - |
| frame->data(VideoFrame::kYPlane); |
| const size_t v_offset = y_offset + frame->data(VideoFrame::kVPlane) - |
| frame->data(VideoFrame::kYPlane); |
| // Temporary Mojo VideoFrame to allow for marshalling. |
| scoped_refptr<MojoSharedBufferVideoFrame> mojo_frame = |
| MojoSharedBufferVideoFrame::Create( |
| frame->format(), frame->coded_size(), frame->visible_rect(), |
| frame->natural_size(), std::move(handle), allocation_size, y_offset, |
| u_offset, v_offset, frame->stride(VideoFrame::kYPlane), |
| frame->stride(VideoFrame::kUPlane), |
| frame->stride(VideoFrame::kVPlane), frame->timestamp()); |
| |
| // Encode() is synchronous: clients will assume full ownership of |frame| when |
| // this gets destroyed and probably recycle its shared_memory_handle(): keep |
| // the former alive until the remote end is actually finished. |
| DCHECK(vea_.is_bound()); |
| vea_->Encode(mojo_frame, force_keyframe, |
| base::Bind(&KeepVideoFrameAlive, frame)); |
| } |
| |
| void MojoVideoEncodeAccelerator::UseOutputBitstreamBuffer( |
| const BitstreamBuffer& buffer) { |
| DVLOG(2) << __func__ << " buffer.id()= " << buffer.id() |
| << " buffer.size()= " << buffer.size() << "B"; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| DCHECK(buffer.handle().IsValid()); |
| |
| // TODO(https://crbug.com/793446): Only wrap read-only handles here and change |
| // the protection status to kReadOnly. |
| mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle( |
| buffer.handle().Duplicate(), buffer.size(), |
| mojo::UnwrappedSharedMemoryHandleProtection::kReadWrite); |
| |
| vea_->UseOutputBitstreamBuffer(buffer.id(), std::move(buffer_handle)); |
| } |
| |
| void MojoVideoEncodeAccelerator::RequestEncodingParametersChange( |
| uint32_t bitrate, |
| uint32_t framerate) { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(vea_.is_bound()); |
| |
| media::VideoBitrateAllocation bitrate_allocation; |
| bitrate_allocation.SetBitrate(0, 0, bitrate); |
| vea_->RequestEncodingParametersChange(bitrate_allocation, framerate); |
| } |
| |
| void MojoVideoEncodeAccelerator::RequestEncodingParametersChange( |
| const VideoBitrateAllocation& bitrate, |
| uint32_t framerate) { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(vea_.is_bound()); |
| |
| vea_->RequestEncodingParametersChange(bitrate, framerate); |
| } |
| |
| void MojoVideoEncodeAccelerator::Destroy() { |
| DVLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| vea_client_.reset(); |
| vea_.reset(); |
| // See media::VideoEncodeAccelerator for more info on this peculiar pattern. |
| delete this; |
| } |
| |
| MojoVideoEncodeAccelerator::~MojoVideoEncodeAccelerator() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| } // namespace media |