OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/gpu/gpu_arc_video_encode_accelerator.h" |
| 6 |
| 7 #include <utility> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/sys_info.h" |
| 11 #include "media/base/video_types.h" |
| 12 #include "media/gpu/gpu_video_encode_accelerator_factory.h" |
| 13 #include "mojo/public/cpp/bindings/strong_binding.h" |
| 14 #include "mojo/public/cpp/bindings/type_converter.h" |
| 15 #include "mojo/public/cpp/system/platform_handle.h" |
| 16 |
| 17 namespace mojo { |
| 18 |
| 19 // Make sure arc::mojom::VideoEncodeAccelerator::Error and |
| 20 // media::VideoEncodeAccelerator::Error match. |
| 21 #define CHECK_ERROR_ENUM(value) \ |
| 22 static_assert( \ |
| 23 static_cast<int>(arc::mojom::VideoEncodeAccelerator::Error::value) == \ |
| 24 media::VideoEncodeAccelerator::Error::value, \ |
| 25 "enum ##value mismatch") |
| 26 |
| 27 CHECK_ERROR_ENUM(kIllegalStateError); |
| 28 CHECK_ERROR_ENUM(kInvalidArgumentError); |
| 29 CHECK_ERROR_ENUM(kPlatformFailureError); |
| 30 CHECK_ERROR_ENUM(kErrorMax); |
| 31 |
| 32 #undef CHECK_ERROR_ENUM |
| 33 |
| 34 // Make sure values in arc::mojom::VideoPixelFormat match to the values in |
| 35 // media::VideoPixelFormat. The former is a subset of the later. |
| 36 #define CHECK_PIXEL_FORMAT_ENUM(value) \ |
| 37 static_assert( \ |
| 38 static_cast<int>(arc::mojom::VideoPixelFormat::value) == media::value, \ |
| 39 "enum ##value mismatch") |
| 40 |
| 41 CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_I420); |
| 42 |
| 43 #undef CHECK_PXIEL_FORMAT_ENUM |
| 44 |
| 45 // Make sure values in arc::mojom::VideoCodecProfile match to the values in |
| 46 // media::VideoCodecProfile. |
| 47 #define CHECK_PROFILE_ENUM(value) \ |
| 48 static_assert( \ |
| 49 static_cast<int>(arc::mojom::VideoCodecProfile::value) == media::value, \ |
| 50 "enum ##value mismatch") |
| 51 |
| 52 CHECK_PROFILE_ENUM(H264PROFILE_BASELINE); |
| 53 CHECK_PROFILE_ENUM(H264PROFILE_MAIN); |
| 54 CHECK_PROFILE_ENUM(H264PROFILE_EXTENDED); |
| 55 CHECK_PROFILE_ENUM(H264PROFILE_HIGH); |
| 56 CHECK_PROFILE_ENUM(H264PROFILE_HIGH10PROFILE); |
| 57 CHECK_PROFILE_ENUM(H264PROFILE_HIGH422PROFILE); |
| 58 CHECK_PROFILE_ENUM(H264PROFILE_HIGH444PREDICTIVEPROFILE); |
| 59 CHECK_PROFILE_ENUM(H264PROFILE_SCALABLEBASELINE); |
| 60 CHECK_PROFILE_ENUM(H264PROFILE_SCALABLEHIGH); |
| 61 CHECK_PROFILE_ENUM(H264PROFILE_STEREOHIGH); |
| 62 CHECK_PROFILE_ENUM(H264PROFILE_MULTIVIEWHIGH); |
| 63 CHECK_PROFILE_ENUM(VP8PROFILE_ANY); |
| 64 CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE0); |
| 65 CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE1); |
| 66 CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE2); |
| 67 CHECK_PROFILE_ENUM(VP9PROFILE_PROFILE3); |
| 68 |
| 69 #undef CHECK_PROFILE_ENUM |
| 70 |
| 71 template <> |
| 72 struct TypeConverter<arc::mojom::VideoEncodeProfilePtr, |
| 73 media::VideoEncodeAccelerator::SupportedProfile> { |
| 74 static arc::mojom::VideoEncodeProfilePtr Convert( |
| 75 media::VideoEncodeAccelerator::SupportedProfile input) { |
| 76 auto result = arc::mojom::VideoEncodeProfile::New(); |
| 77 result->profile = static_cast<arc::mojom::VideoCodecProfile>(input.profile); |
| 78 result->max_resolution = input.max_resolution; |
| 79 result->max_framerate_numerator = input.max_framerate_numerator; |
| 80 result->max_framerate_denominator = input.max_framerate_denominator; |
| 81 return result; |
| 82 } |
| 83 }; |
| 84 |
| 85 } // namespace mojo |
| 86 |
| 87 namespace chromeos { |
| 88 namespace arc { |
| 89 |
| 90 GpuArcVideoEncodeAccelerator::GpuArcVideoEncodeAccelerator( |
| 91 const gpu::GpuPreferences& gpu_preferences) |
| 92 : gpu_preferences_(gpu_preferences) {} |
| 93 |
| 94 GpuArcVideoEncodeAccelerator::~GpuArcVideoEncodeAccelerator() = default; |
| 95 |
| 96 // VideoEncodeAccelerator::Client implementation. |
| 97 void GpuArcVideoEncodeAccelerator::RequireBitstreamBuffers( |
| 98 unsigned int input_count, |
| 99 const gfx::Size& coded_size, |
| 100 size_t output_buffer_size) { |
| 101 DVLOG(2) << "RequireBitstreamBuffers(input_count=" << input_count |
| 102 << ", coded_size=" << coded_size.ToString() |
| 103 << ", output_buffer_size=" << output_buffer_size << ")"; |
| 104 DCHECK(client_); |
| 105 coded_size_ = coded_size; |
| 106 client_->RequireBitstreamBuffers(input_count, coded_size, output_buffer_size); |
| 107 } |
| 108 |
| 109 void GpuArcVideoEncodeAccelerator::BitstreamBufferReady( |
| 110 int32_t bitstream_buffer_id, |
| 111 size_t payload_size, |
| 112 bool key_frame, |
| 113 base::TimeDelta timestamp) { |
| 114 DVLOG(2) << "BitstreamBufferReady(id=" << bitstream_buffer_id << ")"; |
| 115 DCHECK(client_); |
| 116 client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame, |
| 117 timestamp.InMicroseconds()); |
| 118 } |
| 119 |
| 120 void GpuArcVideoEncodeAccelerator::NotifyError( |
| 121 media::VideoEncodeAccelerator::Error error) { |
| 122 DVLOG(2) << "NotifyError(" << error << ")"; |
| 123 DCHECK(client_); |
| 124 client_->NotifyError(static_cast<VideoEncodeAccelerator::Error>(error)); |
| 125 } |
| 126 |
| 127 // ::arc::mojom::VideoEncodeAccelerator implementation. |
| 128 void GpuArcVideoEncodeAccelerator::GetSupportedProfiles( |
| 129 const GetSupportedProfilesCallback& callback) { |
| 130 std::vector<::arc::mojom::VideoEncodeProfilePtr> profiles; |
| 131 for (auto p : media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles( |
| 132 gpu_preferences_)) |
| 133 profiles.push_back(::arc::mojom::VideoEncodeProfile::From(p)); |
| 134 callback.Run(std::move(profiles)); |
| 135 } |
| 136 |
| 137 void GpuArcVideoEncodeAccelerator::Initialize( |
| 138 VideoPixelFormat input_format, |
| 139 const gfx::Size& visible_size, |
| 140 VideoEncodeAccelerator::StorageType input_storage, |
| 141 VideoCodecProfile output_profile, |
| 142 uint32_t initial_bitrate, |
| 143 VideoEncodeClientPtr client, |
| 144 const InitializeCallback& callback) { |
| 145 DVLOG(2) << "Initialize()"; |
| 146 |
| 147 if (!::arc::mojom::IsKnownEnumValue(input_format)) { |
| 148 DLOG(ERROR) << "Invalid input_format: " << input_format; |
| 149 callback.Run(false); |
| 150 return; |
| 151 } |
| 152 if (!::arc::mojom::IsKnownEnumValue(output_profile)) { |
| 153 DLOG(ERROR) << "Invalid codec profile: " << output_profile; |
| 154 callback.Run(false); |
| 155 return; |
| 156 } |
| 157 if (!client) { |
| 158 DLOG(ERROR) << "Invalid client"; |
| 159 callback.Run(false); |
| 160 return; |
| 161 } |
| 162 |
| 163 input_pixel_format_ = static_cast<media::VideoPixelFormat>(input_format); |
| 164 visible_size_ = visible_size; |
| 165 accelerator_ = media::GpuVideoEncodeAcceleratorFactory::CreateVEA( |
| 166 input_pixel_format_, visible_size_, |
| 167 static_cast<media::VideoCodecProfile>(output_profile), initial_bitrate, |
| 168 this, gpu_preferences_); |
| 169 if (accelerator_ == nullptr) { |
| 170 DLOG(ERROR) << "failed to create vea."; |
| 171 callback.Run(false); |
| 172 return; |
| 173 } |
| 174 client_ = std::move(client); |
| 175 callback.Run(true); |
| 176 } |
| 177 |
| 178 static void DropSharedMemory(std::unique_ptr<base::SharedMemory> shm) { |
| 179 // Just let |shm| fall out of scope. |
| 180 } |
| 181 |
| 182 void GpuArcVideoEncodeAccelerator::Encode( |
| 183 mojo::ScopedHandle handle, |
| 184 std::vector<::arc::VideoFramePlane> planes, |
| 185 int64_t timestamp, |
| 186 bool force_keyframe) { |
| 187 DVLOG(2) << "Encode(timestamp=" << timestamp << ")"; |
| 188 if (!accelerator_) { |
| 189 DLOG(ERROR) << "Accelerator not initialized"; |
| 190 return; |
| 191 } |
| 192 |
| 193 scoped_refptr<media::VideoFrame> frame; |
| 194 if (planes.empty()) { // EOS |
| 195 frame = media::VideoFrame::CreateEOSFrame(); |
| 196 } else { |
| 197 base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(handle)); |
| 198 if (!fd.is_valid()) |
| 199 return; |
| 200 |
| 201 size_t allocation_size = |
| 202 media::VideoFrame::AllocationSize(input_pixel_format_, coded_size_); |
| 203 |
| 204 // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. |
| 205 // TODO(rockot): This fd comes from a mojo::ScopedHandle in |
| 206 // GpuArcVideoService::BindSharedMemory. That should be passed through, |
| 207 // rather than pulling out the fd. https://crbug.com/713763. |
| 208 // TODO(rockot): Pass through a real size rather than |0|. |
| 209 base::UnguessableToken guid = base::UnguessableToken::Create(); |
| 210 base::SharedMemoryHandle shm_handle( |
| 211 base::FileDescriptor(fd.release(), true), 0u, guid); |
| 212 auto shm = base::MakeUnique<base::SharedMemory>(shm_handle, true); |
| 213 |
| 214 base::CheckedNumeric<off_t> map_offset = planes[0].offset; |
| 215 base::CheckedNumeric<size_t> map_size = allocation_size; |
| 216 const uint32_t aligned_offset = |
| 217 planes[0].offset % base::SysInfo::VMAllocationGranularity(); |
| 218 map_offset -= aligned_offset; |
| 219 map_size += aligned_offset; |
| 220 |
| 221 if (!map_offset.IsValid() || !map_size.IsValid()) { |
| 222 DLOG(ERROR) << "Invalid map_offset or map_size"; |
| 223 client_->NotifyError(Error::kInvalidArgumentError); |
| 224 return; |
| 225 } |
| 226 if (!shm->MapAt(map_offset.ValueOrDie(), map_size.ValueOrDie())) { |
| 227 DLOG(ERROR) << "Could not map memroy"; |
| 228 client_->NotifyError(Error::kPlatformFailureError); |
| 229 return; |
| 230 } |
| 231 |
| 232 uint8_t* shm_memory = reinterpret_cast<uint8_t*>(shm->memory()); |
| 233 frame = media::VideoFrame::WrapExternalSharedMemory( |
| 234 input_pixel_format_, coded_size_, gfx::Rect(visible_size_), |
| 235 visible_size_, shm_memory + aligned_offset, allocation_size, shm_handle, |
| 236 planes[0].offset, base::TimeDelta::FromMicroseconds(timestamp)); |
| 237 |
| 238 // Wrap |shm| in a callback and add it as a destruction observer, so it |
| 239 // stays alive and mapped until |frame| goes out of scope. |
| 240 frame->AddDestructionObserver( |
| 241 base::Bind(&DropSharedMemory, base::Passed(&shm))); |
| 242 } |
| 243 accelerator_->Encode(frame, force_keyframe); |
| 244 } |
| 245 |
| 246 void GpuArcVideoEncodeAccelerator::UseOutputBitstreamBuffer( |
| 247 int32_t bitstream_buffer_id, |
| 248 mojo::ScopedHandle shmem_fd, |
| 249 uint32_t offset, |
| 250 uint32_t size) { |
| 251 DVLOG(2) << "UseOutputBitstreamBuffer(id=" << bitstream_buffer_id << ")"; |
| 252 if (!accelerator_) { |
| 253 DLOG(ERROR) << "Accelerator not initialized"; |
| 254 return; |
| 255 } |
| 256 |
| 257 base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(shmem_fd)); |
| 258 if (!fd.is_valid()) |
| 259 return; |
| 260 |
| 261 // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. |
| 262 // TODO(rockot): This fd comes from a mojo::ScopedHandle in |
| 263 // GpuArcVideoService::BindSharedMemory. That should be passed through, |
| 264 // rather than pulling out the fd. https://crbug.com/713763. |
| 265 // TODO(rockot): Pass through a real size rather than |0|. |
| 266 base::UnguessableToken guid = base::UnguessableToken::Create(); |
| 267 base::SharedMemoryHandle shm_handle(base::FileDescriptor(fd.release(), true), |
| 268 0u, guid); |
| 269 accelerator_->UseOutputBitstreamBuffer( |
| 270 media::BitstreamBuffer(bitstream_buffer_id, shm_handle, size, offset)); |
| 271 } |
| 272 |
| 273 void GpuArcVideoEncodeAccelerator::RequestEncodingParametersChange( |
| 274 uint32_t bitrate, |
| 275 uint32_t framerate) { |
| 276 DVLOG(2) << "RequestEncodingParametersChange(" << bitrate << ", " << framerate |
| 277 << ")"; |
| 278 if (!accelerator_) { |
| 279 DLOG(ERROR) << "Accelerator not initialized"; |
| 280 return; |
| 281 } |
| 282 accelerator_->RequestEncodingParametersChange(bitrate, framerate); |
| 283 } |
| 284 |
| 285 base::ScopedFD GpuArcVideoEncodeAccelerator::UnwrapFdFromMojoHandle( |
| 286 mojo::ScopedHandle handle) { |
| 287 DCHECK(client_); |
| 288 if (!handle.is_valid()) { |
| 289 DLOG(ERROR) << "handle is invalid"; |
| 290 client_->NotifyError(Error::kInvalidArgumentError); |
| 291 return base::ScopedFD(); |
| 292 } |
| 293 |
| 294 base::PlatformFile platform_file; |
| 295 MojoResult mojo_result = |
| 296 mojo::UnwrapPlatformFile(std::move(handle), &platform_file); |
| 297 if (mojo_result != MOJO_RESULT_OK) { |
| 298 DLOG(ERROR) << "UnwrapPlatformFile failed: " << mojo_result; |
| 299 client_->NotifyError(Error::kPlatformFailureError); |
| 300 return base::ScopedFD(); |
| 301 } |
| 302 |
| 303 return base::ScopedFD(platform_file); |
| 304 } |
| 305 |
| 306 } // namespace arc |
| 307 } // namespace chromeos |
OLD | NEW |