Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: content/common/gpu/media/vaapi_video_decode_accelerator.cc

Issue 9814001: Add VAVDA, the VAAPI Video Decode Accelerator for Intel CPUs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « content/common/gpu/media/vaapi_video_decode_accelerator.h ('k') | content/content_common.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/bind.h"
6 #include "base/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/video/picture.h"
17 #include "third_party/libva/va/va.h"
18 #include "ui/gl/gl_bindings.h"
19
20 #define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
21 do { \
22 if (!(result)) { \
23 DVLOG(1) << log; \
24 Destroy(); \
25 NotifyError(error_code); \
26 return ret; \
27 } \
28 } while (0)
29
30 using content::VaapiH264Decoder;
31
32 VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() {
33 }
34
35 VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() {
36 }
37
38 void VaapiVideoDecodeAccelerator::NotifyError(Error error) {
39 if (message_loop_ != MessageLoop::current()) {
40 message_loop_->PostTask(FROM_HERE, base::Bind(
41 &VaapiVideoDecodeAccelerator::NotifyError, this, error));
42 return;
43 }
44
45 DVLOG(1) << "Notifying of error " << error;
46
47 if (client_)
48 client_->NotifyError(error);
49 client_ = NULL;
50 }
51
52 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(Client* client)
53 : state_(kUninitialized),
54 input_ready_(&lock_),
55 output_ready_(&lock_),
56 message_loop_(MessageLoop::current()),
57 client_(client),
58 decoder_thread_("VaapiDecoderThread") {
59 DCHECK(client_);
60 }
61
62 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
63 DCHECK_EQ(message_loop_, MessageLoop::current());
64 }
65
66 bool VaapiVideoDecodeAccelerator::Initialize(
67 media::VideoCodecProfile profile) {
68 DCHECK_EQ(message_loop_, MessageLoop::current());
69
70 base::AutoLock auto_lock(lock_);
71 DCHECK_EQ(state_, kUninitialized);
72 DVLOG(2) << "Initializing VAVDA, profile: " << profile;
73
74 // TODO(posciak): try moving the flag check up to higher layers, possibly
75 // out of the GPU process.
76 bool res = CommandLine::ForCurrentProcess()->HasSwitch(
77 switches::kEnableVaapi);
78 RETURN_AND_NOTIFY_ON_FAILURE(res, "Vaapi HW acceleration disabled",
79 PLATFORM_FAILURE, false);
80
81 res = decoder_.Initialize(
82 profile, x_display_, glx_context_,
83 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicCallback, this));
84 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed initializing decoder",
85 PLATFORM_FAILURE, false);
86
87 res = decoder_thread_.Start();
88 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed starting decoder thread",
89 PLATFORM_FAILURE, false);
90
91 state_ = kInitialized;
92
93 message_loop_->PostTask(FROM_HERE, base::Bind(
94 &VaapiVideoDecodeAccelerator::NotifyInitializeDone, this));
95 return true;
96 }
97
98 void VaapiVideoDecodeAccelerator::NotifyInitializeDone() {
99 DCHECK_EQ(message_loop_, MessageLoop::current());
100 client_->NotifyInitializeDone();
101 }
102
103 // TODO(posciak, fischman): try to move these to constructor parameters,
104 // but while removing SetEglState from OVDA as well for symmetry.
105 void VaapiVideoDecodeAccelerator::SetGlxState(Display* x_display,
106 GLXContext glx_context) {
107 DCHECK_EQ(message_loop_, MessageLoop::current());
108 x_display_ = x_display;
109 glx_context_ = glx_context;
110 }
111
112 void VaapiVideoDecodeAccelerator::NotifyInputBufferRead(int input_buffer_id) {
113 DCHECK_EQ(message_loop_, MessageLoop::current());
114
115 DVLOG(4) << "Notifying end of input buffer " << input_buffer_id;
116 if (client_)
117 client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
118 }
119
120 void VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady(int32 input_id,
121 int32 output_id) {
122 DCHECK_EQ(message_loop_, MessageLoop::current());
123
124 // Sync the contents of the texture.
125 RETURN_AND_NOTIFY_ON_FAILURE(decoder_.PutPicToTexture(output_id),
126 "Failed putting picture to texture",
127 PLATFORM_FAILURE, );
128
129 // And notify the client a picture is ready to be displayed.
130 media::Picture picture(output_id, input_id);
131 DVLOG(4) << "Notifying output picture id " << output_id
132 << " for input "<< input_id << " is ready";
133 if (client_)
134 client_->PictureReady(picture);
135 }
136
137 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
138 const media::BitstreamBuffer& bitstream_buffer) {
139 DCHECK_EQ(message_loop_, MessageLoop::current());
140
141 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
142 << " size: " << (int)bitstream_buffer.size();
143
144 scoped_ptr<base::SharedMemory> shm(
145 new base::SharedMemory(bitstream_buffer.handle(), true));
146 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
147 "Failed to map input buffer", UNREADABLE_INPUT,);
148
149 // Set up a new input buffer and queue it for later.
150 linked_ptr<InputBuffer> input_buffer(new InputBuffer());
151 input_buffer->shm.reset(shm.release());
152 input_buffer->id = bitstream_buffer.id();
153 input_buffer->size = bitstream_buffer.size();
154
155 base::AutoLock auto_lock(lock_);
156 input_buffers_.push(input_buffer);
157 input_ready_.Signal();
158 }
159
160 void VaapiVideoDecodeAccelerator::InitialDecodeTask() {
161 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
162
163 // Try to initialize or resume playback after reset.
164 for (;;) {
165 if (!GetInputBuffer())
166 return;
167 DCHECK(curr_input_buffer_.get());
168
169 VaapiH264Decoder::DecResult res = decoder_.DecodeInitial(
170 curr_input_buffer_->id);
171 switch (res) {
172 case VaapiH264Decoder::kReadyToDecode:
173 message_loop_->PostTask(FROM_HERE, base::Bind(
174 &VaapiVideoDecodeAccelerator::ReadyToDecode, this,
175 decoder_.GetRequiredNumOfPictures(),
176 gfx::Size(decoder_.pic_width(), decoder_.pic_height())));
177 return;
178
179 case VaapiH264Decoder::kNeedMoreStreamData:
180 ReturnCurrInputBuffer();
181 break;
182
183 case VaapiH264Decoder::kDecodeError:
184 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error in decoding",
185 PLATFORM_FAILURE, );
186
187 default:
188 RETURN_AND_NOTIFY_ON_FAILURE(false,
189 "Unexpected result from decoder: " << res,
190 PLATFORM_FAILURE, );
191 }
192 }
193 }
194
195 bool VaapiVideoDecodeAccelerator::GetInputBuffer() {
196 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
197
198 base::AutoLock auto_lock(lock_);
199
200 if (curr_input_buffer_.get())
201 return true;
202
203 // Will only wait if it is expected that in current state new buffers will
204 // be queued from the client via Decode(). The state can change during wait.
205 while (input_buffers_.empty() &&
206 (state_ == kDecoding || state_ == kInitialized || state_ == kIdle)) {
207 input_ready_.Wait();
208 }
209
210 // We could have got woken up in a different state or never got to sleep
211 // due to current state; check for that.
212 switch (state_) {
213 case kFlushing:
214 // Here we are only interested in finishing up decoding buffers that are
215 // already queued up. Otherwise will stop decoding.
216 if (input_buffers_.empty())
217 return false;
218 // else fallthrough
219 case kDecoding:
220 case kInitialized:
221 case kIdle:
222 DCHECK(!input_buffers_.empty());
223
224 curr_input_buffer_ = input_buffers_.front();
225 input_buffers_.pop();
226
227 DVLOG(4) << "New current bitstream buffer, id: "
228 << curr_input_buffer_->id
229 << " size: " << curr_input_buffer_->size;
230
231 decoder_.SetStream(
232 static_cast<uint8*>(curr_input_buffer_->shm->memory()),
233 curr_input_buffer_->size);
234 return true;
235
236 default:
237 // We got woken up due to being destroyed/reset, ignore any already
238 // queued inputs.
239 return false;
240 }
241 }
242
243 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer() {
244 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
245
246 base::AutoLock auto_lock(lock_);
247 DCHECK(curr_input_buffer_.get());
248 int32 id = curr_input_buffer_->id;
249 curr_input_buffer_.reset();
250 message_loop_->PostTask(FROM_HERE, base::Bind(
251 &VaapiVideoDecodeAccelerator::NotifyInputBufferRead, this, id));
252 }
253
254 bool VaapiVideoDecodeAccelerator::GetOutputBuffers() {
255 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
256
257 base::AutoLock auto_lock(lock_);
258
259 while (output_buffers_.empty() &&
260 (state_ == kDecoding || state_ == kFlushing)) {
261 output_ready_.Wait();
262 }
263
264 if (state_ != kDecoding && state_ != kFlushing)
265 return false;
266
267 while (!output_buffers_.empty()) {
268 decoder_.ReusePictureBuffer(output_buffers_.front());
269 output_buffers_.pop();
270 }
271
272 return true;
273 }
274
275 void VaapiVideoDecodeAccelerator::DecodeTask() {
276 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
277
278 // Main decode task.
279 DVLOG(4) << "Decode task";
280
281 // Try to decode what stream data is (still) in the decoder until we run out
282 // of it.
283 for (;;) {
284 if (!GetInputBuffer())
285 // Early exit requested.
286 return;
287 DCHECK(curr_input_buffer_.get());
288
289 VaapiH264Decoder::DecResult res =
290 decoder_.DecodeOneFrame(curr_input_buffer_->id);
291 switch (res) {
292 case VaapiH264Decoder::kNeedMoreStreamData:
293 ReturnCurrInputBuffer();
294 break;
295
296 case VaapiH264Decoder::kDecodedFrame:
297 // May still have more stream data, continue decoding.
298 break;
299
300 case VaapiH264Decoder::kNoOutputAvailable:
301 // No more output buffers in the decoder, try getting more or go to
302 // sleep waiting for them.
303 if (!GetOutputBuffers())
304 return;
305 break;
306
307 case VaapiH264Decoder::kDecodeError:
308 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
309 PLATFORM_FAILURE, );
310 return;
311
312 default:
313 RETURN_AND_NOTIFY_ON_FAILURE(
314 false, "Unexpected result from the decoder: " << res,
315 PLATFORM_FAILURE, );
316 return;
317 }
318 }
319 }
320
321 void VaapiVideoDecodeAccelerator::ReadyToDecode(int num_pics,
322 const gfx::Size& size) {
323 DCHECK_EQ(message_loop_, MessageLoop::current());
324
325 base::AutoLock auto_lock(lock_);
326 switch (state_) {
327 case kInitialized:
328 DVLOG(1) << "Requesting " << num_pics << " pictures of size: "
329 << size.width() << "x" << size.height();
330 if (client_)
331 client_->ProvidePictureBuffers(num_pics, size);
332 state_ = kPicturesRequested;
333 break;
334 case kIdle:
335 state_ = kDecoding;
336 decoder_thread_.message_loop()->PostTask(FROM_HERE,
337 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
338 break;
339 default:
340 NOTREACHED() << "Invalid state";
341 }
342 }
343
344 void VaapiVideoDecodeAccelerator::Decode(
345 const media::BitstreamBuffer& bitstream_buffer) {
346 DCHECK_EQ(message_loop_, MessageLoop::current());
347
348 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
349 bitstream_buffer.id());
350
351 // We got a new input buffer from the client, map it and queue for later use.
352 MapAndQueueNewInputBuffer(bitstream_buffer);
353
354 base::AutoLock auto_lock(lock_);
355 switch (state_) {
356 case kInitialized:
357 // Initial decode to get the required size of output buffers.
358 decoder_thread_.message_loop()->PostTask(FROM_HERE,
359 base::Bind(&VaapiVideoDecodeAccelerator::InitialDecodeTask, this));
360 break;
361
362 case kPicturesRequested:
363 // Waiting for pictures, return.
364 break;
365
366 case kDecoding:
367 break;
368
369 case kIdle:
370 // Need to get decoder into suitable stream location to resume.
371 decoder_thread_.message_loop()->PostTask(FROM_HERE,
372 base::Bind(&VaapiVideoDecodeAccelerator::InitialDecodeTask, this));
373 break;
374
375 default:
376 DVLOG(1) << "Decode request from client in invalid state: " << state_;
377 return;
378 }
379 }
380
381 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
382 const std::vector<media::PictureBuffer>& buffers) {
383 DCHECK_EQ(message_loop_, MessageLoop::current());
384
385 base::AutoLock auto_lock(lock_);
386 DCHECK_EQ(state_, kPicturesRequested);
387
388 for (size_t i = 0; i < buffers.size(); ++i) {
389 DVLOG(2) << "Assigning picture id " << buffers[i].id()
390 << " to texture id " << buffers[i].texture_id();
391
392 bool res = decoder_.AssignPictureBuffer(buffers[i].id(),
393 buffers[i].texture_id());
394 RETURN_AND_NOTIFY_ON_FAILURE(
395 res, "Failed assigning picture buffer id: " << buffers[i].id() <<
396 ", texture id: " << buffers[i].texture_id(), PLATFORM_FAILURE, );
397 }
398
399 state_ = kDecoding;
400 decoder_thread_.message_loop()->PostTask(FROM_HERE,
401 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
402 }
403
404 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
405 DCHECK_EQ(message_loop_, MessageLoop::current());
406 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id",
407 picture_buffer_id);
408
409 base::AutoLock auto_lock(lock_);
410 output_buffers_.push(picture_buffer_id);
411 output_ready_.Signal();
412 }
413
414 void VaapiVideoDecodeAccelerator::FlushTask() {
415 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
416 DVLOG(1) << "Flush task";
417
418 // First flush all the pictures that haven't been outputted, notifying the
419 // client to output them.
420 bool res = decoder_.Flush();
421 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.",
422 PLATFORM_FAILURE, );
423
424 // Put the decoder in idle state, ready to resume.
425 decoder_.Reset();
426
427 message_loop_->PostTask(FROM_HERE,
428 base::Bind(&VaapiVideoDecodeAccelerator::FinishFlush, this));
429 }
430
431 void VaapiVideoDecodeAccelerator::Flush() {
432 DCHECK_EQ(message_loop_, MessageLoop::current());
433 DVLOG(1) << "Got flush request";
434
435 base::AutoLock auto_lock(lock_);
436 state_ = kFlushing;
437 // Queue a flush task after all existing decoding tasks to clean up.
438 decoder_thread_.message_loop()->PostTask(FROM_HERE,
439 base::Bind(&VaapiVideoDecodeAccelerator::FlushTask, this));
440
441 input_ready_.Signal();
442 output_ready_.Signal();
443 }
444
445 void VaapiVideoDecodeAccelerator::FinishFlush() {
446 DCHECK_EQ(message_loop_, MessageLoop::current());
447
448 base::AutoLock auto_lock(lock_);
449 if (state_ != kFlushing) {
450 DCHECK_EQ(state_, kDestroying);
451 return; // We could've gotten destroyed already.
452 }
453
454 state_ = kIdle;
455
456 if (client_)
457 client_->NotifyFlushDone();
458
459 DVLOG(1) << "Flush finished";
460 }
461
462 void VaapiVideoDecodeAccelerator::ResetTask() {
463 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
464
465 // All the decoding tasks from before the reset request from client are done
466 // by now, as this task was scheduled after them and client is expected not
467 // to call Decode() after Reset() and before NotifyResetDone.
468 decoder_.Reset();
469
470 // Return current input buffer, if present.
471 if (curr_input_buffer_.get())
472 ReturnCurrInputBuffer();
473
474 // And let client know that we are done with reset.
475 message_loop_->PostTask(FROM_HERE, base::Bind(
476 &VaapiVideoDecodeAccelerator::FinishReset, this));
477 }
478
479 void VaapiVideoDecodeAccelerator::Reset() {
480 DCHECK_EQ(message_loop_, MessageLoop::current());
481 DVLOG(1) << "Got reset request";
482
483 // This will make any new decode tasks exit early.
484 base::AutoLock auto_lock(lock_);
485 state_ = kResetting;
486
487 decoder_thread_.message_loop()->PostTask(FROM_HERE,
488 base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, this));
489
490 input_ready_.Signal();
491 output_ready_.Signal();
492 }
493
494 void VaapiVideoDecodeAccelerator::FinishReset() {
495 DCHECK_EQ(message_loop_, MessageLoop::current());
496
497 base::AutoLock auto_lock(lock_);
498 if (state_ != kResetting) {
499 DCHECK_EQ(state_, kDestroying);
500 return; // We could've gotten destroyed already.
501 }
502
503 // Drop all remaining input buffers, if present.
504 while (!input_buffers_.empty())
505 input_buffers_.pop();
506
507 state_ = kIdle;
508
509 if (client_)
510 client_->NotifyResetDone();
511
512 DVLOG(1) << "Reset finished";
513 }
514
515 void VaapiVideoDecodeAccelerator::DestroyTask() {
516 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
517
518 DVLOG(1) << "DestroyTask";
519 base::AutoLock auto_lock(lock_);
520
521 // This is a dummy task to ensure that all tasks on decoder thread have
522 // finished, so we can destroy it from ChildThread, as we need to do that
523 // on the thread that has the GLX context.
524
525 message_loop_->PostTask(FROM_HERE, base::Bind(
526 &VaapiVideoDecodeAccelerator::FinishDestroy, this));
527 }
528
529 void VaapiVideoDecodeAccelerator::Destroy() {
530 if (message_loop_ != MessageLoop::current()) {
531 message_loop_->PostTask(FROM_HERE, base::Bind(
532 &VaapiVideoDecodeAccelerator::Destroy, this));
533 return;
534 }
535
536 if (state_ == kUninitialized || state_ == kDestroying)
537 return;
538
539 DVLOG(1) << "Destroying VAVDA";
540 base::AutoLock auto_lock(lock_);
541 state_ = kDestroying;
542 decoder_thread_.message_loop()->PostTask(FROM_HERE,
543 base::Bind(&VaapiVideoDecodeAccelerator::DestroyTask, this));
544 client_ = NULL;
545
546 input_ready_.Signal();
547 output_ready_.Signal();
548 }
549
550 void VaapiVideoDecodeAccelerator::FinishDestroy() {
551 DCHECK_EQ(message_loop_, MessageLoop::current());
552 base::AutoLock auto_lock(lock_);
553 // Called from here as we need to be on the thread that has the GLX context
554 // as current.
555 decoder_.Destroy();
556 state_ = kUninitialized;
557 }
558
559 void VaapiVideoDecodeAccelerator::OutputPicCallback(int32 input_id,
560 int32 output_id) {
561 TRACE_EVENT2("Video Decoder", "VAVDA::OutputPicCallback",
562 "Input id", input_id, "Picture id", output_id);
563 DVLOG(4) << "Outputting picture, input id: " << input_id
564 << " output id: " << output_id;
565
566 // Forward the request to the main thread.
567 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
568 message_loop_->PostTask(FROM_HERE,
569 base::Bind(&VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady,
570 this, input_id, output_id));
571 }
572
OLDNEW
« no previous file with comments | « content/common/gpu/media/vaapi_video_decode_accelerator.h ('k') | content/content_common.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698