vaapi_video_decode_accelerator.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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. 4 5#include "base/bind.h" 6#include "base/debug/trace_event.h" 7#include "base/logging.h" 8#include "base/metrics/histogram.h" 9#include "base/stl_util.h" 10#include "base/strings/string_util.h" 11#include "base/synchronization/waitable_event.h" 12#include "base/threading/non_thread_safe.h" 13#include "content/common/gpu/gpu_channel.h" 14#include "content/common/gpu/media/vaapi_video_decode_accelerator.h" 15#include "media/base/bind_to_current_loop.h" 16#include "media/video/picture.h" 17#include "ui/gl/gl_bindings.h" 18#include "ui/gl/scoped_binders.h" 19 20static void ReportToUMA( 21 content::VaapiH264Decoder::VAVDAH264DecoderFailure failure) { 22 UMA_HISTOGRAM_ENUMERATION( 23 "Media.VAVDAH264.DecoderFailure", 24 failure, 25 content::VaapiH264Decoder::VAVDA_H264_DECODER_FAILURES_MAX); 26} 27 28namespace content { 29 30#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ 31 do { \ 32 if (!(result)) { \ 33 DVLOG(1) << log; \ 34 NotifyError(error_code); \ 35 return ret; \ 36 } \ 37 } while (0) 38 39VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) { 40} 41 42VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() { 43} 44 45void VaapiVideoDecodeAccelerator::NotifyError(Error error) { 46 if (message_loop_ != base::MessageLoop::current()) { 47 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 48 message_loop_->PostTask(FROM_HERE, base::Bind( 49 &VaapiVideoDecodeAccelerator::NotifyError, weak_this_, error)); 50 return; 51 } 52 53 // Post Cleanup() as a task so we don't recursively acquire lock_. 54 message_loop_->PostTask(FROM_HERE, base::Bind( 55 &VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); 56 57 DVLOG(1) << "Notifying of error " << error; 58 if (client_) { 59 client_->NotifyError(error); 60 client_ptr_factory_.reset(); 61 } 62} 63 64// TFPPicture allocates X Pixmaps and binds them to textures passed 65// in PictureBuffers from clients to them. TFPPictures are created as 66// a consequence of receiving a set of PictureBuffers from clients and released 67// at the end of decode (or when a new set of PictureBuffers is required). 68// 69// TFPPictures are used for output, contents of VASurfaces passed from decoder 70// are put into the associated pixmap memory and sent to client. 71class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe { 72 public: 73 ~TFPPicture(); 74 75 static linked_ptr<TFPPicture> Create( 76 const base::Callback<bool(void)>& make_context_current, 77 const GLXFBConfig& fb_config, 78 Display* x_display, 79 int32 picture_buffer_id, 80 uint32 texture_id, 81 gfx::Size size); 82 83 int32 picture_buffer_id() { 84 return picture_buffer_id_; 85 } 86 87 gfx::Size size() { 88 return size_; 89 } 90 91 int x_pixmap() { 92 return x_pixmap_; 93 } 94 95 // Bind texture to pixmap. Needs to be called every frame. 96 bool Bind(); 97 98 private: 99 TFPPicture(const base::Callback<bool(void)>& make_context_current, 100 Display* x_display, 101 int32 picture_buffer_id, 102 uint32 texture_id, 103 gfx::Size size); 104 105 bool Initialize(const GLXFBConfig& fb_config); 106 107 base::Callback<bool(void)> make_context_current_; 108 109 Display* x_display_; 110 111 // Output id for the client. 112 int32 picture_buffer_id_; 113 uint32 texture_id_; 114 115 gfx::Size size_; 116 117 // Pixmaps bound to this texture. 118 Pixmap x_pixmap_; 119 GLXPixmap glx_pixmap_; 120 121 DISALLOW_COPY_AND_ASSIGN(TFPPicture); 122}; 123 124VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture( 125 const base::Callback<bool(void)>& make_context_current, 126 Display* x_display, 127 int32 picture_buffer_id, 128 uint32 texture_id, 129 gfx::Size size) 130 : make_context_current_(make_context_current), 131 x_display_(x_display), 132 picture_buffer_id_(picture_buffer_id), 133 texture_id_(texture_id), 134 size_(size), 135 x_pixmap_(0), 136 glx_pixmap_(0) { 137 DCHECK(!make_context_current_.is_null()); 138}; 139 140linked_ptr<VaapiVideoDecodeAccelerator::TFPPicture> 141VaapiVideoDecodeAccelerator::TFPPicture::Create( 142 const base::Callback<bool(void)>& make_context_current, 143 const GLXFBConfig& fb_config, 144 Display* x_display, 145 int32 picture_buffer_id, 146 uint32 texture_id, 147 gfx::Size size) { 148 149 linked_ptr<TFPPicture> tfp_picture( 150 new TFPPicture(make_context_current, x_display, picture_buffer_id, 151 texture_id, size)); 152 153 if (!tfp_picture->Initialize(fb_config)) 154 tfp_picture.reset(); 155 156 return tfp_picture; 157} 158 159bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize( 160 const GLXFBConfig& fb_config) { 161 DCHECK(CalledOnValidThread()); 162 if (!make_context_current_.Run()) 163 return false; 164 165 XWindowAttributes win_attr; 166 int screen = DefaultScreen(x_display_); 167 XGetWindowAttributes(x_display_, RootWindow(x_display_, screen), &win_attr); 168 //TODO(posciak): pass the depth required by libva, not the RootWindow's depth 169 x_pixmap_ = XCreatePixmap(x_display_, RootWindow(x_display_, screen), 170 size_.width(), size_.height(), win_attr.depth); 171 if (!x_pixmap_) { 172 DVLOG(1) << "Failed creating an X Pixmap for TFP"; 173 return false; 174 } 175 176 static const int pixmap_attr[] = { 177 GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, 178 GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, 179 GL_NONE, 180 }; 181 182 glx_pixmap_ = glXCreatePixmap(x_display_, fb_config, x_pixmap_, pixmap_attr); 183 if (!glx_pixmap_) { 184 // x_pixmap_ will be freed in the destructor. 185 DVLOG(1) << "Failed creating a GLX Pixmap for TFP"; 186 return false; 187 } 188 189 return true; 190} 191 192VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() { 193 DCHECK(CalledOnValidThread()); 194 // Unbind surface from texture and deallocate resources. 195 if (glx_pixmap_ && make_context_current_.Run()) { 196 glXReleaseTexImageEXT(x_display_, glx_pixmap_, GLX_FRONT_LEFT_EXT); 197 glXDestroyPixmap(x_display_, glx_pixmap_); 198 } 199 200 if (x_pixmap_) 201 XFreePixmap(x_display_, x_pixmap_); 202 XSync(x_display_, False); // Needed to work around buggy vdpau-driver. 203} 204 205bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() { 206 DCHECK(CalledOnValidThread()); 207 DCHECK(x_pixmap_); 208 DCHECK(glx_pixmap_); 209 if (!make_context_current_.Run()) 210 return false; 211 212 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_); 213 glXBindTexImageEXT(x_display_, glx_pixmap_, GLX_FRONT_LEFT_EXT, NULL); 214 215 return true; 216} 217 218VaapiVideoDecodeAccelerator::TFPPicture* 219 VaapiVideoDecodeAccelerator::TFPPictureById(int32 picture_buffer_id) { 220 TFPPictures::iterator it = tfp_pictures_.find(picture_buffer_id); 221 if (it == tfp_pictures_.end()) { 222 DVLOG(1) << "Picture id " << picture_buffer_id << " does not exist"; 223 return NULL; 224 } 225 226 return it->second.get(); 227} 228 229VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( 230 Display* x_display, 231 const base::Callback<bool(void)>& make_context_current) 232 : x_display_(x_display), 233 make_context_current_(make_context_current), 234 state_(kUninitialized), 235 input_ready_(&lock_), 236 surfaces_available_(&lock_), 237 message_loop_(base::MessageLoop::current()), 238 decoder_thread_("VaapiDecoderThread"), 239 num_frames_at_client_(0), 240 num_stream_bufs_at_decoder_(0), 241 finish_flush_pending_(false), 242 awaiting_va_surfaces_recycle_(false), 243 requested_num_pics_(0), 244 weak_this_factory_(this) { 245 weak_this_ = weak_this_factory_.GetWeakPtr(); 246 va_surface_release_cb_ = media::BindToCurrentLoop( 247 base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); 248} 249 250VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { 251 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 252} 253 254class XFreeDeleter { 255 public: 256 void operator()(void* x) const { 257 ::XFree(x); 258 } 259}; 260 261bool VaapiVideoDecodeAccelerator::InitializeFBConfig() { 262 const int fbconfig_attr[] = { 263 GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, 264 GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, 265 GLX_BIND_TO_TEXTURE_RGB_EXT, GL_TRUE, 266 GLX_Y_INVERTED_EXT, GL_TRUE, 267 GL_NONE, 268 }; 269 270 int num_fbconfigs; 271 scoped_ptr<GLXFBConfig, XFreeDeleter> glx_fb_configs( 272 glXChooseFBConfig(x_display_, DefaultScreen(x_display_), fbconfig_attr, 273 &num_fbconfigs)); 274 if (!glx_fb_configs) 275 return false; 276 if (!num_fbconfigs) 277 return false; 278 279 fb_config_ = glx_fb_configs.get()[0]; 280 return true; 281} 282 283bool VaapiVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile, 284 Client* client) { 285 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 286 287 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); 288 client_ = client_ptr_factory_->GetWeakPtr(); 289 290 base::AutoLock auto_lock(lock_); 291 DCHECK_EQ(state_, kUninitialized); 292 DVLOG(2) << "Initializing VAVDA, profile: " << profile; 293 294 if (!make_context_current_.Run()) 295 return false; 296 297 if (!InitializeFBConfig()) { 298 DVLOG(1) << "Could not get a usable FBConfig"; 299 return false; 300 } 301 302 vaapi_wrapper_ = VaapiWrapper::Create( 303 VaapiWrapper::kDecode, 304 profile, 305 x_display_, 306 base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR)); 307 308 if (!vaapi_wrapper_.get()) { 309 DVLOG(1) << "Failed initializing VAAPI"; 310 return false; 311 } 312 313 decoder_.reset( 314 new VaapiH264Decoder( 315 vaapi_wrapper_.get(), 316 media::BindToCurrentLoop(base::Bind( 317 &VaapiVideoDecodeAccelerator::SurfaceReady, weak_this_)), 318 base::Bind(&ReportToUMA))); 319 320 CHECK(decoder_thread_.Start()); 321 decoder_thread_proxy_ = decoder_thread_.message_loop_proxy(); 322 323 state_ = kIdle; 324 return true; 325} 326 327void VaapiVideoDecodeAccelerator::SurfaceReady( 328 int32 input_id, 329 const scoped_refptr<VASurface>& va_surface) { 330 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 331 DCHECK(!awaiting_va_surfaces_recycle_); 332 333 // Drop any requests to output if we are resetting or being destroyed. 334 if (state_ == kResetting || state_ == kDestroying) 335 return; 336 337 pending_output_cbs_.push( 338 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, 339 weak_this_, va_surface, input_id)); 340 341 TryOutputSurface(); 342} 343 344void VaapiVideoDecodeAccelerator::OutputPicture( 345 const scoped_refptr<VASurface>& va_surface, 346 int32 input_id, 347 TFPPicture* tfp_picture) { 348 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 349 350 int32 output_id = tfp_picture->picture_buffer_id(); 351 352 TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface", 353 "input_id", input_id, 354 "output_id", output_id); 355 356 DVLOG(3) << "Outputting VASurface " << va_surface->id() 357 << " into pixmap bound to picture buffer id " << output_id; 358 359 RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(), 360 "Failed binding texture to pixmap", 361 PLATFORM_FAILURE, ); 362 363 RETURN_AND_NOTIFY_ON_FAILURE( 364 vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(), 365 tfp_picture->x_pixmap(), 366 tfp_picture->size()), 367 "Failed putting surface into pixmap", PLATFORM_FAILURE, ); 368 369 // Notify the client a picture is ready to be displayed. 370 ++num_frames_at_client_; 371 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); 372 DVLOG(4) << "Notifying output picture id " << output_id 373 << " for input "<< input_id << " is ready"; 374 if (client_) 375 client_->PictureReady(media::Picture(output_id, input_id)); 376} 377 378void VaapiVideoDecodeAccelerator::TryOutputSurface() { 379 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 380 381 // Handle Destroy() arriving while pictures are queued for output. 382 if (!client_) 383 return; 384 385 if (pending_output_cbs_.empty() || output_buffers_.empty()) 386 return; 387 388 OutputCB output_cb = pending_output_cbs_.front(); 389 pending_output_cbs_.pop(); 390 391 TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front()); 392 DCHECK(tfp_picture); 393 output_buffers_.pop(); 394 395 output_cb.Run(tfp_picture); 396 397 if (finish_flush_pending_ && pending_output_cbs_.empty()) 398 FinishFlush(); 399} 400 401void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer( 402 const media::BitstreamBuffer& bitstream_buffer) { 403 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 404 TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id", 405 bitstream_buffer.id()); 406 407 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id() 408 << " size: " << (int)bitstream_buffer.size(); 409 410 scoped_ptr<base::SharedMemory> shm( 411 new base::SharedMemory(bitstream_buffer.handle(), true)); 412 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()), 413 "Failed to map input buffer", UNREADABLE_INPUT,); 414 415 base::AutoLock auto_lock(lock_); 416 417 // Set up a new input buffer and queue it for later. 418 linked_ptr<InputBuffer> input_buffer(new InputBuffer()); 419 input_buffer->shm.reset(shm.release()); 420 input_buffer->id = bitstream_buffer.id(); 421 input_buffer->size = bitstream_buffer.size(); 422 423 ++num_stream_bufs_at_decoder_; 424 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", 425 num_stream_bufs_at_decoder_); 426 427 input_buffers_.push(input_buffer); 428 input_ready_.Signal(); 429} 430 431bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { 432 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 433 lock_.AssertAcquired(); 434 435 if (curr_input_buffer_.get()) 436 return true; 437 438 // Will only wait if it is expected that in current state new buffers will 439 // be queued from the client via Decode(). The state can change during wait. 440 while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { 441 input_ready_.Wait(); 442 } 443 444 // We could have got woken up in a different state or never got to sleep 445 // due to current state; check for that. 446 switch (state_) { 447 case kFlushing: 448 // Here we are only interested in finishing up decoding buffers that are 449 // already queued up. Otherwise will stop decoding. 450 if (input_buffers_.empty()) 451 return false; 452 // else fallthrough 453 case kDecoding: 454 case kIdle: 455 DCHECK(!input_buffers_.empty()); 456 457 curr_input_buffer_ = input_buffers_.front(); 458 input_buffers_.pop(); 459 460 DVLOG(4) << "New current bitstream buffer, id: " 461 << curr_input_buffer_->id 462 << " size: " << curr_input_buffer_->size; 463 464 decoder_->SetStream( 465 static_cast<uint8*>(curr_input_buffer_->shm->memory()), 466 curr_input_buffer_->size, curr_input_buffer_->id); 467 return true; 468 469 default: 470 // We got woken up due to being destroyed/reset, ignore any already 471 // queued inputs. 472 return false; 473 } 474} 475 476void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { 477 lock_.AssertAcquired(); 478 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 479 DCHECK(curr_input_buffer_.get()); 480 481 int32 id = curr_input_buffer_->id; 482 curr_input_buffer_.reset(); 483 DVLOG(4) << "End of input buffer " << id; 484 message_loop_->PostTask(FROM_HERE, base::Bind( 485 &Client::NotifyEndOfBitstreamBuffer, client_, id)); 486 487 --num_stream_bufs_at_decoder_; 488 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", 489 num_stream_bufs_at_decoder_); 490} 491 492bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() { 493 lock_.AssertAcquired(); 494 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 495 496 while (available_va_surfaces_.empty() && 497 (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) { 498 surfaces_available_.Wait(); 499 } 500 501 if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle) 502 return false; 503 504 while (!available_va_surfaces_.empty()) { 505 scoped_refptr<VASurface> va_surface( 506 new VASurface(available_va_surfaces_.front(), va_surface_release_cb_)); 507 available_va_surfaces_.pop_front(); 508 decoder_->ReuseSurface(va_surface); 509 } 510 511 return true; 512} 513 514void VaapiVideoDecodeAccelerator::DecodeTask() { 515 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 516 TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask"); 517 base::AutoLock auto_lock(lock_); 518 519 if (state_ != kDecoding) 520 return; 521 522 // Main decode task. 523 DVLOG(4) << "Decode task"; 524 525 // Try to decode what stream data is (still) in the decoder until we run out 526 // of it. 527 while (GetInputBuffer_Locked()) { 528 DCHECK(curr_input_buffer_.get()); 529 530 VaapiH264Decoder::DecResult res; 531 { 532 // We are OK releasing the lock here, as decoder never calls our methods 533 // directly and we will reacquire the lock before looking at state again. 534 // This is the main decode function of the decoder and while keeping 535 // the lock for its duration would be fine, it would defeat the purpose 536 // of having a separate decoder thread. 537 base::AutoUnlock auto_unlock(lock_); 538 res = decoder_->Decode(); 539 } 540 541 switch (res) { 542 case VaapiH264Decoder::kAllocateNewSurfaces: 543 DVLOG(1) << "Decoder requesting a new set of surfaces"; 544 message_loop_->PostTask(FROM_HERE, base::Bind( 545 &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_, 546 decoder_->GetRequiredNumOfPictures(), 547 decoder_->GetPicSize())); 548 // We'll get rescheduled once ProvidePictureBuffers() finishes. 549 return; 550 551 case VaapiH264Decoder::kRanOutOfStreamData: 552 ReturnCurrInputBuffer_Locked(); 553 break; 554 555 case VaapiH264Decoder::kRanOutOfSurfaces: 556 // No more output buffers in the decoder, try getting more or go to 557 // sleep waiting for them. 558 if (!FeedDecoderWithOutputSurfaces_Locked()) 559 return; 560 561 break; 562 563 case VaapiH264Decoder::kDecodeError: 564 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", 565 PLATFORM_FAILURE, ); 566 return; 567 } 568 } 569} 570 571void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, 572 gfx::Size size) { 573 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 574 DCHECK(!awaiting_va_surfaces_recycle_); 575 576 // At this point decoder has stopped running and has already posted onto our 577 // loop any remaining output request callbacks, which executed before we got 578 // here. Some of them might have been pended though, because we might not 579 // have had enough TFPictures to output surfaces to. Initiate a wait cycle, 580 // which will wait for client to return enough PictureBuffers to us, so that 581 // we can finish all pending output callbacks, releasing associated surfaces. 582 DVLOG(1) << "Initiating surface set change"; 583 awaiting_va_surfaces_recycle_ = true; 584 585 requested_num_pics_ = num_pics; 586 requested_pic_size_ = size; 587 588 TryFinishSurfaceSetChange(); 589} 590 591void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { 592 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 593 594 if (!awaiting_va_surfaces_recycle_) 595 return; 596 597 if (!pending_output_cbs_.empty() || 598 tfp_pictures_.size() != available_va_surfaces_.size()) { 599 // Either: 600 // 1. Not all pending pending output callbacks have been executed yet. 601 // Wait for the client to return enough pictures and retry later. 602 // 2. The above happened and all surface release callbacks have been posted 603 // as the result, but not all have executed yet. Post ourselves after them 604 // to let them release surfaces. 605 DVLOG(2) << "Awaiting pending output/surface release callbacks to finish"; 606 message_loop_->PostTask(FROM_HERE, base::Bind( 607 &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_)); 608 return; 609 } 610 611 // All surfaces released, destroy them and dismiss all PictureBuffers. 612 awaiting_va_surfaces_recycle_ = false; 613 available_va_surfaces_.clear(); 614 vaapi_wrapper_->DestroySurfaces(); 615 616 for (TFPPictures::iterator iter = tfp_pictures_.begin(); 617 iter != tfp_pictures_.end(); ++iter) { 618 DVLOG(2) << "Dismissing picture id: " << iter->first; 619 if (client_) 620 client_->DismissPictureBuffer(iter->first); 621 } 622 tfp_pictures_.clear(); 623 624 // And ask for a new set as requested. 625 DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: " 626 << requested_pic_size_.ToString(); 627 628 message_loop_->PostTask(FROM_HERE, base::Bind( 629 &Client::ProvidePictureBuffers, client_, 630 requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D)); 631} 632 633void VaapiVideoDecodeAccelerator::Decode( 634 const media::BitstreamBuffer& bitstream_buffer) { 635 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 636 637 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", 638 bitstream_buffer.id()); 639 640 // We got a new input buffer from the client, map it and queue for later use. 641 MapAndQueueNewInputBuffer(bitstream_buffer); 642 643 base::AutoLock auto_lock(lock_); 644 switch (state_) { 645 case kIdle: 646 state_ = kDecoding; 647 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 648 &VaapiVideoDecodeAccelerator::DecodeTask, 649 base::Unretained(this))); 650 break; 651 652 case kDecoding: 653 // Decoder already running, fallthrough. 654 case kResetting: 655 // When resetting, allow accumulating bitstream buffers, so that 656 // the client can queue after-seek-buffers while we are finishing with 657 // the before-seek one. 658 break; 659 660 default: 661 RETURN_AND_NOTIFY_ON_FAILURE(false, 662 "Decode request from client in invalid state: " << state_, 663 PLATFORM_FAILURE, ); 664 break; 665 } 666} 667 668void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( 669 VASurfaceID va_surface_id) { 670 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 671 base::AutoLock auto_lock(lock_); 672 673 available_va_surfaces_.push_back(va_surface_id); 674 surfaces_available_.Signal(); 675} 676 677void VaapiVideoDecodeAccelerator::AssignPictureBuffers( 678 const std::vector<media::PictureBuffer>& buffers) { 679 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 680 681 base::AutoLock auto_lock(lock_); 682 DCHECK(tfp_pictures_.empty()); 683 684 while (!output_buffers_.empty()) 685 output_buffers_.pop(); 686 687 RETURN_AND_NOTIFY_ON_FAILURE( 688 buffers.size() == requested_num_pics_, 689 "Got an invalid number of picture buffers. (Got " << buffers.size() 690 << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); 691 DCHECK(requested_pic_size_ == buffers[0].size()); 692 693 std::vector<VASurfaceID> va_surface_ids; 694 RETURN_AND_NOTIFY_ON_FAILURE( 695 vaapi_wrapper_->CreateSurfaces(requested_pic_size_, 696 buffers.size(), 697 &va_surface_ids), 698 "Failed creating VA Surfaces", PLATFORM_FAILURE, ); 699 DCHECK_EQ(va_surface_ids.size(), buffers.size()); 700 701 for (size_t i = 0; i < buffers.size(); ++i) { 702 DVLOG(2) << "Assigning picture id: " << buffers[i].id() 703 << " to texture id: " << buffers[i].texture_id() 704 << " VASurfaceID: " << va_surface_ids[i]; 705 706 linked_ptr<TFPPicture> tfp_picture( 707 TFPPicture::Create(make_context_current_, fb_config_, x_display_, 708 buffers[i].id(), buffers[i].texture_id(), 709 requested_pic_size_)); 710 711 RETURN_AND_NOTIFY_ON_FAILURE( 712 tfp_picture.get(), "Failed assigning picture buffer to a texture.", 713 PLATFORM_FAILURE, ); 714 715 bool inserted = tfp_pictures_.insert(std::make_pair( 716 buffers[i].id(), tfp_picture)).second; 717 DCHECK(inserted); 718 719 output_buffers_.push(buffers[i].id()); 720 available_va_surfaces_.push_back(va_surface_ids[i]); 721 surfaces_available_.Signal(); 722 } 723 724 state_ = kDecoding; 725 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 726 &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this))); 727} 728 729void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { 730 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 731 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", 732 picture_buffer_id); 733 734 --num_frames_at_client_; 735 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); 736 737 output_buffers_.push(picture_buffer_id); 738 TryOutputSurface(); 739} 740 741void VaapiVideoDecodeAccelerator::FlushTask() { 742 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 743 DVLOG(1) << "Flush task"; 744 745 // First flush all the pictures that haven't been outputted, notifying the 746 // client to output them. 747 bool res = decoder_->Flush(); 748 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", 749 PLATFORM_FAILURE, ); 750 751 // Put the decoder in idle state, ready to resume. 752 decoder_->Reset(); 753 754 message_loop_->PostTask(FROM_HERE, base::Bind( 755 &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); 756} 757 758void VaapiVideoDecodeAccelerator::Flush() { 759 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 760 DVLOG(1) << "Got flush request"; 761 762 base::AutoLock auto_lock(lock_); 763 state_ = kFlushing; 764 // Queue a flush task after all existing decoding tasks to clean up. 765 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 766 &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this))); 767 768 input_ready_.Signal(); 769 surfaces_available_.Signal(); 770} 771 772void VaapiVideoDecodeAccelerator::FinishFlush() { 773 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 774 775 finish_flush_pending_ = false; 776 777 base::AutoLock auto_lock(lock_); 778 if (state_ != kFlushing) { 779 DCHECK_EQ(state_, kDestroying); 780 return; // We could've gotten destroyed already. 781 } 782 783 // Still waiting for textures from client to finish outputting all pending 784 // frames. Try again later. 785 if (!pending_output_cbs_.empty()) { 786 finish_flush_pending_ = true; 787 return; 788 } 789 790 state_ = kIdle; 791 792 message_loop_->PostTask(FROM_HERE, base::Bind( 793 &Client::NotifyFlushDone, client_)); 794 795 DVLOG(1) << "Flush finished"; 796} 797 798void VaapiVideoDecodeAccelerator::ResetTask() { 799 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 800 DVLOG(1) << "ResetTask"; 801 802 // All the decoding tasks from before the reset request from client are done 803 // by now, as this task was scheduled after them and client is expected not 804 // to call Decode() after Reset() and before NotifyResetDone. 805 decoder_->Reset(); 806 807 base::AutoLock auto_lock(lock_); 808 809 // Return current input buffer, if present. 810 if (curr_input_buffer_.get()) 811 ReturnCurrInputBuffer_Locked(); 812 813 // And let client know that we are done with reset. 814 message_loop_->PostTask(FROM_HERE, base::Bind( 815 &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); 816} 817 818void VaapiVideoDecodeAccelerator::Reset() { 819 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 820 DVLOG(1) << "Got reset request"; 821 822 // This will make any new decode tasks exit early. 823 base::AutoLock auto_lock(lock_); 824 state_ = kResetting; 825 finish_flush_pending_ = false; 826 827 // Drop all remaining input buffers, if present. 828 while (!input_buffers_.empty()) { 829 message_loop_->PostTask(FROM_HERE, base::Bind( 830 &Client::NotifyEndOfBitstreamBuffer, client_, 831 input_buffers_.front()->id)); 832 input_buffers_.pop(); 833 } 834 835 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 836 &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this))); 837 838 input_ready_.Signal(); 839 surfaces_available_.Signal(); 840} 841 842void VaapiVideoDecodeAccelerator::FinishReset() { 843 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 844 DVLOG(1) << "FinishReset"; 845 base::AutoLock auto_lock(lock_); 846 847 if (state_ != kResetting) { 848 DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; 849 return; // We could've gotten destroyed already. 850 } 851 852 // Drop pending outputs. 853 while (!pending_output_cbs_.empty()) 854 pending_output_cbs_.pop(); 855 856 if (awaiting_va_surfaces_recycle_) { 857 // Decoder requested a new surface set while we were waiting for it to 858 // finish the last DecodeTask, running at the time of Reset(). 859 // Let the surface set change finish first before resetting. 860 message_loop_->PostTask(FROM_HERE, base::Bind( 861 &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); 862 return; 863 } 864 865 num_stream_bufs_at_decoder_ = 0; 866 state_ = kIdle; 867 868 message_loop_->PostTask(FROM_HERE, base::Bind( 869 &Client::NotifyResetDone, client_)); 870 871 // The client might have given us new buffers via Decode() while we were 872 // resetting and might be waiting for our move, and not call Decode() anymore 873 // until we return something. Post a DecodeTask() so that we won't 874 // sleep forever waiting for Decode() in that case. Having two of them 875 // in the pipe is harmless, the additional one will return as soon as it sees 876 // that we are back in kDecoding state. 877 if (!input_buffers_.empty()) { 878 state_ = kDecoding; 879 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 880 &VaapiVideoDecodeAccelerator::DecodeTask, 881 base::Unretained(this))); 882 } 883 884 DVLOG(1) << "Reset finished"; 885} 886 887void VaapiVideoDecodeAccelerator::Cleanup() { 888 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 889 890 if (state_ == kUninitialized || state_ == kDestroying) 891 return; 892 893 DVLOG(1) << "Destroying VAVDA"; 894 base::AutoLock auto_lock(lock_); 895 state_ = kDestroying; 896 897 client_ptr_factory_.reset(); 898 weak_this_factory_.InvalidateWeakPtrs(); 899 900 // Signal all potential waiters on the decoder_thread_, let them early-exit, 901 // as we've just moved to the kDestroying state, and wait for all tasks 902 // to finish. 903 input_ready_.Signal(); 904 surfaces_available_.Signal(); 905 { 906 base::AutoUnlock auto_unlock(lock_); 907 decoder_thread_.Stop(); 908 } 909 910 state_ = kUninitialized; 911} 912 913void VaapiVideoDecodeAccelerator::Destroy() { 914 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 915 Cleanup(); 916 delete this; 917} 918 919bool VaapiVideoDecodeAccelerator::CanDecodeOnIOThread() { 920 return false; 921} 922 923} // namespace content 924