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