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

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

Powered by Google App Engine
This is Rietveld 408576698