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

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

Powered by Google App Engine
This is Rietveld 408576698