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, \ | |
Luis Héctor Chávez
2017/05/19 15:14:44
Why not make a typemap for this? You also get comp
Owen Lin
2017/06/03 13:32:32
Done.
| |
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, | |
Luis Héctor Chávez
2017/05/19 15:14:44
same here, TypeConverters are discouraged in new c
Owen Lin
2017/06/03 13:32:32
Done.
| |
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& input_coded_size, | |
100 size_t output_buffer_size) { | |
101 DVLOG(2) << "RequireBitstreamBuffers(input_count=" << input_count | |
102 << ", input_coded_size=" << input_coded_size.ToString() | |
103 << ", output_buffer_size=" << output_buffer_size << ")"; | |
104 DCHECK(client_); | |
105 input_coded_size_ = input_coded_size; | |
106 client_->RequireBitstreamBuffers(input_count, input_coded_size, | |
107 output_buffer_size); | |
108 } | |
109 | |
110 void GpuArcVideoEncodeAccelerator::BitstreamBufferReady( | |
111 int32_t bitstream_buffer_id, | |
112 size_t payload_size, | |
113 bool key_frame, | |
114 base::TimeDelta timestamp) { | |
115 DVLOG(2) << "BitstreamBufferReady(id=" << bitstream_buffer_id << ")"; | |
116 DCHECK(client_); | |
117 client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame, | |
118 timestamp.InMicroseconds()); | |
119 } | |
120 | |
121 void GpuArcVideoEncodeAccelerator::NotifyError( | |
122 media::VideoEncodeAccelerator::Error error) { | |
123 DVLOG(2) << "NotifyError(" << error << ")"; | |
124 DCHECK(client_); | |
125 client_->NotifyError(static_cast<VideoEncodeAccelerator::Error>(error)); | |
126 } | |
127 | |
128 // ::arc::mojom::VideoEncodeAccelerator implementation. | |
129 void GpuArcVideoEncodeAccelerator::GetSupportedProfiles( | |
130 const GetSupportedProfilesCallback& callback) { | |
131 std::vector<::arc::mojom::VideoEncodeProfilePtr> profiles; | |
132 for (auto p : media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles( | |
133 gpu_preferences_)) | |
Luis Héctor Chávez
2017/05/19 15:14:44
nit: you cannot elide braces if the 'for' does not
Owen Lin
2017/06/03 13:32:31
Code is modified.
| |
134 profiles.push_back(::arc::mojom::VideoEncodeProfile::From(p)); | |
135 callback.Run(std::move(profiles)); | |
136 } | |
137 | |
138 void GpuArcVideoEncodeAccelerator::Initialize( | |
139 VideoPixelFormat input_format, | |
140 const gfx::Size& visible_size, | |
141 VideoEncodeAccelerator::StorageType input_storage, | |
142 VideoCodecProfile output_profile, | |
143 uint32_t initial_bitrate, | |
144 VideoEncodeClientPtr client, | |
145 const InitializeCallback& callback) { | |
146 DVLOG(2) << "Initialize()"; | |
147 | |
148 if (!::arc::mojom::IsKnownEnumValue(input_format)) { | |
149 DLOG(ERROR) << "Invalid input_format: " << input_format; | |
150 callback.Run(false); | |
151 return; | |
152 } | |
153 if (!::arc::mojom::IsKnownEnumValue(output_profile)) { | |
154 DLOG(ERROR) << "Invalid codec profile: " << output_profile; | |
155 callback.Run(false); | |
156 return; | |
157 } | |
158 if (!client) { | |
159 DLOG(ERROR) << "Invalid client"; | |
160 callback.Run(false); | |
161 return; | |
162 } | |
163 | |
164 input_pixel_format_ = static_cast<media::VideoPixelFormat>(input_format); | |
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 const gfx::Size& visible_size, | |
186 int64_t timestamp, | |
187 bool force_keyframe) { | |
188 DVLOG(2) << "Encode(timestamp=" << timestamp << ")"; | |
189 if (!accelerator_) { | |
190 DLOG(ERROR) << "Accelerator not initialized"; | |
191 return; | |
192 } | |
193 | |
194 scoped_refptr<media::VideoFrame> frame; | |
195 if (planes.empty()) { // EOS | |
196 frame = media::VideoFrame::CreateEOSFrame(); | |
197 } else { | |
198 base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(handle)); | |
199 if (!fd.is_valid()) | |
200 return; | |
201 | |
202 size_t allocation_size = media::VideoFrame::AllocationSize( | |
203 input_pixel_format_, input_coded_size_); | |
204 | |
205 // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. | |
206 // TODO(rockot): This fd comes from a mojo::ScopedHandle in | |
207 // GpuArcVideoService::BindSharedMemory. That should be passed through, | |
208 // rather than pulling out the fd. https://crbug.com/713763. | |
209 // TODO(rockot): Pass through a real size rather than |0|. | |
210 base::UnguessableToken guid = base::UnguessableToken::Create(); | |
211 base::SharedMemoryHandle shm_handle( | |
212 base::FileDescriptor(fd.release(), true), 0u, guid); | |
213 auto shm = base::MakeUnique<base::SharedMemory>(shm_handle, true); | |
214 | |
215 base::CheckedNumeric<off_t> map_offset = planes[0].offset; | |
216 base::CheckedNumeric<size_t> map_size = allocation_size; | |
217 const uint32_t aligned_offset = | |
218 planes[0].offset % base::SysInfo::VMAllocationGranularity(); | |
219 map_offset -= aligned_offset; | |
220 map_size += aligned_offset; | |
221 | |
222 if (!map_offset.IsValid() || !map_size.IsValid()) { | |
223 DLOG(ERROR) << "Invalid map_offset or map_size"; | |
224 client_->NotifyError(Error::kInvalidArgumentError); | |
225 return; | |
226 } | |
227 if (!shm->MapAt(map_offset.ValueOrDie(), map_size.ValueOrDie())) { | |
228 DLOG(ERROR) << "Could not map memroy"; | |
229 client_->NotifyError(Error::kPlatformFailureError); | |
230 return; | |
231 } | |
232 | |
233 uint8_t* shm_memory = reinterpret_cast<uint8_t*>(shm->memory()); | |
234 frame = media::VideoFrame::WrapExternalSharedMemory( | |
235 input_pixel_format_, input_coded_size_, gfx::Rect(visible_size), | |
236 visible_size, shm_memory + aligned_offset, allocation_size, shm_handle, | |
237 planes[0].offset, base::TimeDelta::FromMicroseconds(timestamp)); | |
238 | |
239 // Wrap |shm| in a callback and add it as a destruction observer, so it | |
240 // stays alive and mapped until |frame| goes out of scope. | |
241 frame->AddDestructionObserver( | |
242 base::Bind(&DropSharedMemory, base::Passed(&shm))); | |
243 } | |
244 accelerator_->Encode(frame, force_keyframe); | |
245 } | |
246 | |
247 void GpuArcVideoEncodeAccelerator::UseOutputBitstreamBuffer( | |
248 int32_t bitstream_buffer_id, | |
249 mojo::ScopedHandle shmem_fd, | |
250 uint32_t offset, | |
251 uint32_t size) { | |
252 DVLOG(2) << "UseOutputBitstreamBuffer(id=" << bitstream_buffer_id << ")"; | |
253 if (!accelerator_) { | |
254 DLOG(ERROR) << "Accelerator not initialized"; | |
255 return; | |
256 } | |
257 | |
258 base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(shmem_fd)); | |
259 if (!fd.is_valid()) | |
260 return; | |
261 | |
262 // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. | |
263 // TODO(rockot): This fd comes from a mojo::ScopedHandle in | |
264 // GpuArcVideoService::BindSharedMemory. That should be passed through, | |
265 // rather than pulling out the fd. https://crbug.com/713763. | |
266 // TODO(rockot): Pass through a real size rather than |0|. | |
267 base::UnguessableToken guid = base::UnguessableToken::Create(); | |
268 base::SharedMemoryHandle shm_handle(base::FileDescriptor(fd.release(), true), | |
269 0u, guid); | |
270 accelerator_->UseOutputBitstreamBuffer( | |
271 media::BitstreamBuffer(bitstream_buffer_id, shm_handle, size, offset)); | |
272 } | |
273 | |
274 void GpuArcVideoEncodeAccelerator::RequestEncodingParametersChange( | |
275 uint32_t bitrate, | |
276 uint32_t framerate) { | |
277 DVLOG(2) << "RequestEncodingParametersChange(" << bitrate << ", " << framerate | |
278 << ")"; | |
279 if (!accelerator_) { | |
280 DLOG(ERROR) << "Accelerator not initialized"; | |
281 return; | |
282 } | |
283 accelerator_->RequestEncodingParametersChange(bitrate, framerate); | |
284 } | |
285 | |
286 base::ScopedFD GpuArcVideoEncodeAccelerator::UnwrapFdFromMojoHandle( | |
287 mojo::ScopedHandle handle) { | |
288 DCHECK(client_); | |
289 if (!handle.is_valid()) { | |
290 DLOG(ERROR) << "handle is invalid"; | |
291 client_->NotifyError(Error::kInvalidArgumentError); | |
292 return base::ScopedFD(); | |
293 } | |
294 | |
295 base::PlatformFile platform_file; | |
296 MojoResult mojo_result = | |
297 mojo::UnwrapPlatformFile(std::move(handle), &platform_file); | |
298 if (mojo_result != MOJO_RESULT_OK) { | |
299 DLOG(ERROR) << "UnwrapPlatformFile failed: " << mojo_result; | |
300 client_->NotifyError(Error::kPlatformFailureError); | |
301 return base::ScopedFD(); | |
302 } | |
303 | |
304 return base::ScopedFD(platform_file); | |
305 } | |
306 | |
307 } // namespace arc | |
308 } // namespace chromeos | |
OLD | NEW |