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