1 // Copyright (c) 2012 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_video_decode_accelerator.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/stl_util.h"
16 #include "content/common/gpu/gpu_channel.h"
17 #include "content/common/gpu/gpu_messages.h"
18 #include "content/public/common/content_switches.h"
19 #include "gpu/command_buffer/common/command_buffer.h"
20 #include "ipc/ipc_message_macros.h"
21 #include "ipc/ipc_message_utils.h"
22 #include "ipc/message_filter.h"
23 #include "media/base/limits.h"
24 #include "ui/gl/gl_context.h"
25 #include "ui/gl/gl_image.h"
26 #include "ui/gl/gl_surface_egl.h"
29 #include "base/win/windows_version.h"
30 #include "content/common/gpu/media/dxva_video_decode_accelerator.h"
31 #elif defined(OS_MACOSX)
32 #include "content/common/gpu/media/vt_video_decode_accelerator.h"
33 #elif defined(OS_CHROMEOS)
34 #if defined(USE_V4L2_CODEC)
35 #include "content/common/gpu/media/v4l2_device.h"
36 #include "content/common/gpu/media/v4l2_slice_video_decode_accelerator.h"
37 #include "content/common/gpu/media/v4l2_video_decode_accelerator.h"
39 #if defined(ARCH_CPU_X86_FAMILY)
40 #include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
41 #include "ui/gl/gl_implementation.h"
43 #elif defined(USE_OZONE)
44 #include "media/ozone/media_ozone_platform.h"
45 #elif defined(OS_ANDROID)
46 #include "content/common/gpu/media/android_video_decode_accelerator.h"
49 #include "ui/gfx/geometry/size.h"
53 static bool MakeDecoderContextCurrent(
54 const base::WeakPtr
<GpuCommandBufferStub
> stub
) {
56 DLOG(ERROR
) << "Stub is gone; won't MakeCurrent().";
60 if (!stub
->decoder()->MakeCurrent()) {
61 DLOG(ERROR
) << "Failed to MakeCurrent()";
68 // DebugAutoLock works like AutoLock but only acquires the lock when
71 typedef base::AutoLock DebugAutoLock
;
75 explicit DebugAutoLock(base::Lock
&) {}
79 class GpuVideoDecodeAccelerator::MessageFilter
: public IPC::MessageFilter
{
81 MessageFilter(GpuVideoDecodeAccelerator
* owner
, int32 host_route_id
)
82 : owner_(owner
), host_route_id_(host_route_id
) {}
84 void OnChannelError() override
{ sender_
= NULL
; }
86 void OnChannelClosing() override
{ sender_
= NULL
; }
88 void OnFilterAdded(IPC::Sender
* sender
) override
{ sender_
= sender
; }
90 void OnFilterRemoved() override
{
91 // This will delete |owner_| and |this|.
92 owner_
->OnFilterRemoved();
95 bool OnMessageReceived(const IPC::Message
& msg
) override
{
96 if (msg
.routing_id() != host_route_id_
)
99 IPC_BEGIN_MESSAGE_MAP(MessageFilter
, msg
)
100 IPC_MESSAGE_FORWARD(AcceleratedVideoDecoderMsg_Decode
, owner_
,
101 GpuVideoDecodeAccelerator::OnDecode
)
102 IPC_MESSAGE_UNHANDLED(return false;)
103 IPC_END_MESSAGE_MAP()
107 bool SendOnIOThread(IPC::Message
* message
) {
108 DCHECK(!message
->is_sync());
113 return sender_
->Send(message
);
117 ~MessageFilter() override
{}
120 GpuVideoDecodeAccelerator
* owner_
;
121 int32 host_route_id_
;
122 // The sender to which this filter was added.
123 IPC::Sender
* sender_
;
126 GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator(
128 GpuCommandBufferStub
* stub
,
129 const scoped_refptr
<base::MessageLoopProxy
>& io_message_loop
)
130 : host_route_id_(host_route_id
),
133 filter_removed_(true, false),
134 io_message_loop_(io_message_loop
),
135 weak_factory_for_io_(this) {
137 stub_
->AddDestructionObserver(this);
138 child_message_loop_
= base::MessageLoopProxy::current();
139 make_context_current_
=
140 base::Bind(&MakeDecoderContextCurrent
, stub_
->AsWeakPtr());
143 GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() {
144 // This class can only be self-deleted from OnWillDestroyStub(), which means
145 // the VDA has already been destroyed in there.
146 DCHECK(!video_decode_accelerator_
);
149 bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message
& msg
) {
150 if (!video_decode_accelerator_
)
154 IPC_BEGIN_MESSAGE_MAP(GpuVideoDecodeAccelerator
, msg
)
155 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Decode
, OnDecode
)
156 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_AssignPictureBuffers
,
157 OnAssignPictureBuffers
)
158 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_ReusePictureBuffer
,
159 OnReusePictureBuffer
)
160 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Flush
, OnFlush
)
161 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Reset
, OnReset
)
162 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Destroy
, OnDestroy
)
163 IPC_MESSAGE_UNHANDLED(handled
= false)
164 IPC_END_MESSAGE_MAP()
168 void GpuVideoDecodeAccelerator::ProvidePictureBuffers(
169 uint32 requested_num_of_buffers
,
170 const gfx::Size
& dimensions
,
171 uint32 texture_target
) {
172 if (dimensions
.width() > media::limits::kMaxDimension
||
173 dimensions
.height() > media::limits::kMaxDimension
||
174 dimensions
.GetArea() > media::limits::kMaxCanvas
) {
175 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
178 if (!Send(new AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers(
180 requested_num_of_buffers
,
183 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers) "
186 texture_dimensions_
= dimensions
;
187 texture_target_
= texture_target
;
190 void GpuVideoDecodeAccelerator::DismissPictureBuffer(
191 int32 picture_buffer_id
) {
192 // Notify client that picture buffer is now unused.
193 if (!Send(new AcceleratedVideoDecoderHostMsg_DismissPictureBuffer(
194 host_route_id_
, picture_buffer_id
))) {
195 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_DismissPictureBuffer) "
198 DebugAutoLock
auto_lock(debug_uncleared_textures_lock_
);
199 uncleared_textures_
.erase(picture_buffer_id
);
202 void GpuVideoDecodeAccelerator::PictureReady(
203 const media::Picture
& picture
) {
204 // VDA may call PictureReady on IO thread. SetTextureCleared should run on
205 // the child thread. VDA is responsible to call PictureReady on the child
206 // thread when a picture buffer is delivered the first time.
207 if (child_message_loop_
->BelongsToCurrentThread()) {
208 SetTextureCleared(picture
);
210 DCHECK(io_message_loop_
->BelongsToCurrentThread());
211 DebugAutoLock
auto_lock(debug_uncleared_textures_lock_
);
212 DCHECK_EQ(0u, uncleared_textures_
.count(picture
.picture_buffer_id()));
215 if (!Send(new AcceleratedVideoDecoderHostMsg_PictureReady(
216 host_route_id_
, picture
.picture_buffer_id(),
217 picture
.bitstream_buffer_id(), picture
.visible_rect(),
218 picture
.allow_overlay()))) {
219 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_PictureReady) failed";
223 void GpuVideoDecodeAccelerator::NotifyError(
224 media::VideoDecodeAccelerator::Error error
) {
225 if (!Send(new AcceleratedVideoDecoderHostMsg_ErrorNotification(
226 host_route_id_
, error
))) {
227 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_ErrorNotification) "
232 void GpuVideoDecodeAccelerator::Initialize(
233 const media::VideoCodecProfile profile
,
234 IPC::Message
* init_done_msg
) {
235 DCHECK(!video_decode_accelerator_
.get());
237 if (!stub_
->channel()->AddRoute(host_route_id_
, this)) {
238 DLOG(ERROR
) << "GpuVideoDecodeAccelerator::Initialize(): "
239 "failed to add route";
240 SendCreateDecoderReply(init_done_msg
, false);
244 // Ensure we will be able to get a GL context at all before initializing
246 if (!make_context_current_
.Run()) {
247 SendCreateDecoderReply(init_done_msg
, false);
252 std::vector
<GpuVideoDecodeAccelerator::CreateVDAFp
>
253 create_vda_fps
= CreateVDAFps();
255 for (size_t i
= 0; i
< create_vda_fps
.size(); ++i
) {
256 video_decode_accelerator_
= (this->*create_vda_fps
[i
])();
257 if (!video_decode_accelerator_
||
258 !video_decode_accelerator_
->Initialize(profile
, this))
261 if (video_decode_accelerator_
->CanDecodeOnIOThread()) {
262 filter_
= new MessageFilter(this, host_route_id_
);
263 stub_
->channel()->AddFilter(filter_
.get());
265 SendCreateDecoderReply(init_done_msg
, true);
268 video_decode_accelerator_
.reset();
269 NOTIMPLEMENTED() << "HW video decode acceleration not available.";
270 SendCreateDecoderReply(init_done_msg
, false);
273 std::vector
<GpuVideoDecodeAccelerator::CreateVDAFp
>
274 GpuVideoDecodeAccelerator::CreateVDAFps() {
275 std::vector
<GpuVideoDecodeAccelerator::CreateVDAFp
> create_vda_fps
;
276 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateDXVAVDA
);
277 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateV4L2VDA
);
278 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateV4L2SliceVDA
);
279 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateVaapiVDA
);
280 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateVTVDA
);
281 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateOzoneVDA
);
282 create_vda_fps
.push_back(&GpuVideoDecodeAccelerator::CreateAndroidVDA
);
283 return create_vda_fps
;
286 scoped_ptr
<media::VideoDecodeAccelerator
>
287 GpuVideoDecodeAccelerator::CreateDXVAVDA() {
288 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
290 if (base::win::GetVersion() >= base::win::VERSION_WIN7
) {
291 DVLOG(0) << "Initializing DXVA HW decoder for windows.";
292 decoder
.reset(new DXVAVideoDecodeAccelerator(make_context_current_
,
293 stub_
->decoder()->GetGLContext()));
295 NOTIMPLEMENTED() << "HW video decode acceleration not available.";
298 return decoder
.Pass();
301 scoped_ptr
<media::VideoDecodeAccelerator
>
302 GpuVideoDecodeAccelerator::CreateV4L2VDA() {
303 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
304 #if defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC)
305 scoped_refptr
<V4L2Device
> device
= V4L2Device::Create(V4L2Device::kDecoder
);
307 decoder
.reset(new V4L2VideoDecodeAccelerator(
308 gfx::GLSurfaceEGL::GetHardwareDisplay(),
309 stub_
->decoder()->GetGLContext()->GetHandle(),
310 weak_factory_for_io_
.GetWeakPtr(),
311 make_context_current_
,
316 return decoder
.Pass();
319 scoped_ptr
<media::VideoDecodeAccelerator
>
320 GpuVideoDecodeAccelerator::CreateV4L2SliceVDA() {
321 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
322 #if defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC)
323 scoped_refptr
<V4L2Device
> device
= V4L2Device::Create(V4L2Device::kDecoder
);
325 decoder
.reset(new V4L2SliceVideoDecodeAccelerator(
327 gfx::GLSurfaceEGL::GetHardwareDisplay(),
328 stub_
->decoder()->GetGLContext()->GetHandle(),
329 weak_factory_for_io_
.GetWeakPtr(),
330 make_context_current_
,
334 return decoder
.Pass();
337 void GpuVideoDecodeAccelerator::BindImage(uint32 client_texture_id
,
338 uint32 texture_target
,
339 scoped_refptr
<gfx::GLImage
> image
) {
340 gpu::gles2::GLES2Decoder
* command_decoder
= stub_
->decoder();
341 gpu::gles2::TextureManager
* texture_manager
=
342 command_decoder
->GetContextGroup()->texture_manager();
343 gpu::gles2::TextureRef
* ref
= texture_manager
->GetTexture(client_texture_id
);
345 texture_manager
->SetLevelImage(ref
, texture_target
, 0, image
.get());
348 scoped_ptr
<media::VideoDecodeAccelerator
>
349 GpuVideoDecodeAccelerator::CreateVaapiVDA() {
350 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
351 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
352 decoder
.reset(new VaapiVideoDecodeAccelerator(
353 make_context_current_
, base::Bind(&GpuVideoDecodeAccelerator::BindImage
,
354 base::Unretained(this))));
356 return decoder
.Pass();
359 scoped_ptr
<media::VideoDecodeAccelerator
>
360 GpuVideoDecodeAccelerator::CreateVTVDA() {
361 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
362 #if defined(OS_MACOSX)
363 decoder
.reset(new VTVideoDecodeAccelerator(
364 static_cast<CGLContextObj
>(stub_
->decoder()->GetGLContext()->GetHandle()),
365 make_context_current_
));
367 return decoder
.Pass();
370 scoped_ptr
<media::VideoDecodeAccelerator
>
371 GpuVideoDecodeAccelerator::CreateOzoneVDA() {
372 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
373 #if !defined(OS_CHROMEOS) && defined(USE_OZONE)
374 media::MediaOzonePlatform
* platform
=
375 media::MediaOzonePlatform::GetInstance();
376 decoder
.reset(platform
->CreateVideoDecodeAccelerator(make_context_current_
));
378 return decoder
.Pass();
381 scoped_ptr
<media::VideoDecodeAccelerator
>
382 GpuVideoDecodeAccelerator::CreateAndroidVDA() {
383 scoped_ptr
<media::VideoDecodeAccelerator
> decoder
;
384 #if defined(OS_ANDROID)
385 decoder
.reset(new AndroidVideoDecodeAccelerator(
386 stub_
->decoder()->AsWeakPtr(),
387 make_context_current_
));
389 return decoder
.Pass();
392 // Runs on IO thread if video_decode_accelerator_->CanDecodeOnIOThread() is
393 // true, otherwise on the main thread.
394 void GpuVideoDecodeAccelerator::OnDecode(
395 base::SharedMemoryHandle handle
, int32 id
, uint32 size
) {
396 DCHECK(video_decode_accelerator_
.get());
398 DLOG(ERROR
) << "BitstreamBuffer id " << id
<< " out of range";
399 if (child_message_loop_
->BelongsToCurrentThread()) {
400 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
402 child_message_loop_
->PostTask(
404 base::Bind(&GpuVideoDecodeAccelerator::NotifyError
,
405 base::Unretained(this),
406 media::VideoDecodeAccelerator::INVALID_ARGUMENT
));
410 video_decode_accelerator_
->Decode(media::BitstreamBuffer(id
, handle
, size
));
413 void GpuVideoDecodeAccelerator::OnAssignPictureBuffers(
414 const std::vector
<int32
>& buffer_ids
,
415 const std::vector
<uint32
>& texture_ids
) {
416 if (buffer_ids
.size() != texture_ids
.size()) {
417 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
421 gpu::gles2::GLES2Decoder
* command_decoder
= stub_
->decoder();
422 gpu::gles2::TextureManager
* texture_manager
=
423 command_decoder
->GetContextGroup()->texture_manager();
425 std::vector
<media::PictureBuffer
> buffers
;
426 std::vector
<scoped_refptr
<gpu::gles2::TextureRef
> > textures
;
427 for (uint32 i
= 0; i
< buffer_ids
.size(); ++i
) {
428 if (buffer_ids
[i
] < 0) {
429 DLOG(ERROR
) << "Buffer id " << buffer_ids
[i
] << " out of range";
430 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
433 gpu::gles2::TextureRef
* texture_ref
= texture_manager
->GetTexture(
436 DLOG(ERROR
) << "Failed to find texture id " << texture_ids
[i
];
437 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
440 gpu::gles2::Texture
* info
= texture_ref
->texture();
441 if (info
->target() != texture_target_
) {
442 DLOG(ERROR
) << "Texture target mismatch for texture id "
444 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
447 if (texture_target_
== GL_TEXTURE_EXTERNAL_OES
||
448 texture_target_
== GL_TEXTURE_RECTANGLE_ARB
) {
449 // These textures have their dimensions defined by the underlying storage.
450 // Use |texture_dimensions_| for this size.
451 texture_manager
->SetLevelInfo(texture_ref
,
455 texture_dimensions_
.width(),
456 texture_dimensions_
.height(),
463 // For other targets, texture dimensions should already be defined.
464 GLsizei width
= 0, height
= 0;
465 info
->GetLevelSize(texture_target_
, 0, &width
, &height
);
466 if (width
!= texture_dimensions_
.width() ||
467 height
!= texture_dimensions_
.height()) {
468 DLOG(ERROR
) << "Size mismatch for texture id " << texture_ids
[i
];
469 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
473 // TODO(dshwang): after moving to D3D11, remove this. crbug.com/438691
475 video_decode_accelerator_
.get()->GetSurfaceInternalFormat();
476 if (format
!= GL_RGBA
) {
477 texture_manager
->SetLevelInfo(texture_ref
, texture_target_
, 0, format
,
478 width
, height
, 1, 0, format
, 0, false);
481 buffers
.push_back(media::PictureBuffer(buffer_ids
[i
], texture_dimensions_
,
482 texture_ref
->service_id(),
484 textures
.push_back(texture_ref
);
486 video_decode_accelerator_
->AssignPictureBuffers(buffers
);
487 DebugAutoLock
auto_lock(debug_uncleared_textures_lock_
);
488 for (uint32 i
= 0; i
< buffer_ids
.size(); ++i
)
489 uncleared_textures_
[buffer_ids
[i
]] = textures
[i
];
492 void GpuVideoDecodeAccelerator::OnReusePictureBuffer(
493 int32 picture_buffer_id
) {
494 DCHECK(video_decode_accelerator_
.get());
495 video_decode_accelerator_
->ReusePictureBuffer(picture_buffer_id
);
498 void GpuVideoDecodeAccelerator::OnFlush() {
499 DCHECK(video_decode_accelerator_
.get());
500 video_decode_accelerator_
->Flush();
503 void GpuVideoDecodeAccelerator::OnReset() {
504 DCHECK(video_decode_accelerator_
.get());
505 video_decode_accelerator_
->Reset();
508 void GpuVideoDecodeAccelerator::OnDestroy() {
509 DCHECK(video_decode_accelerator_
.get());
513 void GpuVideoDecodeAccelerator::OnFilterRemoved() {
514 // We're destroying; cancel all callbacks.
515 weak_factory_for_io_
.InvalidateWeakPtrs();
516 filter_removed_
.Signal();
519 void GpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
520 int32 bitstream_buffer_id
) {
521 if (!Send(new AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed(
522 host_route_id_
, bitstream_buffer_id
))) {
524 << "Send(AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed) "
529 void GpuVideoDecodeAccelerator::NotifyFlushDone() {
530 if (!Send(new AcceleratedVideoDecoderHostMsg_FlushDone(host_route_id_
)))
531 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_FlushDone) failed";
534 void GpuVideoDecodeAccelerator::NotifyResetDone() {
535 if (!Send(new AcceleratedVideoDecoderHostMsg_ResetDone(host_route_id_
)))
536 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_ResetDone) failed";
539 void GpuVideoDecodeAccelerator::OnWillDestroyStub() {
540 // The stub is going away, so we have to stop and destroy VDA here, before
541 // returning, because the VDA may need the GL context to run and/or do its
542 // cleanup. We cannot destroy the VDA before the IO thread message filter is
543 // removed however, since we cannot service incoming messages with VDA gone.
544 // We cannot simply check for existence of VDA on IO thread though, because
545 // we don't want to synchronize the IO thread with the ChildThread.
546 // So we have to wait for the RemoveFilter callback here instead and remove
547 // the VDA after it arrives and before returning.
549 stub_
->channel()->RemoveFilter(filter_
.get());
550 filter_removed_
.Wait();
553 stub_
->channel()->RemoveRoute(host_route_id_
);
554 stub_
->RemoveDestructionObserver(this);
556 video_decode_accelerator_
.reset();
560 void GpuVideoDecodeAccelerator::SetTextureCleared(
561 const media::Picture
& picture
) {
562 DCHECK(child_message_loop_
->BelongsToCurrentThread());
563 DebugAutoLock
auto_lock(debug_uncleared_textures_lock_
);
564 std::map
<int32
, scoped_refptr
<gpu::gles2::TextureRef
> >::iterator it
;
565 it
= uncleared_textures_
.find(picture
.picture_buffer_id());
566 if (it
== uncleared_textures_
.end())
567 return; // the texture has been cleared
569 scoped_refptr
<gpu::gles2::TextureRef
> texture_ref
= it
->second
;
570 GLenum target
= texture_ref
->texture()->target();
571 gpu::gles2::TextureManager
* texture_manager
=
572 stub_
->decoder()->GetContextGroup()->texture_manager();
573 DCHECK(!texture_ref
->texture()->IsLevelCleared(target
, 0));
574 texture_manager
->SetLevelCleared(texture_ref
.get(), target
, 0, true);
575 uncleared_textures_
.erase(it
);
578 bool GpuVideoDecodeAccelerator::Send(IPC::Message
* message
) {
579 if (filter_
.get() && io_message_loop_
->BelongsToCurrentThread())
580 return filter_
->SendOnIOThread(message
);
581 DCHECK(child_message_loop_
->BelongsToCurrentThread());
582 return stub_
->channel()->Send(message
);
585 void GpuVideoDecodeAccelerator::SendCreateDecoderReply(IPC::Message
* message
,
587 GpuCommandBufferMsg_CreateVideoDecoder::WriteReplyParams(message
, succeeded
);
591 } // namespace content