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