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