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 20 static 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 28 namespace 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 39 VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) { 40 } 41 42 VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() { 43 } 44 45 void 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. 71 class 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 124 VaapiVideoDecodeAccelerator::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 140 linked_ptr<VaapiVideoDecodeAccelerator::TFPPicture> 141 VaapiVideoDecodeAccelerator::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 159 bool 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 192 VaapiVideoDecodeAccelerator::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 205 bool 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 218 VaapiVideoDecodeAccelerator::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 229 VaapiVideoDecodeAccelerator::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 250 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { 251 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 252 } 253 254 class XFreeDeleter { 255 public: 256 void operator()(void* x) const { 257 ::XFree(x); 258 } 259 }; 260 261 bool 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 283 bool 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 profile, x_display_, 304 base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR)); 305 306 if (!vaapi_wrapper_.get()) { 307 DVLOG(1) << "Failed initializing VAAPI"; 308 return false; 309 } 310 311 decoder_.reset( 312 new VaapiH264Decoder( 313 vaapi_wrapper_.get(), 314 media::BindToCurrentLoop(base::Bind( 315 &VaapiVideoDecodeAccelerator::SurfaceReady, weak_this_)), 316 base::Bind(&ReportToUMA))); 317 318 CHECK(decoder_thread_.Start()); 319 decoder_thread_proxy_ = decoder_thread_.message_loop_proxy(); 320 321 state_ = kIdle; 322 return true; 323 } 324 325 void VaapiVideoDecodeAccelerator::SurfaceReady( 326 int32 input_id, 327 const scoped_refptr<VASurface>& va_surface) { 328 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 329 DCHECK(!awaiting_va_surfaces_recycle_); 330 331 // Drop any requests to output if we are resetting or being destroyed. 332 if (state_ == kResetting || state_ == kDestroying) 333 return; 334 335 pending_output_cbs_.push( 336 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, 337 weak_this_, va_surface, input_id)); 338 339 TryOutputSurface(); 340 } 341 342 void VaapiVideoDecodeAccelerator::OutputPicture( 343 const scoped_refptr<VASurface>& va_surface, 344 int32 input_id, 345 TFPPicture* tfp_picture) { 346 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 347 348 int32 output_id = tfp_picture->picture_buffer_id(); 349 350 TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface", 351 "input_id", input_id, 352 "output_id", output_id); 353 354 DVLOG(3) << "Outputting VASurface " << va_surface->id() 355 << " into pixmap bound to picture buffer id " << output_id; 356 357 RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(), 358 "Failed binding texture to pixmap", 359 PLATFORM_FAILURE, ); 360 361 RETURN_AND_NOTIFY_ON_FAILURE( 362 vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(), 363 tfp_picture->x_pixmap(), 364 tfp_picture->size()), 365 "Failed putting surface into pixmap", PLATFORM_FAILURE, ); 366 367 // Notify the client a picture is ready to be displayed. 368 ++num_frames_at_client_; 369 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); 370 DVLOG(4) << "Notifying output picture id " << output_id 371 << " for input "<< input_id << " is ready"; 372 if (client_) 373 client_->PictureReady(media::Picture(output_id, input_id)); 374 } 375 376 void VaapiVideoDecodeAccelerator::TryOutputSurface() { 377 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 378 379 // Handle Destroy() arriving while pictures are queued for output. 380 if (!client_) 381 return; 382 383 if (pending_output_cbs_.empty() || output_buffers_.empty()) 384 return; 385 386 OutputCB output_cb = pending_output_cbs_.front(); 387 pending_output_cbs_.pop(); 388 389 TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front()); 390 DCHECK(tfp_picture); 391 output_buffers_.pop(); 392 393 output_cb.Run(tfp_picture); 394 395 if (finish_flush_pending_ && pending_output_cbs_.empty()) 396 FinishFlush(); 397 } 398 399 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer( 400 const media::BitstreamBuffer& bitstream_buffer) { 401 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 402 TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id", 403 bitstream_buffer.id()); 404 405 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id() 406 << " size: " << (int)bitstream_buffer.size(); 407 408 scoped_ptr<base::SharedMemory> shm( 409 new base::SharedMemory(bitstream_buffer.handle(), true)); 410 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()), 411 "Failed to map input buffer", UNREADABLE_INPUT,); 412 413 base::AutoLock auto_lock(lock_); 414 415 // Set up a new input buffer and queue it for later. 416 linked_ptr<InputBuffer> input_buffer(new InputBuffer()); 417 input_buffer->shm.reset(shm.release()); 418 input_buffer->id = bitstream_buffer.id(); 419 input_buffer->size = bitstream_buffer.size(); 420 421 ++num_stream_bufs_at_decoder_; 422 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", 423 num_stream_bufs_at_decoder_); 424 425 input_buffers_.push(input_buffer); 426 input_ready_.Signal(); 427 } 428 429 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { 430 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 431 lock_.AssertAcquired(); 432 433 if (curr_input_buffer_.get()) 434 return true; 435 436 // Will only wait if it is expected that in current state new buffers will 437 // be queued from the client via Decode(). The state can change during wait. 438 while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { 439 input_ready_.Wait(); 440 } 441 442 // We could have got woken up in a different state or never got to sleep 443 // due to current state; check for that. 444 switch (state_) { 445 case kFlushing: 446 // Here we are only interested in finishing up decoding buffers that are 447 // already queued up. Otherwise will stop decoding. 448 if (input_buffers_.empty()) 449 return false; 450 // else fallthrough 451 case kDecoding: 452 case kIdle: 453 DCHECK(!input_buffers_.empty()); 454 455 curr_input_buffer_ = input_buffers_.front(); 456 input_buffers_.pop(); 457 458 DVLOG(4) << "New current bitstream buffer, id: " 459 << curr_input_buffer_->id 460 << " size: " << curr_input_buffer_->size; 461 462 decoder_->SetStream( 463 static_cast<uint8*>(curr_input_buffer_->shm->memory()), 464 curr_input_buffer_->size, curr_input_buffer_->id); 465 return true; 466 467 default: 468 // We got woken up due to being destroyed/reset, ignore any already 469 // queued inputs. 470 return false; 471 } 472 } 473 474 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { 475 lock_.AssertAcquired(); 476 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 477 DCHECK(curr_input_buffer_.get()); 478 479 int32 id = curr_input_buffer_->id; 480 curr_input_buffer_.reset(); 481 DVLOG(4) << "End of input buffer " << id; 482 message_loop_->PostTask(FROM_HERE, base::Bind( 483 &Client::NotifyEndOfBitstreamBuffer, client_, id)); 484 485 --num_stream_bufs_at_decoder_; 486 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder", 487 num_stream_bufs_at_decoder_); 488 } 489 490 bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() { 491 lock_.AssertAcquired(); 492 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 493 494 while (available_va_surfaces_.empty() && 495 (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) { 496 surfaces_available_.Wait(); 497 } 498 499 if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle) 500 return false; 501 502 while (!available_va_surfaces_.empty()) { 503 scoped_refptr<VASurface> va_surface( 504 new VASurface(available_va_surfaces_.front(), va_surface_release_cb_)); 505 available_va_surfaces_.pop_front(); 506 decoder_->ReuseSurface(va_surface); 507 } 508 509 return true; 510 } 511 512 void VaapiVideoDecodeAccelerator::DecodeTask() { 513 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 514 TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask"); 515 base::AutoLock auto_lock(lock_); 516 517 if (state_ != kDecoding) 518 return; 519 520 // Main decode task. 521 DVLOG(4) << "Decode task"; 522 523 // Try to decode what stream data is (still) in the decoder until we run out 524 // of it. 525 while (GetInputBuffer_Locked()) { 526 DCHECK(curr_input_buffer_.get()); 527 528 VaapiH264Decoder::DecResult res; 529 { 530 // We are OK releasing the lock here, as decoder never calls our methods 531 // directly and we will reacquire the lock before looking at state again. 532 // This is the main decode function of the decoder and while keeping 533 // the lock for its duration would be fine, it would defeat the purpose 534 // of having a separate decoder thread. 535 base::AutoUnlock auto_unlock(lock_); 536 res = decoder_->Decode(); 537 } 538 539 switch (res) { 540 case VaapiH264Decoder::kAllocateNewSurfaces: 541 DVLOG(1) << "Decoder requesting a new set of surfaces"; 542 message_loop_->PostTask(FROM_HERE, base::Bind( 543 &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_, 544 decoder_->GetRequiredNumOfPictures(), 545 decoder_->GetPicSize())); 546 // We'll get rescheduled once ProvidePictureBuffers() finishes. 547 return; 548 549 case VaapiH264Decoder::kRanOutOfStreamData: 550 ReturnCurrInputBuffer_Locked(); 551 break; 552 553 case VaapiH264Decoder::kRanOutOfSurfaces: 554 // No more output buffers in the decoder, try getting more or go to 555 // sleep waiting for them. 556 if (!FeedDecoderWithOutputSurfaces_Locked()) 557 return; 558 559 break; 560 561 case VaapiH264Decoder::kDecodeError: 562 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", 563 PLATFORM_FAILURE, ); 564 return; 565 } 566 } 567 } 568 569 void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, 570 gfx::Size size) { 571 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 572 DCHECK(!awaiting_va_surfaces_recycle_); 573 574 // At this point decoder has stopped running and has already posted onto our 575 // loop any remaining output request callbacks, which executed before we got 576 // here. Some of them might have been pended though, because we might not 577 // have had enough TFPictures to output surfaces to. Initiate a wait cycle, 578 // which will wait for client to return enough PictureBuffers to us, so that 579 // we can finish all pending output callbacks, releasing associated surfaces. 580 DVLOG(1) << "Initiating surface set change"; 581 awaiting_va_surfaces_recycle_ = true; 582 583 requested_num_pics_ = num_pics; 584 requested_pic_size_ = size; 585 586 TryFinishSurfaceSetChange(); 587 } 588 589 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { 590 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 591 592 if (!awaiting_va_surfaces_recycle_) 593 return; 594 595 if (!pending_output_cbs_.empty() || 596 tfp_pictures_.size() != available_va_surfaces_.size()) { 597 // Either: 598 // 1. Not all pending pending output callbacks have been executed yet. 599 // Wait for the client to return enough pictures and retry later. 600 // 2. The above happened and all surface release callbacks have been posted 601 // as the result, but not all have executed yet. Post ourselves after them 602 // to let them release surfaces. 603 DVLOG(2) << "Awaiting pending output/surface release callbacks to finish"; 604 message_loop_->PostTask(FROM_HERE, base::Bind( 605 &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_)); 606 return; 607 } 608 609 // All surfaces released, destroy them and dismiss all PictureBuffers. 610 awaiting_va_surfaces_recycle_ = false; 611 available_va_surfaces_.clear(); 612 vaapi_wrapper_->DestroySurfaces(); 613 614 for (TFPPictures::iterator iter = tfp_pictures_.begin(); 615 iter != tfp_pictures_.end(); ++iter) { 616 DVLOG(2) << "Dismissing picture id: " << iter->first; 617 if (client_) 618 client_->DismissPictureBuffer(iter->first); 619 } 620 tfp_pictures_.clear(); 621 622 // And ask for a new set as requested. 623 DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: " 624 << requested_pic_size_.ToString(); 625 626 message_loop_->PostTask(FROM_HERE, base::Bind( 627 &Client::ProvidePictureBuffers, client_, 628 requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D)); 629 } 630 631 void VaapiVideoDecodeAccelerator::Decode( 632 const media::BitstreamBuffer& bitstream_buffer) { 633 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 634 635 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", 636 bitstream_buffer.id()); 637 638 // We got a new input buffer from the client, map it and queue for later use. 639 MapAndQueueNewInputBuffer(bitstream_buffer); 640 641 base::AutoLock auto_lock(lock_); 642 switch (state_) { 643 case kIdle: 644 state_ = kDecoding; 645 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 646 &VaapiVideoDecodeAccelerator::DecodeTask, 647 base::Unretained(this))); 648 break; 649 650 case kDecoding: 651 // Decoder already running, fallthrough. 652 case kResetting: 653 // When resetting, allow accumulating bitstream buffers, so that 654 // the client can queue after-seek-buffers while we are finishing with 655 // the before-seek one. 656 break; 657 658 default: 659 RETURN_AND_NOTIFY_ON_FAILURE(false, 660 "Decode request from client in invalid state: " << state_, 661 PLATFORM_FAILURE, ); 662 break; 663 } 664 } 665 666 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( 667 VASurfaceID va_surface_id) { 668 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 669 base::AutoLock auto_lock(lock_); 670 671 available_va_surfaces_.push_back(va_surface_id); 672 surfaces_available_.Signal(); 673 } 674 675 void VaapiVideoDecodeAccelerator::AssignPictureBuffers( 676 const std::vector<media::PictureBuffer>& buffers) { 677 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 678 679 base::AutoLock auto_lock(lock_); 680 DCHECK(tfp_pictures_.empty()); 681 682 while (!output_buffers_.empty()) 683 output_buffers_.pop(); 684 685 RETURN_AND_NOTIFY_ON_FAILURE( 686 buffers.size() == requested_num_pics_, 687 "Got an invalid number of picture buffers. (Got " << buffers.size() 688 << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); 689 DCHECK(requested_pic_size_ == buffers[0].size()); 690 691 std::vector<VASurfaceID> va_surface_ids; 692 RETURN_AND_NOTIFY_ON_FAILURE( 693 vaapi_wrapper_->CreateSurfaces(requested_pic_size_, 694 buffers.size(), 695 &va_surface_ids), 696 "Failed creating VA Surfaces", PLATFORM_FAILURE, ); 697 DCHECK_EQ(va_surface_ids.size(), buffers.size()); 698 699 for (size_t i = 0; i < buffers.size(); ++i) { 700 DVLOG(2) << "Assigning picture id: " << buffers[i].id() 701 << " to texture id: " << buffers[i].texture_id() 702 << " VASurfaceID: " << va_surface_ids[i]; 703 704 linked_ptr<TFPPicture> tfp_picture( 705 TFPPicture::Create(make_context_current_, fb_config_, x_display_, 706 buffers[i].id(), buffers[i].texture_id(), 707 requested_pic_size_)); 708 709 RETURN_AND_NOTIFY_ON_FAILURE( 710 tfp_picture.get(), "Failed assigning picture buffer to a texture.", 711 PLATFORM_FAILURE, ); 712 713 bool inserted = tfp_pictures_.insert(std::make_pair( 714 buffers[i].id(), tfp_picture)).second; 715 DCHECK(inserted); 716 717 output_buffers_.push(buffers[i].id()); 718 available_va_surfaces_.push_back(va_surface_ids[i]); 719 surfaces_available_.Signal(); 720 } 721 722 state_ = kDecoding; 723 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 724 &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this))); 725 } 726 727 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) { 728 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 729 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", 730 picture_buffer_id); 731 732 --num_frames_at_client_; 733 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); 734 735 output_buffers_.push(picture_buffer_id); 736 TryOutputSurface(); 737 } 738 739 void VaapiVideoDecodeAccelerator::FlushTask() { 740 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 741 DVLOG(1) << "Flush task"; 742 743 // First flush all the pictures that haven't been outputted, notifying the 744 // client to output them. 745 bool res = decoder_->Flush(); 746 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", 747 PLATFORM_FAILURE, ); 748 749 // Put the decoder in idle state, ready to resume. 750 decoder_->Reset(); 751 752 message_loop_->PostTask(FROM_HERE, base::Bind( 753 &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); 754 } 755 756 void VaapiVideoDecodeAccelerator::Flush() { 757 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 758 DVLOG(1) << "Got flush request"; 759 760 base::AutoLock auto_lock(lock_); 761 state_ = kFlushing; 762 // Queue a flush task after all existing decoding tasks to clean up. 763 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 764 &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this))); 765 766 input_ready_.Signal(); 767 surfaces_available_.Signal(); 768 } 769 770 void VaapiVideoDecodeAccelerator::FinishFlush() { 771 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 772 773 finish_flush_pending_ = false; 774 775 base::AutoLock auto_lock(lock_); 776 if (state_ != kFlushing) { 777 DCHECK_EQ(state_, kDestroying); 778 return; // We could've gotten destroyed already. 779 } 780 781 // Still waiting for textures from client to finish outputting all pending 782 // frames. Try again later. 783 if (!pending_output_cbs_.empty()) { 784 finish_flush_pending_ = true; 785 return; 786 } 787 788 state_ = kIdle; 789 790 message_loop_->PostTask(FROM_HERE, base::Bind( 791 &Client::NotifyFlushDone, client_)); 792 793 DVLOG(1) << "Flush finished"; 794 } 795 796 void VaapiVideoDecodeAccelerator::ResetTask() { 797 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); 798 DVLOG(1) << "ResetTask"; 799 800 // All the decoding tasks from before the reset request from client are done 801 // by now, as this task was scheduled after them and client is expected not 802 // to call Decode() after Reset() and before NotifyResetDone. 803 decoder_->Reset(); 804 805 base::AutoLock auto_lock(lock_); 806 807 // Return current input buffer, if present. 808 if (curr_input_buffer_.get()) 809 ReturnCurrInputBuffer_Locked(); 810 811 // And let client know that we are done with reset. 812 message_loop_->PostTask(FROM_HERE, base::Bind( 813 &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); 814 } 815 816 void VaapiVideoDecodeAccelerator::Reset() { 817 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 818 DVLOG(1) << "Got reset request"; 819 820 // This will make any new decode tasks exit early. 821 base::AutoLock auto_lock(lock_); 822 state_ = kResetting; 823 finish_flush_pending_ = false; 824 825 // Drop all remaining input buffers, if present. 826 while (!input_buffers_.empty()) { 827 message_loop_->PostTask(FROM_HERE, base::Bind( 828 &Client::NotifyEndOfBitstreamBuffer, client_, 829 input_buffers_.front()->id)); 830 input_buffers_.pop(); 831 } 832 833 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 834 &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this))); 835 836 input_ready_.Signal(); 837 surfaces_available_.Signal(); 838 } 839 840 void VaapiVideoDecodeAccelerator::FinishReset() { 841 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 842 DVLOG(1) << "FinishReset"; 843 base::AutoLock auto_lock(lock_); 844 845 if (state_ != kResetting) { 846 DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; 847 return; // We could've gotten destroyed already. 848 } 849 850 // Drop pending outputs. 851 while (!pending_output_cbs_.empty()) 852 pending_output_cbs_.pop(); 853 854 if (awaiting_va_surfaces_recycle_) { 855 // Decoder requested a new surface set while we were waiting for it to 856 // finish the last DecodeTask, running at the time of Reset(). 857 // Let the surface set change finish first before resetting. 858 message_loop_->PostTask(FROM_HERE, base::Bind( 859 &VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); 860 return; 861 } 862 863 num_stream_bufs_at_decoder_ = 0; 864 state_ = kIdle; 865 866 message_loop_->PostTask(FROM_HERE, base::Bind( 867 &Client::NotifyResetDone, client_)); 868 869 // The client might have given us new buffers via Decode() while we were 870 // resetting and might be waiting for our move, and not call Decode() anymore 871 // until we return something. Post a DecodeTask() so that we won't 872 // sleep forever waiting for Decode() in that case. Having two of them 873 // in the pipe is harmless, the additional one will return as soon as it sees 874 // that we are back in kDecoding state. 875 if (!input_buffers_.empty()) { 876 state_ = kDecoding; 877 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( 878 &VaapiVideoDecodeAccelerator::DecodeTask, 879 base::Unretained(this))); 880 } 881 882 DVLOG(1) << "Reset finished"; 883 } 884 885 void VaapiVideoDecodeAccelerator::Cleanup() { 886 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 887 888 if (state_ == kUninitialized || state_ == kDestroying) 889 return; 890 891 DVLOG(1) << "Destroying VAVDA"; 892 base::AutoLock auto_lock(lock_); 893 state_ = kDestroying; 894 895 client_ptr_factory_.reset(); 896 weak_this_factory_.InvalidateWeakPtrs(); 897 898 // Signal all potential waiters on the decoder_thread_, let them early-exit, 899 // as we've just moved to the kDestroying state, and wait for all tasks 900 // to finish. 901 input_ready_.Signal(); 902 surfaces_available_.Signal(); 903 { 904 base::AutoUnlock auto_unlock(lock_); 905 decoder_thread_.Stop(); 906 } 907 908 state_ = kUninitialized; 909 } 910 911 void VaapiVideoDecodeAccelerator::Destroy() { 912 DCHECK_EQ(message_loop_, base::MessageLoop::current()); 913 Cleanup(); 914 delete this; 915 } 916 917 bool VaapiVideoDecodeAccelerator::CanDecodeOnIOThread() { 918 return false; 919 } 920 921 } // namespace content 922