OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "content/common/gpu/media/gpu_jpeg_decode_accelerator.h" | |
6 | |
7 #include <stdint.h> | |
8 #include <utility> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/containers/hash_tables.h" | |
12 #include "base/logging.h" | |
13 #include "base/memory/shared_memory.h" | |
14 #include "base/single_thread_task_runner.h" | |
15 #include "base/stl_util.h" | |
16 #include "base/thread_task_runner_handle.h" | |
17 #include "base/trace_event/trace_event.h" | |
18 #include "build/build_config.h" | |
19 #include "gpu/ipc/service/gpu_channel.h" | |
20 #include "ipc/ipc_message_macros.h" | |
21 #include "ipc/message_filter.h" | |
22 #include "media/filters/jpeg_parser.h" | |
23 #include "media/gpu/ipc/common/media_messages.h" | |
24 #include "ui/gfx/geometry/size.h" | |
25 | |
26 #if defined(OS_CHROMEOS) | |
27 #if defined(ARCH_CPU_X86_FAMILY) | |
28 #include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h" | |
29 #endif | |
30 #if defined(USE_V4L2_CODEC) | |
31 #include "content/common/gpu/media/v4l2_device.h" | |
32 #include "content/common/gpu/media/v4l2_jpeg_decode_accelerator.h" | |
33 #endif | |
34 #endif | |
35 | |
36 namespace { | |
37 | |
38 void DecodeFinished(std::unique_ptr<base::SharedMemory> shm) { | |
39 // Do nothing. Because VideoFrame is backed by |shm|, the purpose of this | |
40 // function is to just keep reference of |shm| to make sure it lives util | |
41 // decode finishes. | |
42 } | |
43 | |
44 bool VerifyDecodeParams(const AcceleratedJpegDecoderMsg_Decode_Params& params) { | |
45 const int kJpegMaxDimension = UINT16_MAX; | |
46 if (params.coded_size.IsEmpty() || | |
47 params.coded_size.width() > kJpegMaxDimension || | |
48 params.coded_size.height() > kJpegMaxDimension) { | |
49 LOG(ERROR) << "invalid coded_size " << params.coded_size.ToString(); | |
50 return false; | |
51 } | |
52 | |
53 if (!base::SharedMemory::IsHandleValid(params.output_video_frame_handle)) { | |
54 LOG(ERROR) << "invalid output_video_frame_handle"; | |
55 return false; | |
56 } | |
57 | |
58 if (params.output_buffer_size < | |
59 media::VideoFrame::AllocationSize(media::PIXEL_FORMAT_I420, | |
60 params.coded_size)) { | |
61 LOG(ERROR) << "output_buffer_size is too small: " | |
62 << params.output_buffer_size; | |
63 return false; | |
64 } | |
65 | |
66 return true; | |
67 } | |
68 | |
69 } // namespace | |
70 | |
71 namespace content { | |
72 | |
73 class GpuJpegDecodeAccelerator::Client | |
74 : public media::JpegDecodeAccelerator::Client, | |
75 public base::NonThreadSafe { | |
76 public: | |
77 Client(content::GpuJpegDecodeAccelerator* owner, int32_t route_id) | |
78 : owner_(owner->AsWeakPtr()), route_id_(route_id) {} | |
79 | |
80 ~Client() override { DCHECK(CalledOnValidThread()); } | |
81 | |
82 // media::JpegDecodeAccelerator::Client implementation. | |
83 void VideoFrameReady(int32_t bitstream_buffer_id) override { | |
84 DCHECK(CalledOnValidThread()); | |
85 if (owner_) | |
86 owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id, | |
87 media::JpegDecodeAccelerator::NO_ERRORS); | |
88 } | |
89 | |
90 void NotifyError(int32_t bitstream_buffer_id, | |
91 media::JpegDecodeAccelerator::Error error) override { | |
92 DCHECK(CalledOnValidThread()); | |
93 if (owner_) | |
94 owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id, error); | |
95 } | |
96 | |
97 void Decode(const media::BitstreamBuffer& bitstream_buffer, | |
98 const scoped_refptr<media::VideoFrame>& video_frame) { | |
99 DCHECK(CalledOnValidThread()); | |
100 DCHECK(accelerator_); | |
101 accelerator_->Decode(bitstream_buffer, video_frame); | |
102 } | |
103 | |
104 void set_accelerator( | |
105 std::unique_ptr<media::JpegDecodeAccelerator> accelerator) { | |
106 DCHECK(CalledOnValidThread()); | |
107 accelerator_ = std::move(accelerator); | |
108 } | |
109 | |
110 private: | |
111 base::WeakPtr<content::GpuJpegDecodeAccelerator> owner_; | |
112 int32_t route_id_; | |
113 std::unique_ptr<media::JpegDecodeAccelerator> accelerator_; | |
114 }; | |
115 | |
116 // Create, destroy, and RemoveClient run on child thread. All other methods run | |
117 // on IO thread. | |
118 class GpuJpegDecodeAccelerator::MessageFilter : public IPC::MessageFilter { | |
119 public: | |
120 explicit MessageFilter(GpuJpegDecodeAccelerator* owner) | |
121 : owner_(owner->AsWeakPtr()), | |
122 child_task_runner_(owner_->child_task_runner_), | |
123 io_task_runner_(owner_->io_task_runner_) {} | |
124 | |
125 void OnChannelError() override { sender_ = nullptr; } | |
126 | |
127 void OnChannelClosing() override { sender_ = nullptr; } | |
128 | |
129 void OnFilterAdded(IPC::Sender* sender) override { sender_ = sender; } | |
130 | |
131 bool OnMessageReceived(const IPC::Message& msg) override { | |
132 const int32_t route_id = msg.routing_id(); | |
133 if (client_map_.find(route_id) == client_map_.end()) | |
134 return false; | |
135 | |
136 bool handled = true; | |
137 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MessageFilter, msg, &route_id) | |
138 IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Decode, OnDecodeOnIOThread) | |
139 IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Destroy, | |
140 OnDestroyOnIOThread) | |
141 IPC_MESSAGE_UNHANDLED(handled = false) | |
142 IPC_END_MESSAGE_MAP() | |
143 return handled; | |
144 } | |
145 | |
146 bool SendOnIOThread(IPC::Message* message) { | |
147 DCHECK(!message->is_sync()); | |
148 if (!sender_) { | |
149 delete message; | |
150 return false; | |
151 } | |
152 return sender_->Send(message); | |
153 } | |
154 | |
155 void AddClientOnIOThread(int32_t route_id, | |
156 Client* client, | |
157 base::Callback<void(bool)> response) { | |
158 DCHECK(io_task_runner_->BelongsToCurrentThread()); | |
159 DCHECK(client_map_.count(route_id) == 0); | |
160 | |
161 client_map_[route_id] = client; | |
162 response.Run(true); | |
163 } | |
164 | |
165 void OnDestroyOnIOThread(const int32_t* route_id) { | |
166 DCHECK(io_task_runner_->BelongsToCurrentThread()); | |
167 const auto& it = client_map_.find(*route_id); | |
168 DCHECK(it != client_map_.end()); | |
169 Client* client = it->second; | |
170 DCHECK(client); | |
171 client_map_.erase(it); | |
172 | |
173 child_task_runner_->PostTask( | |
174 FROM_HERE, base::Bind(&MessageFilter::DestroyClient, this, client)); | |
175 } | |
176 | |
177 void DestroyClient(Client* client) { | |
178 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
179 delete client; | |
180 if (owner_) | |
181 owner_->ClientRemoved(); | |
182 } | |
183 | |
184 void NotifyDecodeStatusOnIOThread(int32_t route_id, | |
185 int32_t buffer_id, | |
186 media::JpegDecodeAccelerator::Error error) { | |
187 DCHECK(io_task_runner_->BelongsToCurrentThread()); | |
188 SendOnIOThread(new AcceleratedJpegDecoderHostMsg_DecodeAck( | |
189 route_id, buffer_id, error)); | |
190 } | |
191 | |
192 void OnDecodeOnIOThread( | |
193 const int32_t* route_id, | |
194 const AcceleratedJpegDecoderMsg_Decode_Params& params) { | |
195 DCHECK(io_task_runner_->BelongsToCurrentThread()); | |
196 DCHECK(route_id); | |
197 TRACE_EVENT0("jpeg", "GpuJpegDecodeAccelerator::MessageFilter::OnDecode"); | |
198 | |
199 if (!VerifyDecodeParams(params)) { | |
200 NotifyDecodeStatusOnIOThread( | |
201 *route_id, params.input_buffer.id(), | |
202 media::JpegDecodeAccelerator::INVALID_ARGUMENT); | |
203 if (base::SharedMemory::IsHandleValid(params.output_video_frame_handle)) | |
204 base::SharedMemory::CloseHandle(params.output_video_frame_handle); | |
205 return; | |
206 } | |
207 | |
208 // For handles in |params|, from now on, |params.output_video_frame_handle| | |
209 // is taken cared by scoper. |params.input_buffer.handle()| need to be | |
210 // closed manually for early exits. | |
211 std::unique_ptr<base::SharedMemory> output_shm( | |
212 new base::SharedMemory(params.output_video_frame_handle, false)); | |
213 if (!output_shm->Map(params.output_buffer_size)) { | |
214 LOG(ERROR) << "Could not map output shared memory for input buffer id " | |
215 << params.input_buffer.id(); | |
216 NotifyDecodeStatusOnIOThread( | |
217 *route_id, params.input_buffer.id(), | |
218 media::JpegDecodeAccelerator::PLATFORM_FAILURE); | |
219 base::SharedMemory::CloseHandle(params.input_buffer.handle()); | |
220 return; | |
221 } | |
222 | |
223 uint8_t* shm_memory = static_cast<uint8_t*>(output_shm->memory()); | |
224 scoped_refptr<media::VideoFrame> frame = | |
225 media::VideoFrame::WrapExternalSharedMemory( | |
226 media::PIXEL_FORMAT_I420, // format | |
227 params.coded_size, // coded_size | |
228 gfx::Rect(params.coded_size), // visible_rect | |
229 params.coded_size, // natural_size | |
230 shm_memory, // data | |
231 params.output_buffer_size, // data_size | |
232 params.output_video_frame_handle, // handle | |
233 0, // data_offset | |
234 base::TimeDelta()); // timestamp | |
235 if (!frame.get()) { | |
236 LOG(ERROR) << "Could not create VideoFrame for input buffer id " | |
237 << params.input_buffer.id(); | |
238 NotifyDecodeStatusOnIOThread( | |
239 *route_id, params.input_buffer.id(), | |
240 media::JpegDecodeAccelerator::PLATFORM_FAILURE); | |
241 base::SharedMemory::CloseHandle(params.input_buffer.handle()); | |
242 return; | |
243 } | |
244 frame->AddDestructionObserver( | |
245 base::Bind(DecodeFinished, base::Passed(&output_shm))); | |
246 | |
247 DCHECK_GT(client_map_.count(*route_id), 0u); | |
248 Client* client = client_map_[*route_id]; | |
249 client->Decode(params.input_buffer, frame); | |
250 } | |
251 | |
252 protected: | |
253 ~MessageFilter() override { | |
254 if (client_map_.empty()) | |
255 return; | |
256 | |
257 if (child_task_runner_->BelongsToCurrentThread()) { | |
258 STLDeleteValues(&client_map_); | |
259 } else { | |
260 // Make sure |Client| are deleted on child thread. | |
261 std::unique_ptr<ClientMap> client_map(new ClientMap); | |
262 client_map->swap(client_map_); | |
263 | |
264 child_task_runner_->PostTask( | |
265 FROM_HERE, | |
266 base::Bind(&DeleteClientMapOnChildThread, base::Passed(&client_map))); | |
267 } | |
268 } | |
269 | |
270 private: | |
271 using ClientMap = base::hash_map<int32_t, Client*>; | |
272 | |
273 // Must be static because this method runs after destructor. | |
274 static void DeleteClientMapOnChildThread( | |
275 std::unique_ptr<ClientMap> client_map) { | |
276 STLDeleteValues(client_map.get()); | |
277 } | |
278 | |
279 base::WeakPtr<GpuJpegDecodeAccelerator> owner_; | |
280 | |
281 // GPU child task runner. | |
282 scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_; | |
283 | |
284 // GPU IO task runner. | |
285 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; | |
286 | |
287 // The sender to which this filter was added. | |
288 IPC::Sender* sender_; | |
289 | |
290 // A map from route id to JpegDecodeAccelerator. | |
291 // Unless in destructor (maybe on child thread), |client_map_| should | |
292 // only be accessed on IO thread. | |
293 ClientMap client_map_; | |
294 }; | |
295 | |
296 GpuJpegDecodeAccelerator::GpuJpegDecodeAccelerator( | |
297 gpu::GpuChannel* channel, | |
298 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) | |
299 : channel_(channel), | |
300 child_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
301 io_task_runner_(io_task_runner), | |
302 client_number_(0) { | |
303 } | |
304 | |
305 GpuJpegDecodeAccelerator::~GpuJpegDecodeAccelerator() { | |
306 DCHECK(CalledOnValidThread()); | |
307 if (filter_) { | |
308 channel_->RemoveFilter(filter_.get()); | |
309 } | |
310 } | |
311 | |
312 void GpuJpegDecodeAccelerator::AddClient(int32_t route_id, | |
313 base::Callback<void(bool)> response) { | |
314 DCHECK(CalledOnValidThread()); | |
315 | |
316 // When adding non-chromeos platforms, VideoCaptureGpuJpegDecoder::Initialize | |
317 // needs to be updated. | |
318 | |
319 // This list is ordered by priority of use. | |
320 const GpuJpegDecodeAccelerator::CreateJDAFp create_jda_fps[] = { | |
321 &GpuJpegDecodeAccelerator::CreateV4L2JDA, | |
322 &GpuJpegDecodeAccelerator::CreateVaapiJDA, | |
323 }; | |
324 | |
325 std::unique_ptr<Client> client(new Client(this, route_id)); | |
326 std::unique_ptr<media::JpegDecodeAccelerator> accelerator; | |
327 for (const auto& create_jda_function : create_jda_fps) { | |
328 std::unique_ptr<media::JpegDecodeAccelerator> tmp_accelerator = | |
329 (*create_jda_function)(io_task_runner_); | |
330 if (tmp_accelerator && tmp_accelerator->Initialize(client.get())) { | |
331 accelerator = std::move(tmp_accelerator); | |
332 break; | |
333 } | |
334 } | |
335 | |
336 if (!accelerator) { | |
337 DLOG(ERROR) << "JPEG accelerator Initialize failed"; | |
338 response.Run(false); | |
339 return; | |
340 } | |
341 client->set_accelerator(std::move(accelerator)); | |
342 | |
343 if (!filter_) { | |
344 DCHECK_EQ(client_number_, 0); | |
345 filter_ = new MessageFilter(this); | |
346 // This should be before AddClientOnIOThread. | |
347 channel_->AddFilter(filter_.get()); | |
348 } | |
349 client_number_++; | |
350 | |
351 // In this PostTask, |client| may leak if |io_task_runner_| is destroyed | |
352 // before |client| reached AddClientOnIOThread. However we cannot use scoper | |
353 // to protect it because |client| can only be deleted on child thread. The IO | |
354 // thread is destroyed at termination, at which point it's ok to leak since | |
355 // we're going to tear down the process anyway. So we just crossed fingers | |
356 // here instead of making the code unnecessary complicated. | |
357 io_task_runner_->PostTask( | |
358 FROM_HERE, base::Bind(&MessageFilter::AddClientOnIOThread, filter_, | |
359 route_id, client.release(), response)); | |
360 } | |
361 | |
362 void GpuJpegDecodeAccelerator::NotifyDecodeStatus( | |
363 int32_t route_id, | |
364 int32_t buffer_id, | |
365 media::JpegDecodeAccelerator::Error error) { | |
366 DCHECK(CalledOnValidThread()); | |
367 Send(new AcceleratedJpegDecoderHostMsg_DecodeAck(route_id, buffer_id, error)); | |
368 } | |
369 | |
370 void GpuJpegDecodeAccelerator::ClientRemoved() { | |
371 DCHECK(CalledOnValidThread()); | |
372 DCHECK_GT(client_number_, 0); | |
373 client_number_--; | |
374 if (client_number_ == 0) { | |
375 channel_->RemoveFilter(filter_.get()); | |
376 filter_ = nullptr; | |
377 } | |
378 } | |
379 | |
380 bool GpuJpegDecodeAccelerator::Send(IPC::Message* message) { | |
381 DCHECK(CalledOnValidThread()); | |
382 return channel_->Send(message); | |
383 } | |
384 | |
385 // static | |
386 std::unique_ptr<media::JpegDecodeAccelerator> | |
387 GpuJpegDecodeAccelerator::CreateV4L2JDA( | |
388 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) { | |
389 std::unique_ptr<media::JpegDecodeAccelerator> decoder; | |
390 #if defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC) | |
391 scoped_refptr<V4L2Device> device = V4L2Device::Create( | |
392 V4L2Device::kJpegDecoder); | |
393 if (device) | |
394 decoder.reset(new V4L2JpegDecodeAccelerator(device, io_task_runner)); | |
395 #endif | |
396 return decoder; | |
397 } | |
398 | |
399 // static | |
400 std::unique_ptr<media::JpegDecodeAccelerator> | |
401 GpuJpegDecodeAccelerator::CreateVaapiJDA( | |
402 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) { | |
403 std::unique_ptr<media::JpegDecodeAccelerator> decoder; | |
404 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) | |
405 decoder.reset(new VaapiJpegDecodeAccelerator(io_task_runner)); | |
406 #endif | |
407 return decoder; | |
408 } | |
409 | |
410 // static | |
411 bool GpuJpegDecodeAccelerator::IsSupported() { | |
412 const GpuJpegDecodeAccelerator::CreateJDAFp create_jda_fps[] = { | |
413 &GpuJpegDecodeAccelerator::CreateV4L2JDA, | |
414 &GpuJpegDecodeAccelerator::CreateVaapiJDA, | |
415 }; | |
416 for (const auto& create_jda_function : create_jda_fps) { | |
417 std::unique_ptr<media::JpegDecodeAccelerator> accelerator = | |
418 (*create_jda_function)(base::ThreadTaskRunnerHandle::Get()); | |
419 if (accelerator && accelerator->IsSupported()) | |
420 return true; | |
421 } | |
422 return false; | |
423 } | |
424 | |
425 } // namespace content | |
OLD | NEW |