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