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 #define DVLOGF(x) DVLOG(x) << __func__ << "(): " | |
18 | |
19 namespace chromeos { | |
20 namespace arc { | |
21 | |
22 GpuArcVideoEncodeAccelerator::GpuArcVideoEncodeAccelerator( | |
23 const gpu::GpuPreferences& gpu_preferences) | |
24 : gpu_preferences_(gpu_preferences) {} | |
25 | |
26 GpuArcVideoEncodeAccelerator::~GpuArcVideoEncodeAccelerator() = default; | |
27 | |
28 // VideoEncodeAccelerator::Client implementation. | |
29 void GpuArcVideoEncodeAccelerator::RequireBitstreamBuffers( | |
30 unsigned int input_count, | |
31 const gfx::Size& coded_size, | |
32 size_t output_buffer_size) { | |
33 DVLOGF(2) << "input_count=" << input_count | |
34 << ", coded_size=" << coded_size.ToString() | |
35 << ", output_buffer_size=" << output_buffer_size; | |
36 DCHECK(client_); | |
37 coded_size_ = coded_size; | |
38 client_->RequireBitstreamBuffers(input_count, coded_size, output_buffer_size); | |
39 } | |
40 | |
41 void GpuArcVideoEncodeAccelerator::BitstreamBufferReady( | |
42 int32_t bitstream_buffer_id, | |
43 size_t payload_size, | |
44 bool key_frame, | |
45 base::TimeDelta timestamp) { | |
46 DVLOGF(2) << "id=" << bitstream_buffer_id; | |
47 DCHECK(client_); | |
48 client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame, | |
49 timestamp.InMicroseconds()); | |
50 } | |
51 | |
52 void GpuArcVideoEncodeAccelerator::NotifyError(Error error) { | |
53 DVLOGF(2) << "error=" << error; | |
54 DCHECK(client_); | |
55 client_->NotifyError(error); | |
56 } | |
57 | |
58 // ::arc::mojom::VideoEncodeAccelerator implementation. | |
59 void GpuArcVideoEncodeAccelerator::GetSupportedProfiles( | |
60 const GetSupportedProfilesCallback& callback) { | |
61 callback.Run(media::GpuVideoEncodeAcceleratorFactory::GetSupportedProfiles( | |
62 gpu_preferences_)); | |
63 } | |
64 | |
65 void GpuArcVideoEncodeAccelerator::Initialize( | |
66 VideoPixelFormat input_format, | |
67 const gfx::Size& visible_size, | |
68 VideoEncodeAccelerator::StorageType input_storage, | |
69 VideoCodecProfile output_profile, | |
70 uint32_t initial_bitrate, | |
71 VideoEncodeClientPtr client, | |
72 const InitializeCallback& callback) { | |
73 DVLOGF(2) << "visible_size=" << visible_size.ToString() | |
74 << ", profile=" << output_profile; | |
75 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
| |
76 LOG(ERROR) << "Invalid client"; | |
77 callback.Run(false); | |
78 return; | |
79 } | |
80 | |
81 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
| |
82 visible_size_ = visible_size; | |
83 accelerator_ = media::GpuVideoEncodeAcceleratorFactory::CreateVEA( | |
84 input_pixel_format_, visible_size_, | |
85 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.
| |
86 this, gpu_preferences_); | |
87 if (accelerator_ == nullptr) { | |
88 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
| |
89 callback.Run(false); | |
90 return; | |
91 } | |
92 client_ = std::move(client); | |
93 callback.Run(true); | |
94 } | |
95 | |
96 static void DropSharedMemory(std::unique_ptr<base::SharedMemory> shm) { | |
97 // Just let |shm| fall out of scope. | |
98 } | |
99 | |
100 void GpuArcVideoEncodeAccelerator::Encode( | |
101 mojo::ScopedHandle handle, | |
102 std::vector<::arc::VideoFramePlane> planes, | |
103 int64_t timestamp, | |
104 bool force_keyframe) { | |
105 DVLOGF(2) << "timestamp=" << timestamp; | |
106 if (!accelerator_) { | |
107 LOG(ERROR) << "Accelerator is not initialized."; | |
108 return; | |
109 } | |
110 | |
111 if (planes.empty()) { // EOS | |
112 accelerator_->Encode(media::VideoFrame::CreateEOSFrame(), force_keyframe); | |
113 return; | |
114 } | |
115 | |
116 base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(handle)); | |
117 if (!fd.is_valid()) | |
118 return; | |
119 | |
120 size_t allocation_size = | |
121 media::VideoFrame::AllocationSize(input_pixel_format_, coded_size_); | |
122 | |
123 // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. | |
124 // TODO(rockot): This fd comes from a mojo::ScopedHandle in | |
125 // GpuArcVideoService::BindSharedMemory. That should be passed through, | |
126 // rather than pulling out the fd. https://crbug.com/713763. | |
127 // TODO(rockot): Pass through a real size rather than |0|. | |
128 base::UnguessableToken guid = base::UnguessableToken::Create(); | |
129 base::SharedMemoryHandle shm_handle(base::FileDescriptor(fd.release(), true), | |
130 0u, guid); | |
131 auto shm = base::MakeUnique<base::SharedMemory>(shm_handle, true); | |
132 | |
133 base::CheckedNumeric<off_t> map_offset = planes[0].offset; | |
134 base::CheckedNumeric<size_t> map_size = allocation_size; | |
135 const uint32_t aligned_offset = | |
136 planes[0].offset % base::SysInfo::VMAllocationGranularity(); | |
137 map_offset -= aligned_offset; | |
138 map_size += aligned_offset; | |
139 | |
140 if (!map_offset.IsValid() || !map_size.IsValid()) { | |
141 LOG(ERROR) << "Invalid map_offset or map_size"; | |
142 client_->NotifyError(Error::kInvalidArgumentError); | |
143 return; | |
144 } | |
145 if (!shm->MapAt(map_offset.ValueOrDie(), map_size.ValueOrDie())) { | |
146 LOG(ERROR) << "Failed to map memory."; | |
147 client_->NotifyError(Error::kPlatformFailureError); | |
148 return; | |
149 } | |
150 | |
151 uint8_t* shm_memory = reinterpret_cast<uint8_t*>(shm->memory()); | |
152 auto frame = media::VideoFrame::WrapExternalSharedMemory( | |
153 input_pixel_format_, coded_size_, gfx::Rect(visible_size_), visible_size_, | |
154 shm_memory + aligned_offset, allocation_size, shm_handle, | |
155 planes[0].offset, base::TimeDelta::FromMicroseconds(timestamp)); | |
156 | |
157 // Wrap |shm| in a callback and add it as a destruction observer, so it | |
158 // stays alive and mapped until |frame| goes out of scope. | |
159 frame->AddDestructionObserver( | |
160 base::Bind(&DropSharedMemory, base::Passed(&shm))); | |
161 accelerator_->Encode(frame, force_keyframe); | |
162 } | |
163 | |
164 void GpuArcVideoEncodeAccelerator::UseOutputBitstreamBuffer( | |
165 int32_t bitstream_buffer_id, | |
166 mojo::ScopedHandle shmem_fd, | |
167 uint32_t offset, | |
168 uint32_t size) { | |
169 DVLOGF(2) << "id=" << bitstream_buffer_id; | |
170 if (!accelerator_) { | |
171 LOG(ERROR) << "Accelerator is not initialized."; | |
172 return; | |
173 } | |
174 | |
175 base::ScopedFD fd = UnwrapFdFromMojoHandle(std::move(shmem_fd)); | |
176 if (!fd.is_valid()) | |
177 return; | |
178 | |
179 // TODO(rockot): Pass GUIDs through Mojo. https://crbug.com/713763. | |
180 // TODO(rockot): This fd comes from a mojo::ScopedHandle in | |
181 // GpuArcVideoService::BindSharedMemory. That should be passed through, | |
182 // rather than pulling out the fd. https://crbug.com/713763. | |
183 // TODO(rockot): Pass through a real size rather than |0|. | |
184 base::UnguessableToken guid = base::UnguessableToken::Create(); | |
185 base::SharedMemoryHandle shm_handle(base::FileDescriptor(fd.release(), true), | |
186 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
| |
187 accelerator_->UseOutputBitstreamBuffer( | |
188 media::BitstreamBuffer(bitstream_buffer_id, shm_handle, size, offset)); | |
189 } | |
190 | |
191 void GpuArcVideoEncodeAccelerator::RequestEncodingParametersChange( | |
192 uint32_t bitrate, | |
193 uint32_t framerate) { | |
194 DVLOGF(2) << "bitrate=" << bitrate << ", framerate=" << framerate; | |
195 if (!accelerator_) { | |
196 LOG(ERROR) << "Accelerator is not initialized."; | |
197 return; | |
198 } | |
199 accelerator_->RequestEncodingParametersChange(bitrate, framerate); | |
200 } | |
201 | |
202 base::ScopedFD GpuArcVideoEncodeAccelerator::UnwrapFdFromMojoHandle( | |
203 mojo::ScopedHandle handle) { | |
204 DCHECK(client_); | |
205 if (!handle.is_valid()) { | |
206 LOG(ERROR) << "handle is invalid."; | |
207 client_->NotifyError(Error::kInvalidArgumentError); | |
208 return base::ScopedFD(); | |
209 } | |
210 | |
211 base::PlatformFile platform_file; | |
212 MojoResult mojo_result = | |
213 mojo::UnwrapPlatformFile(std::move(handle), &platform_file); | |
214 if (mojo_result != MOJO_RESULT_OK) { | |
215 LOG(ERROR) << "UnwrapPlatformFile failed: " << mojo_result; | |
216 client_->NotifyError(Error::kPlatformFailureError); | |
217 return base::ScopedFD(); | |
218 } | |
219 | |
220 return base::ScopedFD(platform_file); | |
221 } | |
222 | |
223 } // namespace arc | |
224 } // namespace chromeos | |
OLD | NEW |