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"
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"
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"
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
41 bool VerifyDecodeParams(const AcceleratedJpegDecoderMsg_Decode_Params
& params
) {
42 if (params
.input_buffer_id
< 0) {
43 LOG(ERROR
) << "BitstreamBuffer id " << params
.input_buffer_id
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();
56 if (!base::SharedMemory::IsHandleValid(params
.input_buffer_handle
)) {
57 LOG(ERROR
) << "invalid input_buffer_handle";
61 if (!base::SharedMemory::IsHandleValid(params
.output_video_frame_handle
)) {
62 LOG(ERROR
) << "invalid output_video_frame_handle";
66 if (params
.output_buffer_size
<
67 media::VideoFrame::AllocationSize(media::PIXEL_FORMAT_I420
,
69 LOG(ERROR
) << "output_buffer_size is too small: "
70 << params
.output_buffer_size
;
81 class GpuJpegDecodeAccelerator::Client
82 : public media::JpegDecodeAccelerator::Client
,
83 public base::NonThreadSafe
{
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());
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());
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();
118 base::WeakPtr
<content::GpuJpegDecodeAccelerator
> owner_
;
120 scoped_ptr
<media::JpegDecodeAccelerator
> accelerator_
;
123 // Create, destroy, and RemoveClient run on child thread. All other methods run
125 class GpuJpegDecodeAccelerator::MessageFilter
: public IPC::MessageFilter
{
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())
144 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MessageFilter
, msg
, &route_id
)
145 IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Decode
, OnDecodeOnIOThread
)
146 IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Destroy
,
148 IPC_MESSAGE_UNHANDLED(handled
= false)
149 IPC_END_MESSAGE_MAP()
153 bool SendOnIOThread(IPC::Message
* message
) {
154 DCHECK(!message
->is_sync());
159 return sender_
->Send(message
);
162 void AddClientOnIOThread(int32 route_id
,
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
;
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());
189 owner_
->ClientRemoved();
192 void NotifyDecodeStatusOnIOThread(int32 route_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());
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
);
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
);
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
245 params
.output_buffer_size
, // data_size
246 params
.output_video_frame_handle
, // handle
248 base::TimeDelta()); // timestamp
249 frame
->AddDestructionObserver(
250 base::Bind(DecodeFinished
, base::Passed(&output_shm
)));
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
);
262 DCHECK_GT(client_map_
.count(*route_id
), 0u);
263 Client
* client
= client_map_
[*route_id
];
264 client
->Decode(input_buffer
, frame
);
268 ~MessageFilter() override
{
269 if (client_map_
.empty())
272 if (child_task_runner_
->BelongsToCurrentThread()) {
273 STLDeleteValues(&client_map_
);
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(
281 base::Bind(&DeleteClientMapOnChildThread
, base::Passed(&client_map
)));
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(
312 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
)
314 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
315 io_task_runner_(io_task_runner
),
319 GpuJpegDecodeAccelerator::~GpuJpegDecodeAccelerator() {
320 DCHECK(CalledOnValidThread());
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();
351 DLOG(ERROR
) << "JPEG accelerator Initialize failed";
352 GpuMsg_CreateJpegDecoder::WriteReplyParams(reply_msg
, false);
356 client
->set_accelerator(accelerator
.Pass());
359 DCHECK_EQ(client_number_
, 0);
360 filter_
= new MessageFilter(this);
361 // This should be before AddClientOnIOThread.
362 channel_
->AddFilter(filter_
.get());
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(
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);
389 if (client_number_
== 0) {
390 channel_
->RemoveFilter(filter_
.get());
395 bool GpuJpegDecodeAccelerator::Send(IPC::Message
* message
) {
396 DCHECK(CalledOnValidThread());
397 return channel_
->Send(message
);
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
);
409 decoder
.reset(new V4L2JpegDecodeAccelerator(device
, io_task_runner
));
411 return decoder
.Pass();
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
));
422 return decoder
.Pass();
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())
440 } // namespace content