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/message_loop/message_loop_proxy.h"
13 #include "base/stl_util.h"
15 #include "content/common/gpu/gpu_channel.h"
16 #include "content/common/gpu/gpu_messages.h"
17 #include "content/public/common/content_switches.h"
18 #include "gpu/command_buffer/common/command_buffer.h"
19 #include "ipc/ipc_message_macros.h"
20 #include "ipc/ipc_message_utils.h"
21 #include "ui/gl/gl_context.h"
22 #include "ui/gl/gl_surface_egl.h"
25 #include "base/win/windows_version.h"
26 #include "content/common/gpu/media/dxva_video_decode_accelerator.h"
27 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11)
28 #include "content/common/gpu/media/exynos_video_decode_accelerator.h"
29 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
30 #include "ui/gl/gl_context_glx.h"
31 #include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
32 #elif defined(OS_ANDROID)
33 #include "content/common/gpu/media/android_video_decode_accelerator.h"
36 #include "gpu/command_buffer/service/texture_manager.h"
37 #include "ui/gfx/size.h"
39 using gpu::gles2::TextureManager
;
43 static bool MakeDecoderContextCurrent(
44 const base::WeakPtr
<GpuCommandBufferStub
> stub
) {
46 DLOG(ERROR
) << "Stub is gone; won't MakeCurrent().";
50 if (!stub
->decoder()->MakeCurrent()) {
51 DLOG(ERROR
) << "Failed to MakeCurrent()";
58 class GpuVideoDecodeAccelerator::MessageFilter
59 : public IPC::ChannelProxy::MessageFilter
{
61 MessageFilter(GpuVideoDecodeAccelerator
* owner
, int32 host_route_id
)
62 : owner_(owner
), host_route_id_(host_route_id
) {}
64 virtual void OnChannelError() OVERRIDE
{ channel_
= NULL
; }
66 virtual void OnChannelClosing() OVERRIDE
{ channel_
= NULL
; }
68 virtual void OnFilterAdded(IPC::Channel
* channel
) OVERRIDE
{
72 virtual void OnFilterRemoved() OVERRIDE
{
73 // This will delete |owner_| and |this|.
74 owner_
->OnFilterRemoved();
77 virtual bool OnMessageReceived(const IPC::Message
& msg
) OVERRIDE
{
78 if (msg
.routing_id() != host_route_id_
)
81 IPC_BEGIN_MESSAGE_MAP(MessageFilter
, msg
)
82 IPC_MESSAGE_FORWARD(AcceleratedVideoDecoderMsg_Decode
, owner_
,
83 GpuVideoDecodeAccelerator::OnDecode
)
84 IPC_MESSAGE_UNHANDLED(return false;)
89 bool SendOnIOThread(IPC::Message
* message
) {
90 DCHECK(!message
->is_sync());
95 return channel_
->Send(message
);
99 virtual ~MessageFilter() {}
102 GpuVideoDecodeAccelerator
* owner_
;
103 int32 host_route_id_
;
104 // The channel to which this filter was added.
105 IPC::Channel
* channel_
;
108 GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator(
110 GpuCommandBufferStub
* stub
,
111 const scoped_refptr
<base::MessageLoopProxy
>& io_message_loop
)
112 : init_done_msg_(NULL
),
113 host_route_id_(host_route_id
),
116 io_message_loop_(io_message_loop
),
117 weak_factory_for_io_(this) {
119 stub_
->AddDestructionObserver(this);
120 stub_
->channel()->AddRoute(host_route_id_
, this);
121 child_message_loop_
= base::MessageLoopProxy::current();
122 make_context_current_
=
123 base::Bind(&MakeDecoderContextCurrent
, stub_
->AsWeakPtr());
126 GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() {
127 if (video_decode_accelerator_
)
128 video_decode_accelerator_
.release()->Destroy();
131 bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message
& msg
) {
133 if (!video_decode_accelerator_
)
136 IPC_BEGIN_MESSAGE_MAP(GpuVideoDecodeAccelerator
, msg
)
137 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Decode
, OnDecode
)
138 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_AssignPictureBuffers
,
139 OnAssignPictureBuffers
)
140 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_ReusePictureBuffer
,
141 OnReusePictureBuffer
)
142 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Flush
, OnFlush
)
143 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Reset
, OnReset
)
144 IPC_MESSAGE_HANDLER(AcceleratedVideoDecoderMsg_Destroy
, OnDestroy
)
145 IPC_MESSAGE_UNHANDLED(handled
= false)
146 IPC_END_MESSAGE_MAP()
150 void GpuVideoDecodeAccelerator::ProvidePictureBuffers(
151 uint32 requested_num_of_buffers
,
152 const gfx::Size
& dimensions
,
153 uint32 texture_target
) {
154 if (!Send(new AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers(
155 host_route_id_
, requested_num_of_buffers
, dimensions
,
157 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_ProvidePictureBuffers) "
160 texture_target_
= texture_target
;
163 void GpuVideoDecodeAccelerator::DismissPictureBuffer(
164 int32 picture_buffer_id
) {
165 // Notify client that picture buffer is now unused.
166 if (!Send(new AcceleratedVideoDecoderHostMsg_DismissPictureBuffer(
167 host_route_id_
, picture_buffer_id
))) {
168 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_DismissPictureBuffer) "
173 void GpuVideoDecodeAccelerator::PictureReady(
174 const media::Picture
& picture
) {
175 if (!Send(new AcceleratedVideoDecoderHostMsg_PictureReady(
177 picture
.picture_buffer_id(),
178 picture
.bitstream_buffer_id()))) {
179 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_PictureReady) failed";
183 void GpuVideoDecodeAccelerator::NotifyError(
184 media::VideoDecodeAccelerator::Error error
) {
185 if (init_done_msg_
) {
186 // If we get an error while we're initializing, NotifyInitializeDone won't
187 // be called, so we need to send the reply (with an error) here.
188 GpuCommandBufferMsg_CreateVideoDecoder::WriteReplyParams(
190 if (!Send(init_done_msg_
))
191 DLOG(ERROR
) << "Send(init_done_msg_) failed";
192 init_done_msg_
= NULL
;
195 if (!Send(new AcceleratedVideoDecoderHostMsg_ErrorNotification(
196 host_route_id_
, error
))) {
197 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_ErrorNotification) "
202 void GpuVideoDecodeAccelerator::Initialize(
203 const media::VideoCodecProfile profile
,
204 IPC::Message
* init_done_msg
) {
206 DCHECK(!video_decode_accelerator_
.get());
207 DCHECK(!init_done_msg_
);
208 DCHECK(init_done_msg
);
209 init_done_msg_
= init_done_msg
;
212 // Ensure we will be able to get a GL context at all before initializing
214 if (!make_context_current_
.Run()) {
215 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
221 if (base::win::GetVersion() < base::win::VERSION_WIN7
) {
222 NOTIMPLEMENTED() << "HW video decode acceleration not available.";
223 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
226 DLOG(INFO
) << "Initializing DXVA HW decoder for windows.";
227 video_decode_accelerator_
.reset(new DXVAVideoDecodeAccelerator(
228 this, make_context_current_
));
229 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11)
230 video_decode_accelerator_
.reset(new ExynosVideoDecodeAccelerator(
231 gfx::GLSurfaceEGL::GetHardwareDisplay(),
232 stub_
->decoder()->GetGLContext()->GetHandle(),
234 weak_factory_for_io_
.GetWeakPtr(),
235 make_context_current_
,
237 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
238 gfx::GLContextGLX
* glx_context
=
239 static_cast<gfx::GLContextGLX
*>(stub_
->decoder()->GetGLContext());
240 GLXContext glx_context_handle
=
241 static_cast<GLXContext
>(glx_context
->GetHandle());
242 video_decode_accelerator_
.reset(new VaapiVideoDecodeAccelerator(
243 glx_context
->display(), glx_context_handle
, this,
244 make_context_current_
));
245 #elif defined(OS_ANDROID)
246 video_decode_accelerator_
.reset(new AndroidVideoDecodeAccelerator(
248 stub_
->decoder()->AsWeakPtr(),
249 make_context_current_
));
251 NOTIMPLEMENTED() << "HW video decode acceleration not available.";
252 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
256 if (video_decode_accelerator_
->CanDecodeOnIOThread()) {
257 filter_
= new MessageFilter(this, host_route_id_
);
258 stub_
->channel()->AddFilter(filter_
.get());
261 if (!video_decode_accelerator_
->Initialize(profile
))
262 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
265 // Runs on IO thread if video_decode_accelerator_->CanDecodeOnIOThread() is
266 // true, otherwise on the main thread.
267 void GpuVideoDecodeAccelerator::OnDecode(
268 base::SharedMemoryHandle handle
, int32 id
, uint32 size
) {
269 DCHECK(video_decode_accelerator_
.get());
271 DLOG(FATAL
) << "BitstreamBuffer id " << id
<< " out of range";
272 if (child_message_loop_
->BelongsToCurrentThread()) {
273 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
275 child_message_loop_
->PostTask(
277 base::Bind(&GpuVideoDecodeAccelerator::NotifyError
,
278 base::Unretained(this),
279 media::VideoDecodeAccelerator::INVALID_ARGUMENT
));
283 video_decode_accelerator_
->Decode(media::BitstreamBuffer(id
, handle
, size
));
286 void GpuVideoDecodeAccelerator::OnAssignPictureBuffers(
287 const std::vector
<int32
>& buffer_ids
,
288 const std::vector
<uint32
>& texture_ids
,
289 const std::vector
<gfx::Size
>& sizes
) {
291 if (buffer_ids
.size() != texture_ids
.size() ||
292 buffer_ids
.size() != sizes
.size()) {
293 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
297 gpu::gles2::GLES2Decoder
* command_decoder
= stub_
->decoder();
298 gpu::gles2::TextureManager
* texture_manager
=
299 command_decoder
->GetContextGroup()->texture_manager();
301 std::vector
<media::PictureBuffer
> buffers
;
302 for (uint32 i
= 0; i
< buffer_ids
.size(); ++i
) {
303 if (buffer_ids
[i
] < 0) {
304 DLOG(FATAL
) << "Buffer id " << buffer_ids
[i
] << " out of range";
305 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
308 gpu::gles2::TextureRef
* texture_ref
= texture_manager
->GetTexture(
311 DLOG(FATAL
) << "Failed to find texture id " << texture_ids
[i
];
312 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
315 gpu::gles2::Texture
* info
= texture_ref
->texture();
316 if (info
->target() != texture_target_
) {
317 DLOG(FATAL
) << "Texture target mismatch for texture id "
319 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
322 // GL_TEXTURE_EXTERNAL_OES textures have their dimensions defined by the
323 // underlying EGLImage.
324 if (texture_target_
!= GL_TEXTURE_EXTERNAL_OES
) {
325 GLsizei width
= 0, height
= 0;
326 info
->GetLevelSize(texture_target_
, 0, &width
, &height
);
327 if (width
!= sizes
[i
].width() || height
!= sizes
[i
].height()) {
328 DLOG(FATAL
) << "Size mismatch for texture id " << texture_ids
[i
];
329 NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT
);
333 if (!texture_manager
->ClearRenderableLevels(command_decoder
, texture_ref
)) {
334 DLOG(FATAL
) << "Failed to Clear texture id " << texture_ids
[i
];
335 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
338 uint32 service_texture_id
;
339 if (!command_decoder
->GetServiceTextureId(
340 texture_ids
[i
], &service_texture_id
)) {
341 DLOG(FATAL
) << "Failed to translate texture!";
342 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE
);
345 buffers
.push_back(media::PictureBuffer(
346 buffer_ids
[i
], sizes
[i
], service_texture_id
));
348 video_decode_accelerator_
->AssignPictureBuffers(buffers
);
351 void GpuVideoDecodeAccelerator::OnReusePictureBuffer(
352 int32 picture_buffer_id
) {
353 DCHECK(video_decode_accelerator_
.get());
354 video_decode_accelerator_
->ReusePictureBuffer(picture_buffer_id
);
357 void GpuVideoDecodeAccelerator::OnFlush() {
358 DCHECK(video_decode_accelerator_
.get());
359 video_decode_accelerator_
->Flush();
362 void GpuVideoDecodeAccelerator::OnReset() {
363 DCHECK(video_decode_accelerator_
.get());
364 video_decode_accelerator_
->Reset();
367 void GpuVideoDecodeAccelerator::OnDestroy() {
368 DCHECK(video_decode_accelerator_
.get());
370 stub_
->channel()->RemoveRoute(host_route_id_
);
371 stub_
->RemoveDestructionObserver(this);
373 // Remove the filter first because the member variables can be accessed on
374 // IO thread. When filter is removed, OnFilterRemoved will delete |this|.
375 stub_
->channel()->RemoveFilter(filter_
.get());
381 void GpuVideoDecodeAccelerator::OnFilterRemoved() {
382 // We're destroying; cancel all callbacks.
383 weak_factory_for_io_
.InvalidateWeakPtrs();
384 child_message_loop_
->DeleteSoon(FROM_HERE
, this);
387 void GpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
388 int32 bitstream_buffer_id
) {
389 if (!Send(new AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed(
390 host_route_id_
, bitstream_buffer_id
))) {
392 << "Send(AcceleratedVideoDecoderHostMsg_BitstreamBufferProcessed) "
397 void GpuVideoDecodeAccelerator::NotifyInitializeDone() {
398 GpuCommandBufferMsg_CreateVideoDecoder::WriteReplyParams(
399 init_done_msg_
, host_route_id_
);
400 if (!Send(init_done_msg_
))
401 DLOG(ERROR
) << "Send(init_done_msg_) failed";
402 init_done_msg_
= NULL
;
405 void GpuVideoDecodeAccelerator::NotifyFlushDone() {
406 if (!Send(new AcceleratedVideoDecoderHostMsg_FlushDone(host_route_id_
)))
407 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_FlushDone) failed";
410 void GpuVideoDecodeAccelerator::NotifyResetDone() {
411 if (!Send(new AcceleratedVideoDecoderHostMsg_ResetDone(host_route_id_
)))
412 DLOG(ERROR
) << "Send(AcceleratedVideoDecoderHostMsg_ResetDone) failed";
415 void GpuVideoDecodeAccelerator::OnWillDestroyStub() { OnDestroy(); }
417 bool GpuVideoDecodeAccelerator::Send(IPC::Message
* message
) {
419 if (filter_
.get() && io_message_loop_
->BelongsToCurrentThread())
420 return filter_
->SendOnIOThread(message
);
421 DCHECK(child_message_loop_
->BelongsToCurrentThread());
422 return stub_
->channel()->Send(message
);
425 } // namespace content