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