df1f6443421d197b67aa31ef61634307175a242d
[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       LOG(ERROR) << 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   LOG(ERROR) << "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     LOG(ERROR) << "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     LOG(ERROR) << "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     LOG(ERROR) << "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     LOG(ERROR) << "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     LOG(ERROR) << "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