blob: 679162c099c427459f17deeb6acd1f8e1edcde9e [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "base/command_line.h"
7#include "base/debug/trace_event.h"
8#include "base/logging.h"
9#include "base/stl_util.h"
10#include "base/string_util.h"
11#include "base/synchronization/waitable_event.h"
12#include "gpu/command_buffer/service/gpu_switches.h"
13#include "content/public/common/content_switches.h"
14#include "content/common/gpu/gpu_channel.h"
15#include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
16#include "media/base/bind_to_loop.h"
17#include "media/video/picture.h"
18#include "third_party/libva/va/va.h"
19#include "ui/gl/gl_bindings.h"
20
21namespace content {
22
23#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
24 do { \
25 if (!(result)) { \
26 DVLOG(1) << log; \
27 NotifyError(error_code); \
28 return ret; \
29 } \
30 } while (0)
31
32VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) {
33}
34
35VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() {
36}
37
38void VaapiVideoDecodeAccelerator::NotifyError(Error error) {
39 if (message_loop_ != MessageLoop::current()) {
40 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
41 message_loop_->PostTask(FROM_HERE, base::Bind(
42 &VaapiVideoDecodeAccelerator::NotifyError, weak_this_, error));
43 return;
44 }
45
46 // Post Cleanup() as a task so we don't recursively acquire lock_.
47 message_loop_->PostTask(FROM_HERE, base::Bind(
48 &VaapiVideoDecodeAccelerator::Cleanup, weak_this_));
49
50 DVLOG(1) << "Notifying of error " << error;
51 if (client_) {
52 client_->NotifyError(error);
53 client_ptr_factory_.InvalidateWeakPtrs();
54 }
55}
56
57VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
58 Display* x_display, GLXContext glx_context,
59 Client* client,
60 const base::Callback<bool(void)>& make_context_current)
61 : x_display_(x_display),
62 glx_context_(glx_context),
63 make_context_current_(make_context_current),
64 state_(kUninitialized),
65 input_ready_(&lock_),
66 output_ready_(&lock_),
67 message_loop_(MessageLoop::current()),
68 weak_this_(base::AsWeakPtr(this)),
69 client_ptr_factory_(client),
70 client_(client_ptr_factory_.GetWeakPtr()),
71 decoder_thread_("VaapiDecoderThread"),
72 num_frames_at_client_(0),
73 num_stream_bufs_at_decoder_(0) {
74 DCHECK(client);
75 static bool vaapi_functions_initialized = PostSandboxInitialization();
76 RETURN_AND_NOTIFY_ON_FAILURE(vaapi_functions_initialized,
77 "Failed to initialize VAAPI libs",
78 PLATFORM_FAILURE, );
79}
80
81VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
82 DCHECK_EQ(message_loop_, MessageLoop::current());
83}
84
85bool VaapiVideoDecodeAccelerator::Initialize(
86 media::VideoCodecProfile profile) {
87 DCHECK_EQ(message_loop_, MessageLoop::current());
88
89 base::AutoLock auto_lock(lock_);
90 DCHECK_EQ(state_, kUninitialized);
91 DVLOG(2) << "Initializing VAVDA, profile: " << profile;
92
93 bool res = decoder_.Initialize(
94 profile, x_display_, glx_context_, make_context_current_,
95 media::BindToLoop(message_loop_->message_loop_proxy(), base::Bind(
96 &VaapiVideoDecodeAccelerator::NotifyPictureReady, weak_this_)),
97 media::BindToLoop(message_loop_->message_loop_proxy(), base::Bind(
98 &VaapiVideoDecodeAccelerator::SubmitDecode, weak_this_)));
99 if (!res) {
100 DVLOG(1) << "Failed initializing decoder";
101 return false;
102 }
103
104 CHECK(decoder_thread_.Start());
105
106 state_ = kInitialized;
107
108 message_loop_->PostTask(FROM_HERE, base::Bind(
109 &Client::NotifyInitializeDone, client_));
110 return true;
111}
112
113void VaapiVideoDecodeAccelerator::SubmitDecode(
114 int32 output_id,
115 scoped_ptr<std::queue<VABufferID> > va_bufs,
116 scoped_ptr<std::queue<VABufferID> > slice_bufs) {
117 DCHECK_EQ(message_loop_, MessageLoop::current());
118
119 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "output_id", output_id);
120
121 // Handle Destroy() arriving while pictures are queued for output.
122 if (!client_)
123 return;
124
125 RETURN_AND_NOTIFY_ON_FAILURE(
126 decoder_.SubmitDecode(output_id, va_bufs.Pass(), slice_bufs.Pass()),
127 "Failed putting picture to texture",
128 PLATFORM_FAILURE, );
129}
130
131void VaapiVideoDecodeAccelerator::NotifyPictureReady(int32 input_id,
132 int32 output_id) {
133 DCHECK_EQ(message_loop_, MessageLoop::current());
134 TRACE_EVENT2("Video Decoder", "VAVDA::NotifyPictureReady",
135 "input_id", input_id, "output_id", output_id);
136
137 // Handle Destroy() arriving while pictures are queued for output.
138 if (!client_)
139 return;
140
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000141 // Don't return any pictures that we might want to return during resetting
142 // as a consequence of finishing up the decode that was running during
143 // Reset() call from the client. Reuse it instead.
144 {
145 base::AutoLock auto_lock(lock_);
146 if (state_ == kResetting) {
147 output_buffers_.push(output_id);
148 return;
149 }
150 }
151
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000152 ++num_frames_at_client_;
153 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
154
155 // Notify the client a picture is ready to be displayed.
156 DVLOG(4) << "Notifying output picture id " << output_id
157 << " for input "<< input_id << " is ready";
158 client_->PictureReady(media::Picture(output_id, input_id));
159}
160
161void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
162 const media::BitstreamBuffer& bitstream_buffer) {
163 DCHECK_EQ(message_loop_, MessageLoop::current());
164 TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id",
165 bitstream_buffer.id());
166
167 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
168 << " size: " << (int)bitstream_buffer.size();
169
170 scoped_ptr<base::SharedMemory> shm(
171 new base::SharedMemory(bitstream_buffer.handle(), true));
172 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
173 "Failed to map input buffer", UNREADABLE_INPUT,);
174
175 base::AutoLock auto_lock(lock_);
176
177 // Set up a new input buffer and queue it for later.
178 linked_ptr<InputBuffer> input_buffer(new InputBuffer());
179 input_buffer->shm.reset(shm.release());
180 input_buffer->id = bitstream_buffer.id();
181 input_buffer->size = bitstream_buffer.size();
182
183 ++num_stream_bufs_at_decoder_;
184 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
185 num_stream_bufs_at_decoder_);
186
187 input_buffers_.push(input_buffer);
188 input_ready_.Signal();
189}
190
191void VaapiVideoDecodeAccelerator::InitialDecodeTask() {
192 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
193 base::AutoLock auto_lock(lock_);
194
195 // Try to initialize or resume playback after reset.
196 while (GetInputBuffer_Locked()) {
197 DCHECK(curr_input_buffer_.get());
198
199 // Since multiple Decode()'s can be in flight at once, it's possible that a
200 // Decode() that seemed like an initial one is actually later in the stream
201 // and we're already kDecoding. Let the normal DecodeTask take over in that
202 // case.
203 if (state_ != kInitialized && state_ != kIdle)
204 return;
205
206 VaapiH264Decoder::DecResult res =
207 decoder_.DecodeInitial(curr_input_buffer_->id);
208 switch (res) {
209 case VaapiH264Decoder::kReadyToDecode:
210 if (state_ == kInitialized) {
211 state_ = kPicturesRequested;
212 int num_pics = decoder_.GetRequiredNumOfPictures();
213 gfx::Size size(decoder_.pic_width(), decoder_.pic_height());
214 DVLOG(1) << "Requesting " << num_pics << " pictures of size: "
215 << size.width() << "x" << size.height();
216 message_loop_->PostTask(FROM_HERE, base::Bind(
217 &Client::ProvidePictureBuffers, client_,
218 num_pics, size, GL_TEXTURE_2D));
219 } else {
220 DCHECK_EQ(state_, kIdle);
221 state_ = kDecoding;
222 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
223 &VaapiVideoDecodeAccelerator::DecodeTask,
224 base::Unretained(this)));
225 }
226 return;
227
228 case VaapiH264Decoder::kNeedMoreStreamData:
229 ReturnCurrInputBuffer_Locked();
230 break;
231
232 case VaapiH264Decoder::kNoOutputAvailable:
233 if (state_ == kIdle) {
234 // No more output buffers in the decoder, try getting more or go to
235 // sleep waiting for them.
236 GetOutputBuffers_Locked();
237 return;
238 }
239 // else fallthrough
240 case VaapiH264Decoder::kDecodeError:
241 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error in decoding",
242 PLATFORM_FAILURE, );
243
244 default:
245 RETURN_AND_NOTIFY_ON_FAILURE(false,
246 "Unexpected result from decoder: " << res,
247 PLATFORM_FAILURE, );
248 }
249 }
250}
251
252bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
253 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
254 lock_.AssertAcquired();
255
256 if (curr_input_buffer_.get())
257 return true;
258
259 // Will only wait if it is expected that in current state new buffers will
260 // be queued from the client via Decode(). The state can change during wait.
261 while (input_buffers_.empty() &&
262 (state_ == kDecoding || state_ == kInitialized || state_ == kIdle)) {
263 input_ready_.Wait();
264 }
265
266 // We could have got woken up in a different state or never got to sleep
267 // due to current state; check for that.
268 switch (state_) {
269 case kFlushing:
270 // Here we are only interested in finishing up decoding buffers that are
271 // already queued up. Otherwise will stop decoding.
272 if (input_buffers_.empty())
273 return false;
274 // else fallthrough
275 case kDecoding:
276 case kInitialized:
277 case kIdle:
278 DCHECK(!input_buffers_.empty());
279
280 curr_input_buffer_ = input_buffers_.front();
281 input_buffers_.pop();
282
283 DVLOG(4) << "New current bitstream buffer, id: "
284 << curr_input_buffer_->id
285 << " size: " << curr_input_buffer_->size;
286
287 decoder_.SetStream(
288 static_cast<uint8*>(curr_input_buffer_->shm->memory()),
289 curr_input_buffer_->size);
290 return true;
291
292 default:
293 // We got woken up due to being destroyed/reset, ignore any already
294 // queued inputs.
295 return false;
296 }
297}
298
299void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() {
300 lock_.AssertAcquired();
301 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
302 DCHECK(curr_input_buffer_.get());
303
304 int32 id = curr_input_buffer_->id;
305 curr_input_buffer_.reset();
306 DVLOG(4) << "End of input buffer " << id;
307 message_loop_->PostTask(FROM_HERE, base::Bind(
308 &Client::NotifyEndOfBitstreamBuffer, client_, id));
309
310 --num_stream_bufs_at_decoder_;
311 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
312 num_stream_bufs_at_decoder_);
313}
314
315bool VaapiVideoDecodeAccelerator::GetOutputBuffers_Locked() {
316 lock_.AssertAcquired();
317 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
318
319 while (output_buffers_.empty() &&
320 (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) {
321 output_ready_.Wait();
322 }
323
324 if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle)
325 return false;
326
327 while (!output_buffers_.empty()) {
328 decoder_.ReusePictureBuffer(output_buffers_.front());
329 output_buffers_.pop();
330 }
331
332 return true;
333}
334
335void VaapiVideoDecodeAccelerator::DecodeTask() {
336 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
337 TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask");
338 base::AutoLock auto_lock(lock_);
339
340 // Main decode task.
341 DVLOG(4) << "Decode task";
342
343 // Try to decode what stream data is (still) in the decoder until we run out
344 // of it.
345 while (GetInputBuffer_Locked()) {
346 DCHECK(curr_input_buffer_.get());
347
348 VaapiH264Decoder::DecResult res;
349 {
350 base::AutoUnlock auto_unlock(lock_);
351 res = decoder_.DecodeOneFrame(curr_input_buffer_->id);
352 }
353
354 switch (res) {
355 case VaapiH264Decoder::kNeedMoreStreamData:
356 ReturnCurrInputBuffer_Locked();
357 break;
358
359 case VaapiH264Decoder::kDecodedFrame:
360 // May still have more stream data, continue decoding.
361 break;
362
363 case VaapiH264Decoder::kNoOutputAvailable:
364 // No more output buffers in the decoder, try getting more or go to
365 // sleep waiting for them.
366 if (!GetOutputBuffers_Locked())
367 return;
368 break;
369
370 case VaapiH264Decoder::kDecodeError:
371 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
372 PLATFORM_FAILURE, );
373 return;
374
375 default:
376 RETURN_AND_NOTIFY_ON_FAILURE(
377 false, "Unexpected result from the decoder: " << res,
378 PLATFORM_FAILURE, );
379 return;
380 }
381 }
382}
383
384void VaapiVideoDecodeAccelerator::Decode(
385 const media::BitstreamBuffer& bitstream_buffer) {
386 DCHECK_EQ(message_loop_, MessageLoop::current());
387
388 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
389 bitstream_buffer.id());
390
391 // We got a new input buffer from the client, map it and queue for later use.
392 MapAndQueueNewInputBuffer(bitstream_buffer);
393
394 base::AutoLock auto_lock(lock_);
395 switch (state_) {
396 case kInitialized:
397 // Initial decode to get the required size of output buffers.
398 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
399 &VaapiVideoDecodeAccelerator::InitialDecodeTask,
400 base::Unretained(this)));
401 break;
402
403 case kPicturesRequested:
404 // Waiting for pictures, return.
405 break;
406
407 case kDecoding:
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000408 // Allow accumulating bitstream buffers so that the client can queue
409 // after-seek-buffers while we are finishing with the before-seek one.
410 case kResetting:
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000411 break;
412
413 case kIdle:
414 // Need to get decoder into suitable stream location to resume.
415 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
416 &VaapiVideoDecodeAccelerator::InitialDecodeTask,
417 base::Unretained(this)));
418 break;
419
420 default:
421 RETURN_AND_NOTIFY_ON_FAILURE(false,
422 "Decode request from client in invalid state: " << state_,
423 PLATFORM_FAILURE, );
424 return;
425 }
426}
427
428void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
429 const std::vector<media::PictureBuffer>& buffers) {
430 DCHECK_EQ(message_loop_, MessageLoop::current());
431
432 base::AutoLock auto_lock(lock_);
433 DCHECK_EQ(state_, kPicturesRequested);
434
435 for (size_t i = 0; i < buffers.size(); ++i) {
436 DVLOG(2) << "Assigning picture id " << buffers[i].id()
437 << " to texture id " << buffers[i].texture_id();
438
439 bool res = decoder_.AssignPictureBuffer(buffers[i].id(),
440 buffers[i].texture_id());
441 RETURN_AND_NOTIFY_ON_FAILURE(
442 res, "Failed assigning picture buffer id: " << buffers[i].id() <<
443 ", texture id: " << buffers[i].texture_id(), PLATFORM_FAILURE, );
444 }
445
446 state_ = kDecoding;
447 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
448 &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this)));
449}
450
451void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
452 DCHECK_EQ(message_loop_, MessageLoop::current());
453 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id",
454 picture_buffer_id);
455
456 --num_frames_at_client_;
457 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
458
459 base::AutoLock auto_lock(lock_);
460 output_buffers_.push(picture_buffer_id);
461 output_ready_.Signal();
462}
463
464void VaapiVideoDecodeAccelerator::FlushTask() {
465 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
466 DVLOG(1) << "Flush task";
467
468 // First flush all the pictures that haven't been outputted, notifying the
469 // client to output them.
470 bool res = decoder_.Flush();
471 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.",
472 PLATFORM_FAILURE, );
473
474 // Put the decoder in idle state, ready to resume.
475 decoder_.Reset();
476
477 message_loop_->PostTask(FROM_HERE, base::Bind(
478 &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_));
479}
480
481void VaapiVideoDecodeAccelerator::Flush() {
482 DCHECK_EQ(message_loop_, MessageLoop::current());
483 DVLOG(1) << "Got flush request";
484
485 base::AutoLock auto_lock(lock_);
486 state_ = kFlushing;
487 // Queue a flush task after all existing decoding tasks to clean up.
488 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
489 &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
490
491 input_ready_.Signal();
492 output_ready_.Signal();
493}
494
495void VaapiVideoDecodeAccelerator::FinishFlush() {
496 DCHECK_EQ(message_loop_, MessageLoop::current());
497
498 base::AutoLock auto_lock(lock_);
499 if (state_ != kFlushing) {
500 DCHECK_EQ(state_, kDestroying);
501 return; // We could've gotten destroyed already.
502 }
503
504 state_ = kIdle;
505
506 message_loop_->PostTask(FROM_HERE, base::Bind(
507 &Client::NotifyFlushDone, client_));
508
509 DVLOG(1) << "Flush finished";
510}
511
512void VaapiVideoDecodeAccelerator::ResetTask() {
513 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
514
515 // All the decoding tasks from before the reset request from client are done
516 // by now, as this task was scheduled after them and client is expected not
517 // to call Decode() after Reset() and before NotifyResetDone.
518 decoder_.Reset();
519
520 base::AutoLock auto_lock(lock_);
521
522 // Return current input buffer, if present.
523 if (curr_input_buffer_.get())
524 ReturnCurrInputBuffer_Locked();
525
526 // And let client know that we are done with reset.
527 message_loop_->PostTask(FROM_HERE, base::Bind(
528 &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
529}
530
531void VaapiVideoDecodeAccelerator::Reset() {
532 DCHECK_EQ(message_loop_, MessageLoop::current());
533 DVLOG(1) << "Got reset request";
534
535 // This will make any new decode tasks exit early.
536 base::AutoLock auto_lock(lock_);
537 state_ = kResetting;
538
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000539 // Drop all remaining input buffers, if present.
540 while (!input_buffers_.empty()) {
541 message_loop_->PostTask(FROM_HERE, base::Bind(
542 &Client::NotifyEndOfBitstreamBuffer, client_,
543 input_buffers_.front()->id));
544 input_buffers_.pop();
545 }
546
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000547 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
548 &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this)));
549
550 input_ready_.Signal();
551 output_ready_.Signal();
552}
553
554void VaapiVideoDecodeAccelerator::FinishReset() {
555 DCHECK_EQ(message_loop_, MessageLoop::current());
556
557 base::AutoLock auto_lock(lock_);
558 if (state_ != kResetting) {
559 DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_;
560 return; // We could've gotten destroyed already.
561 }
562
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000563 state_ = kIdle;
564 num_stream_bufs_at_decoder_ = 0;
565
566 message_loop_->PostTask(FROM_HERE, base::Bind(
567 &Client::NotifyResetDone, client_));
568
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000569 // The client might have given us new buffers via Decode() while we were
570 // resetting and might be waiting for our move, and not call Decode() anymore
571 // until we return something. Post an InitialDecodeTask() so that we won't
572 // sleep forever waiting for Decode() in that case. Having two of them
573 // in the pipe is harmless, the additional one will return as soon as it sees
574 // that we are back in kDecoding state.
575 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
576 &VaapiVideoDecodeAccelerator::InitialDecodeTask,
577 base::Unretained(this)));
578
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000579 DVLOG(1) << "Reset finished";
580}
581
582void VaapiVideoDecodeAccelerator::Cleanup() {
583 DCHECK_EQ(message_loop_, MessageLoop::current());
584
585 if (state_ == kUninitialized || state_ == kDestroying)
586 return;
587
588 DVLOG(1) << "Destroying VAVDA";
589 base::AutoLock auto_lock(lock_);
590 state_ = kDestroying;
591
592 client_ptr_factory_.InvalidateWeakPtrs();
593
594 {
595 base::AutoUnlock auto_unlock(lock_);
596 // Post a dummy task to the decoder_thread_ to ensure it is drained.
597 base::WaitableEvent waiter(false, false);
598 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
599 &base::WaitableEvent::Signal, base::Unretained(&waiter)));
600 input_ready_.Signal();
601 output_ready_.Signal();
602 waiter.Wait();
603 decoder_thread_.Stop();
604 }
605
606 decoder_.Destroy();
607 state_ = kUninitialized;
608}
609
610void VaapiVideoDecodeAccelerator::Destroy() {
611 DCHECK_EQ(message_loop_, MessageLoop::current());
612 Cleanup();
613 delete this;
614}
615
616// static
617void VaapiVideoDecodeAccelerator::PreSandboxInitialization() {
618 VaapiH264Decoder::PreSandboxInitialization();
619}
620
621// static
622bool VaapiVideoDecodeAccelerator::PostSandboxInitialization() {
623 return VaapiH264Decoder::PostSandboxInitialization();
624}
625
626} // namespace content