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

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

Powered by Google App Engine
This is Rietveld 408576698