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