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

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, 9 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 DLOG(ERROR) << log; \
25 StopOnError(error_code); \
26 return ret; \
27 } \
28 } while (0)
29
30 void VaapiVideoDecodeAccelerator::StopOnError(
31 media::VideoDecodeAccelerator::Error error) {
32 DCHECK_EQ(message_loop_, MessageLoop::current());
33
34 DLOG(ERROR) << "Stopping on error " << error;
35
36 decoder_thread_.Stop();
37
38 if (client_)
39 client_->NotifyError(error);
40 client_ = NULL;
41 }
42
43 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
44 media::VideoDecodeAccelerator::Client* client)
45 : input_ready_(true, false), // manually Reset() and initially not signalled
46 message_loop_(MessageLoop::current()),
47 client_(client),
48 pictures_requested_(false),
49 after_reset_(false),
50 resetting_(false),
51 decoder_thread_("VaapiDecoderThread") {
52 }
53
54 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
55 DCHECK_EQ(message_loop_, MessageLoop::current());
56 }
57
58 bool VaapiVideoDecodeAccelerator::Initialize(Profile profile) {
59 DCHECK_EQ(message_loop_, MessageLoop::current());
60 bool res = true;
61
62 DVLOG(2) << "Initializing VAVDA, profile: " << profile;
63
64 res = CommandLine::ForCurrentProcess()->HasSwitch(
65 switches::kEnableVaapiDecodeAcceleration);
66 if (!res) {
67 DVLOG(1) << "Vaapi HW acceleration disabled";
68 goto done;
69 }
70
71 res = decoder_.Initialize(profile, x_display_, glx_context_, message_loop_,
72 &VaapiVideoDecodeAccelerator::OutputPicCallback, this);
73 if (!res) {
74 DLOG(ERROR) << "Failed initializing decoder";
75 goto done;
76 }
77
78 res = decoder_thread_.Start();
79 if (!res)
80 DLOG(ERROR) << "Failed starting decoding thread";
81
82 done:
83 MessageLoop::current()->PostTask(FROM_HERE,
84 base::Bind(&VaapiVideoDecodeAccelerator::NotifyInitializeDone, this));
85 return res;
86 }
87
88 void VaapiVideoDecodeAccelerator::NotifyInitializeDone() {
89 DCHECK_EQ(message_loop_, MessageLoop::current());
90 if (client_)
91 client_->NotifyInitializeDone();
92 }
93
94 void VaapiVideoDecodeAccelerator::SetGlxState(Display* x_display,
95 GLXContext glx_context) {
96 DCHECK_EQ(message_loop_, MessageLoop::current());
97 x_display_ = x_display;
98 glx_context_ = glx_context;
99 }
100
101 void VaapiVideoDecodeAccelerator::NotifyInputBufferRead(int input_buffer_id) {
102 DCHECK_EQ(message_loop_, MessageLoop::current());
103
104 DVLOG(4) << "Notifying end of input buffer " << input_buffer_id;
105 if (client_)
106 client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
107 }
108
109 void VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady(int32 input_id,
110 int32 output_id) {
111 DCHECK_EQ(message_loop_, MessageLoop::current());
112
113 // Sync the contents of the texture.
114 if (!decoder_.PutPicToTexture(output_id)) {
115 DLOG(ERROR) << "Failed putting picture to texture";
116 StopOnError(PLATFORM_FAILURE);
117 return;
118 }
119
120 // And notify the client a picture is ready to be displayed.
121 media::Picture picture(output_id, input_id);
122 DVLOG(4) << "Notifying output picture id " << output_id
123 << " for input "<< input_id << " is ready";
124 if (client_)
125 client_->PictureReady(picture);
126 }
127
128 void VaapiVideoDecodeAccelerator::RequestPictureBuffers(int num_pics,
Ami GONE FROM CHROMIUM 2012/03/21 13:16:24 Why not pass the VDA::Client to the decoder, so it
Pawel Osciak 2012/03/21 18:40:35 I was really trying not to add additional complexi
Ami GONE FROM CHROMIUM 2012/03/22 17:01:36 I'm saying I think passing Client to the decoder w
Pawel Osciak 2012/04/05 10:37:20 VAVDA controls logic behind whether it's required
129 int width,
130 int height) {
131 DCHECK_EQ(message_loop_, MessageLoop::current());
132
133 DVLOG(1) << "Requesting " << num_pics << " pictures of size: "
134 << width << "x" << height;
135 if (client_)
136 client_->ProvidePictureBuffers(num_pics, gfx::Size(width, height));
137 }
138
139 void VaapiVideoDecodeAccelerator::TryGetNewInputBuffer() {
140 DCHECK_EQ(message_loop_, MessageLoop::current());
141 DCHECK(!input_ready_.IsSignaled());
142
143 // If current buffer is still set, return it to the client.
144 if (curr_input_buffer_.get()) {
145 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
146 &VaapiVideoDecodeAccelerator::NotifyInputBufferRead, this,
147 curr_input_buffer_->id));
148
149 curr_input_buffer_.reset();
150 }
151
152 // If there is no more input buffers ready, return. We will come back
153 // here the next time client provides us with a new input buffer.
154 if (input_buffers_.empty())
155 return;
156
157 // Otherwise pop a new input buffer and set it up for the decoder thread.
158 curr_input_buffer_.reset(input_buffers_.front());
159 input_buffers_.pop();
160
161 DVLOG(4) << "New current bitstream buffer, id: " << curr_input_buffer_->id
162 << " size: " << (int)curr_input_buffer_->size;
163
164 // Decoder is waiting on |input_ready_|, so we are safe to do this.
165 decoder_.SetStream((uint8*)curr_input_buffer_->shm->memory(),
166 curr_input_buffer_->size);
167
168 // Wake up the decoder thread and let it continue.
169 input_ready_.Signal();
170 }
171
172 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
173 const media::BitstreamBuffer& bitstream_buffer) {
174 DCHECK_EQ(message_loop_, MessageLoop::current());
175
176 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
177 << " size: " << (int)bitstream_buffer.size();
178
179 scoped_ptr<base::SharedMemory> shm(
180 new base::SharedMemory(bitstream_buffer.handle(), true));
181 CHECK(shm.get());
182 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
183 "Failed to map input buffer", UNREADABLE_INPUT,);
184
185 // Set up a new input buffer and queue it for later.
186 InputBuffer* input_buffer = new InputBuffer();
187 CHECK(input_buffer);
188 input_buffer->shm.reset(shm.release());
189 input_buffer->id = bitstream_buffer.id();
190 input_buffer->size = bitstream_buffer.size();
191 input_buffers_.push(input_buffer);
192 }
193
194 void VaapiVideoDecodeAccelerator::WaitForInput() {
195 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
196
197 // Set us up for sleep, request new input buffer from the main thread
198 // and go to sleep waiting for good news.
199 input_ready_.Reset();
200 message_loop_->PostTask(FROM_HERE,
201 base::Bind(&VaapiVideoDecodeAccelerator::TryGetNewInputBuffer, this));
202 input_ready_.Wait();
203 }
204
205 void VaapiVideoDecodeAccelerator::DecodeTask() {
206 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
207 VaapiH264Decoder::DecResult res;
208
209 // Main decode task.
210 DVLOG(4) << "Decode task";
211
212 DCHECK(input_ready_.IsSignaled());
213
214 if (resetting_) {
215 // Will get back here once we are done with reset. This is needed so we
216 // don't sleep indefinitely blocking the decoder from being forced
217 // to reset.
218 DVLOG(4) << "Resetting, leaving DecodeTask";
219 return;
220 }
221
222 // Could happen after reset, when we are still waiting for a new buffer.
223 if (!curr_input_buffer_.get())
224 return;
225
226 if (!pictures_requested_ || after_reset_) {
227 // Try to initialize or resume playback after reset.
228 res = decoder_.DecodeInitial(curr_input_buffer_->id);
229 switch (res) {
230 case VaapiH264Decoder::kReadyToDecode:
231 if (!pictures_requested_) {
232 // Decoder decoded initial stream information and is ready
233 // to receive output pictures. Request them from the client.
234 message_loop_->PostTask(FROM_HERE, base::Bind(
235 &VaapiVideoDecodeAccelerator::RequestPictureBuffers, this,
236 decoder_.GetRequiredNumOfPictures(),
237 decoder_.pic_width(), decoder_.pic_height()));
238 pictures_requested_ = true;
239 } else {
240 // We are after reset and successfully found a point from which
241 // we can resume normal decoding, so post a task for that.
242 DCHECK(after_reset_);
243 after_reset_ = false;
244 decoder_thread_.message_loop()->PostTask(FROM_HERE,
245 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
246 }
247 break;
248
249 case VaapiH264Decoder::kNeedMoreStreamData:
250 DLOG(INFO) << "Waiting for more stream data to (re)initialize";
251 // May sleep.
252 WaitForInput();
253 break;
254
255 case VaapiH264Decoder::kDecError:
256 DLOG(ERROR) << "Error decoding stream";
257 message_loop_->PostTask(FROM_HERE,
258 base::Bind(&VaapiVideoDecodeAccelerator::StopOnError, this,
259 PLATFORM_FAILURE));
260 break;
261
262 default:
263 NOTREACHED() << "Unexpected result from the decoder";
264 }
265 } else {
266 // Try to decode what stream data is in the decoder until we get
267 // a frame or run out of input stream.
268 res = decoder_.DecodeOneFrame(curr_input_buffer_->id);
269 switch (res) {
270 case VaapiH264Decoder::kNeedMoreStreamData:
271 // May sleep.
272 WaitForInput();
273 // fallthrough
274 case VaapiH264Decoder::kDecodedFrame:
275 // (still) have more stream, try to decode more
276 decoder_thread_.message_loop()->PostTask(FROM_HERE,
277 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
278 break;
279
280 case VaapiH264Decoder::kNoOutputAvailable:
281 // The decoder does not have any output buffers available, so return.
282 // We will come back once the client gives us back a picture buffer
283 // after displaying it.
284 break;
285
286 case VaapiH264Decoder::kDecError:
287 DLOG(ERROR) << "Error decoding stream";
288 message_loop_->PostTask(FROM_HERE,
289 base::Bind(&VaapiVideoDecodeAccelerator::StopOnError, this,
290 PLATFORM_FAILURE));
291 break;
292
293 default:
294 NOTREACHED() << "Unexpected result from the decoder";
295 }
296 }
297 }
298
299
300 void VaapiVideoDecodeAccelerator::Decode(
301 const media::BitstreamBuffer& bitstream_buffer) {
302 DCHECK_EQ(message_loop_, MessageLoop::current());
303
304 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
305 bitstream_buffer.id());
306
307 // We got a new input buffer from the client, map it and queue for later use.
308 MapAndQueueNewInputBuffer(bitstream_buffer);
309
310 // Set it up already if there is no current one.
311 if (!curr_input_buffer_.get())
312 TryGetNewInputBuffer();
313
314 decoder_thread_.message_loop()->PostTask(FROM_HERE,
315 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
316 }
317
318 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
319 const std::vector<media::PictureBuffer>& buffers) {
320 DCHECK_EQ(message_loop_, MessageLoop::current());
321
322 // Here we call sensitive decoder functions out of its thread, but
323 // the decoder thread is not running yet, so we can safely do this.
324 for (size_t i = 0; i < buffers.size(); ++i) {
325 DVLOG(2) << "Assigning picture id " << buffers[i].id()
326 << " to texture id " << buffers[i].texture_id();
327 decoder_.AssignPictureBuffer(buffers[i].id(), buffers[i].texture_id());
328 }
329
330 decoder_thread_.message_loop()->PostTask(FROM_HERE,
331 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
332 }
333
334 void VaapiVideoDecodeAccelerator::ReusePictureTask(int32 picture_buffer_id) {
335 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
336
337 decoder_.ReusePictureBuffer(picture_buffer_id);
338
339 decoder_thread_.message_loop()->PostTask(FROM_HERE,
340 base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
341 }
342
343 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
344 DCHECK_EQ(message_loop_, MessageLoop::current());
345 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer",
346 "Picture id", picture_buffer_id);
347
348 decoder_thread_.message_loop()->PostTask(FROM_HERE,
349 base::Bind(&VaapiVideoDecodeAccelerator::ReusePictureTask, this,
350 picture_buffer_id));
351 }
352
353 void VaapiVideoDecodeAccelerator::FlushTask() {
354 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
355
356 decoder_.Flush();
357 decoder_.Reset();
358
359 message_loop_->PostTask(FROM_HERE,
360 base::Bind(&VaapiVideoDecodeAccelerator::NotifyFlushDone, this));
361
362 WaitForInput();
363 }
364
365 void VaapiVideoDecodeAccelerator::Flush() {
366 DCHECK_EQ(message_loop_, MessageLoop::current());
367
368 DVLOG(1) << "Got flush request";
369 decoder_thread_.message_loop()->PostTask(FROM_HERE,
370 base::Bind(&VaapiVideoDecodeAccelerator::FlushTask, this));
371 }
372
373 void VaapiVideoDecodeAccelerator::NotifyFlushDone() {
374 DCHECK_EQ(message_loop_, MessageLoop::current());
375 if (client_)
376 client_->NotifyFlushDone();
377 DVLOG(1) << "Flush done";
378 }
379
380 void VaapiVideoDecodeAccelerator::ResetTask() {
381 DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
382
383 // All the decoding tasks should be done by now, since the client
384 // does not give more input and we have scheduled ourselves after
385 // we got a reset request.
386 decoder_.Reset();
387
388 // Make sure next time decode tasks are run they will reinitialize.
389 after_reset_ = true;
390
391 // And let client know that we are done with reset.
392 message_loop_->PostTask(FROM_HERE, base::Bind(
393 &VaapiVideoDecodeAccelerator::NotifyResetDone, this));
394
395 //WaitForInput();
396 }
397
398 void VaapiVideoDecodeAccelerator::Reset() {
399 DCHECK_EQ(message_loop_, MessageLoop::current());
400
401 DVLOG(1) << "Got reset request";
402 decoder_thread_.message_loop()->PostTask(FROM_HERE,
403 base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, this));
404 resetting_ = true;
405 // Release waiting decoder thread, if any. Any additional already posted
406 // decoding threads will exit immediately due to resetting_ set to true.
407 input_ready_.Signal();
408 }
409
410 void VaapiVideoDecodeAccelerator::NotifyResetDone() {
411 DCHECK_EQ(message_loop_, MessageLoop::current());
412
413 if (client_)
414 client_->NotifyResetDone();
415 resetting_ = false;
416 DVLOG(1) << "Reset done";
417 }
418
419 void VaapiVideoDecodeAccelerator::Destroy() {
420 DCHECK_EQ(message_loop_, MessageLoop::current());
421
422 decoder_thread_.Stop();
423 // No decoder tasks running anymore, so safe to destroy.
424 decoder_.Destroy();
425 client_ = NULL;
426 }
427
428 //static
429 void VaapiVideoDecodeAccelerator::OutputPicCallback(void* arg,
430 int32 input_id,
431 int32 output_id) {
432 TRACE_EVENT2("Video Decoder", "VAVDA::OutputPicCallback",
433 "Input id", input_id, "Picture id", output_id);
434 DVLOG(4) << "Outputting picture, input id: " << input_id
435 << " output id: " << output_id;
436
437 // Forward the request to the main thread.
438 VaapiVideoDecodeAccelerator* vavda =
439 static_cast<VaapiVideoDecodeAccelerator*>(arg);
440 DCHECK_EQ(vavda->decoder_thread_.message_loop(), MessageLoop::current());
441 vavda->message_loop_->PostTask(FROM_HERE,
442 base::Bind(&VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady,
Ami GONE FROM CHROMIUM 2012/03/21 13:16:24 Wait, wat?? decoder tells VAVDA a pic is ready but
Pawel Osciak 2012/03/21 18:40:35 Decoder can't sync, because it has to be done on G
Ami GONE FROM CHROMIUM 2012/03/22 17:01:36 Then why not pass the decoder the GLX thread and h
Ami GONE FROM CHROMIUM 2012/03/22 17:01:36 Then why not pass the decoder the GLX thread and h
Pawel Osciak 2012/04/05 10:37:20 I'm not passing message_loop or anything related t
443 vavda, input_id, output_id));
444 }
445
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698