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