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

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 "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/gfx/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 void VaapiVideoDecodeAccelerator::NotifyError(Error error) {
33 if (message_loop_ != MessageLoop::current()) {
34 message_loop_->PostTask(FROM_HERE, base::Bind(
35 &VaapiVideoDecodeAccelerator::NotifyError, this, error));
36 return;
37 }
38
39 DVLOG(1) << "Notifying of error " << error;
40
41 if (client_)
42 client_->NotifyError(error);
43 client_ = NULL;
44 }
45
46 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(Client* client)
47 : state_(kUninitialized),
48 input_ready_(&lock_),
49 output_ready_(&lock_),
50 message_loop_(MessageLoop::current()),
51 client_(client),
52 decoder_thread_("VaapiDecoderThread") {
53 DCHECK(client_);
54 }
55
56 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
57 DCHECK_EQ(message_loop_, MessageLoop::current());
58 }
59
60 bool VaapiVideoDecodeAccelerator::Initialize(
61 media::VideoCodecProfile profile) {
62 DCHECK_EQ(message_loop_, MessageLoop::current());
63
64 base::AutoLock auto_lock(lock_);
65 DCHECK_EQ(state_, kUninitialized);
66 DVLOG(2) << "Initializing VAVDA, profile: " << profile;
67
68 // TODO(posciak): try moving the flag check up to higher layers, possibly
69 // out of the GPU process.
70 bool res = CommandLine::ForCurrentProcess()->HasSwitch(
71 switches::kEnableVaapi);
72 RETURN_AND_NOTIFY_ON_FAILURE(res, "Vaapi HW acceleration disabled",
73 PLATFORM_FAILURE, false);
74
75 res = decoder_.Initialize(
76 profile, x_display_, glx_context_,
77 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicCallback, this));
78 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed initializing decoder",
79 PLATFORM_FAILURE, false);
80
81 res = decoder_thread_.Start();
82 RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed starting decoder thread",
83 PLATFORM_FAILURE, false);
84
85 state_ = kInitialized;
86
87 message_loop_->PostTask(FROM_HERE, base::Bind(
88 &VaapiVideoDecodeAccelerator::NotifyInitializeDone, this));
89 return true;
90 }
91
92 void VaapiVideoDecodeAccelerator::NotifyInitializeDone() {
93 DCHECK_EQ(message_loop_, MessageLoop::current());
94 client_->NotifyInitializeDone();
95 }
96
97 // TODO(posciak, fischman): try to move these to constructor parameters,
98 // but while removing SetEglState from OVDA as well for symmetry.
99 void VaapiVideoDecodeAccelerator::SetGlxState(Display* x_display,
100 GLXContext glx_context) {
101 DCHECK_EQ(message_loop_, MessageLoop::current());
102 x_display_ = x_display;
103 glx_context_ = glx_context;
104 }
105
106 void VaapiVideoDecodeAccelerator::NotifyInputBufferRead(int input_buffer_id) {
107 DCHECK_EQ(message_loop_, MessageLoop::current());
108
109 DVLOG(4) << "Notifying end of input buffer " << input_buffer_id;
110 if (client_)
111 client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
112 }
113
114 void VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady(int32 input_id,
115 int32 output_id) {
116 DCHECK_EQ(message_loop_, MessageLoop::current());
117
118 // Sync the contents of the texture.
119 RETURN_AND_NOTIFY_ON_FAILURE(decoder_.PutPicToTexture(output_id),
120 "Failed putting picture to texture",
121 PLATFORM_FAILURE, );
122
123 // And notify the client a picture is ready to be displayed.
124 media::Picture picture(output_id, input_id);
125 DVLOG(4) << "Notifying output picture id " << output_id
126 << " for input "<< input_id << " is ready";
127 if (client_)
128 client_->PictureReady(picture);
129 }
130
131 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
132 const media::BitstreamBuffer& bitstream_buffer) {
133 DCHECK_EQ(message_loop_, MessageLoop::current());
134
135 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
136 << " size: " << (int)bitstream_buffer.size();
137
138 scoped_ptr<base::SharedMemory> shm(
139 new base::SharedMemory(bitstream_buffer.handle(), true));
140 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
141 "Failed to map input buffer", UNREADABLE_INPUT,);
142
143 // Set up a new input buffer and queue it for later.
144 linked_ptr<InputBuffer> input_buffer(new InputBuffer());
145 input_buffer->shm.reset(shm.release());
146 input_buffer->id = bitstream_buffer.id();
147 input_buffer->size = bitstream_buffer.size();
148
149 base::AutoLock auto_lock(lock_);
150 input_buffers_.push(input_buffer);
151 input_ready_.Signal();
152 }
153
154 void VaapiVideoDecodeAccelerator::InitialDecodeTask() {
155 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
156
157 // Try to initialize or resume playback after reset.
158 for (;;) {
159 if (!GetInputBuffer())
160 return;
161 DCHECK(curr_input_buffer_.get());
162
163 VaapiH264Decoder::DecResult res = decoder_.DecodeInitial(
164 curr_input_buffer_->id);
165 switch (res) {
166 case VaapiH264Decoder::kReadyToDecode:
167 message_loop_->PostTask(FROM_HERE, base::Bind(
168 &VaapiVideoDecodeAccelerator::ReadyToDecode, this,
169 decoder_.GetRequiredNumOfPictures(),
170 gfx::Size(decoder_.pic_width(), decoder_.pic_height())));
171 return;
172
173 case VaapiH264Decoder::kNeedMoreStreamData:
174 ReturnCurrInputBuffer();
175 break;
176
177 case VaapiH264Decoder::kDecodeError:
178 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error in decoding",
179 PLATFORM_FAILURE, );
180
181 default:
182 RETURN_AND_NOTIFY_ON_FAILURE(false,
183 "Unexpected result from decoder: " << res,
184 PLATFORM_FAILURE, );
185 }
186 }
187 }
188
189 bool VaapiVideoDecodeAccelerator::GetInputBuffer() {
190 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
191
192 base::AutoLock auto_lock(lock_);
193
194 if (curr_input_buffer_.get())
195 return true;
196
197 // Will only wait if it is expected that in current state new buffers will
198 // be queued from the client via Decode(). The state can change during wait.
199 while (input_buffers_.empty() &&
200 (state_ == kDecoding || state_ == kInitialized || state_ == kIdle)) {
201 input_ready_.Wait();
202 }
203
204 // We could have got woken up in a different state or never got to sleep
205 // due to current state; check for that.
206 switch (state_) {
207 case kFlushing:
208 // Here we are only interested in finishing up decoding buffers that are
209 // already queued up. Otherwise will stop decoding.
210 if (input_buffers_.empty())
211 return false;
212 // else fallthrough
213 case kDecoding:
214 case kInitialized:
215 case kIdle:
216 DCHECK(!input_buffers_.empty());
217
218 curr_input_buffer_ = input_buffers_.front();
219 input_buffers_.pop();
220
221 DVLOG(4) << "New current bitstream buffer, id: "
222 << curr_input_buffer_->id
223 << " size: " << curr_input_buffer_->size;
224
225 decoder_.SetStream(
226 static_cast<uint8*>(curr_input_buffer_->shm->memory()),
227 curr_input_buffer_->size);
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();
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 DCHECK_EQ(state_, kDestroying);
445 return; // We could've gotten destroyed already.
446 }
447
448 state_ = kIdle;
449
450 if (client_)
451 client_->NotifyFlushDone();
452
453 DVLOG(1) << "Flush finished";
454 }
455
456 void VaapiVideoDecodeAccelerator::ResetTask() {
457 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
458
459 // All the decoding tasks from before the reset request from client are done
460 // by now, as this task was scheduled after them and client is expected not
461 // to call Decode() after Reset() and before NotifyResetDone.
462 decoder_.Reset();
463
464 // Return current input buffer, if present.
465 if (curr_input_buffer_.get())
466 ReturnCurrInputBuffer();
467
468 // And let client know that we are done with reset.
469 message_loop_->PostTask(FROM_HERE, base::Bind(
470 &VaapiVideoDecodeAccelerator::FinishReset, this));
471 }
472
473 void VaapiVideoDecodeAccelerator::Reset() {
474 DCHECK_EQ(message_loop_, MessageLoop::current());
475 DVLOG(1) << "Got reset request";
476
477 // This will make any new decode tasks exit early.
478 base::AutoLock auto_lock(lock_);
479 state_ = kResetting;
480
481 decoder_thread_.message_loop()->PostTask(FROM_HERE,
482 base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, this));
483
484 input_ready_.Signal();
485 output_ready_.Signal();
486 }
487
488 void VaapiVideoDecodeAccelerator::FinishReset() {
489 DCHECK_EQ(message_loop_, MessageLoop::current());
490
491 base::AutoLock auto_lock(lock_);
492 if (state_ != kResetting) {
493 DCHECK_EQ(state_, kDestroying);
494 return; // We could've gotten destroyed already.
495 }
496
497 // Drop all remaining input buffers, if present.
498 while (!input_buffers_.empty())
499 input_buffers_.pop();
500
501 state_ = kIdle;
502
503 if (client_)
504 client_->NotifyResetDone();
505
506 DVLOG(1) << "Reset finished";
507 }
508
509 void VaapiVideoDecodeAccelerator::DestroyTask() {
510 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
511
512 DVLOG(1) << "DestroyTask";
513 base::AutoLock auto_lock(lock_);
514 decoder_.Destroy();
515
516 message_loop_->PostTask(FROM_HERE, base::Bind(
517 &VaapiVideoDecodeAccelerator::FinishDestroy, this));
518 }
519
520 void VaapiVideoDecodeAccelerator::Destroy() {
521 if (message_loop_ != MessageLoop::current()) {
522 message_loop_->PostTask(FROM_HERE, base::Bind(
523 &VaapiVideoDecodeAccelerator::Destroy, this));
524 return;
525 }
526
527 if (state_ == kUninitialized || state_ == kDestroying)
528 return;
529
530 DVLOG(1) << "Destroying VAVDA";
531 base::AutoLock auto_lock(lock_);
532 state_ = kDestroying;
533 decoder_thread_.message_loop()->PostTask(FROM_HERE,
534 base::Bind(&VaapiVideoDecodeAccelerator::DestroyTask, this));
535 client_ = NULL;
536
537 input_ready_.Signal();
538 output_ready_.Signal();
539 }
540
541 void VaapiVideoDecodeAccelerator::FinishDestroy() {
542 base::AutoLock auto_lock(lock_);
543 state_ = kUninitialized;
544 }
545
546 void VaapiVideoDecodeAccelerator::OutputPicCallback(int32 input_id,
547 int32 output_id) {
548 TRACE_EVENT2("Video Decoder", "VAVDA::OutputPicCallback",
549 "Input id", input_id, "Picture id", output_id);
550 DVLOG(4) << "Outputting picture, input id: " << input_id
551 << " output id: " << output_id;
552
553 // Forward the request to the main thread.
554 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
555 message_loop_->PostTask(FROM_HERE,
556 base::Bind(&VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady,
557 this, input_id, output_id));
558 }
559
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698