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

Side by Side Diff: content/renderer/pepper/video_decoder_shim.cc

Issue 311853005: Implement software fallback for PPB_VideoDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase, fix Windows compile, restore test. Created 6 years, 6 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) 2014 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 <GLES2/gl2.h>
6 #include <GLES2/gl2ext.h>
7 #include <GLES2/gl2extchromium.h>
8
9 #include "content/renderer/pepper/video_decoder_shim.h"
10
11 #include "base/bind.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "content/public/renderer/render_thread.h"
14 #include "content/renderer/pepper/pepper_video_decoder_host.h"
15 #include "content/renderer/render_thread_impl.h"
16 #include "gpu/command_buffer/client/gles2_implementation.h"
17 #include "media/base/decoder_buffer.h"
18 #include "media/base/limits.h"
19 #include "media/base/video_decoder.h"
20 #include "media/filters/ffmpeg_video_decoder.h"
21 #include "media/video/picture.h"
22 #include "media/video/video_decode_accelerator.h"
23 #include "ppapi/c/pp_errors.h"
24 #include "third_party/libyuv/include/libyuv.h"
25 #include "webkit/common/gpu/context_provider_web_context.h"
26
27 namespace content {
28
29 struct VideoDecoderShim::PendingDecode {
30 PendingDecode(uint32_t decode_id,
31 const scoped_refptr<media::DecoderBuffer>& buffer);
32 ~PendingDecode();
33
34 const uint32_t decode_id;
35 const scoped_refptr<media::DecoderBuffer> buffer;
36 };
37
38 VideoDecoderShim::PendingDecode::PendingDecode(
39 uint32_t decode_id,
40 const scoped_refptr<media::DecoderBuffer>& buffer)
41 : decode_id(decode_id), buffer(buffer) {
42 }
43
44 VideoDecoderShim::PendingDecode::~PendingDecode() {
45 }
46
47 struct VideoDecoderShim::PendingFrame {
48 explicit PendingFrame(uint32_t decode_id);
49 PendingFrame(uint32_t decode_id, const gfx::Size& size);
50 ~PendingFrame();
51
52 const uint32_t decode_id;
53 const gfx::Size size;
54 std::vector<uint8_t> argb_pixels;
55
56 private:
57 // This could be expensive to copy, so guard against that.
58 DISALLOW_COPY_AND_ASSIGN(PendingFrame);
59 };
60
61 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id)
62 : decode_id(decode_id) {
63 }
64
65 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id,
66 const gfx::Size& size)
67 : decode_id(decode_id),
68 size(size),
69 argb_pixels(size.width() * size.height() * 4) {
70 }
71
72 VideoDecoderShim::PendingFrame::~PendingFrame() {
73 }
74
75 // DecoderImpl runs the underlying VideoDecoder on the media thread, receiving
76 // calls from the VideoDecodeShim on the main thread and sending results back.
77 // This class is constructed on the main thread, but used and destructed on the
78 // media thread.
79 class VideoDecoderShim::DecoderImpl {
80 public:
81 explicit DecoderImpl(const base::WeakPtr<VideoDecoderShim>& proxy);
82 ~DecoderImpl();
83
84 void Initialize(media::VideoDecoderConfig config);
85 void Decode(uint32_t decode_id, scoped_refptr<media::DecoderBuffer> buffer);
86 void Reset();
87 void Stop();
88
89 private:
90 void OnPipelineStatus(media::PipelineStatus status);
91 void DoDecode();
92 void OnDecodeComplete(uint32_t decode_id,
93 media::VideoDecoder::Status status,
94 const scoped_refptr<media::VideoFrame>& frame);
95 void OnResetComplete();
96
97 // WeakPtr is bound to main_message_loop_. Use only in shim callbacks.
98 base::WeakPtr<VideoDecoderShim> shim_;
99 scoped_ptr<media::VideoDecoder> decoder_;
100 scoped_refptr<base::MessageLoopProxy> main_message_loop_;
101 // Queue of decodes waiting for the decoder.
102 typedef std::queue<PendingDecode> PendingDecodeQueue;
103 PendingDecodeQueue pending_decodes_;
104 int max_decodes_at_decoder_;
105 int num_decodes_at_decoder_;
106 };
107
108 VideoDecoderShim::DecoderImpl::DecoderImpl(
109 const base::WeakPtr<VideoDecoderShim>& proxy)
110 : shim_(proxy),
111 main_message_loop_(base::MessageLoopProxy::current()),
112 max_decodes_at_decoder_(0),
113 num_decodes_at_decoder_(0) {
114 }
115
116 VideoDecoderShim::DecoderImpl::~DecoderImpl() {
117 DCHECK(pending_decodes_.empty());
118 }
119
120 void VideoDecoderShim::DecoderImpl::Initialize(
121 media::VideoDecoderConfig config) {
122 DCHECK(!decoder_);
123 decoder_.reset(
124 new media::FFmpegVideoDecoder(base::MessageLoopProxy::current()));
125 max_decodes_at_decoder_ = decoder_->GetMaxDecodeRequests();
126 // We can use base::Unretained() safely in decoder callbacks because we call
127 // VideoDecoder::Stop() before deletion. Stop() guarantees there will be no
128 // outstanding callbacks after it returns.
129 decoder_->Initialize(
130 config,
131 true /* low_delay */,
Ami GONE FROM CHROMIUM 2014/06/07 18:26:40 Igor: the commentary on your new API is that it di
igorc 2014/06/11 20:13:18 It could be poor commenting and/or incomplete unde
DaleCurtis 2014/06/11 21:08:08 The comment is incorrect / opposite and should pro
igorc 2014/06/11 22:30:08 Just spoke to Dale, and we agreed to delete "Disab
132 base::Bind(&VideoDecoderShim::DecoderImpl::OnPipelineStatus,
133 base::Unretained(this)));
134 }
135
136 void VideoDecoderShim::DecoderImpl::OnPipelineStatus(
137 media::PipelineStatus status) {
138 int32_t result;
139 switch (status) {
140 case media::PIPELINE_OK:
141 result = PP_OK;
142 break;
143 case media::DECODER_ERROR_NOT_SUPPORTED:
144 result = PP_ERROR_NOTSUPPORTED;
145 break;
146 default:
147 result = PP_ERROR_FAILED;
148 break;
149 }
150
151 // Calculate how many textures the shim should create.
152 uint32_t texture_pool_size =
153 max_decodes_at_decoder_ + media::limits::kMaxVideoFrames;
154 main_message_loop_->PostTask(
155 FROM_HERE,
156 base::Bind(&VideoDecoderShim::OnInitializeComplete,
157 shim_,
158 result,
159 texture_pool_size));
160 }
161
162 void VideoDecoderShim::DecoderImpl::Decode(
163 uint32_t decode_id,
164 scoped_refptr<media::DecoderBuffer> buffer) {
165 DCHECK(decoder_);
166 pending_decodes_.push(PendingDecode(decode_id, buffer));
167 DoDecode();
Ami GONE FROM CHROMIUM 2014/06/07 18:26:40 lol I start out thinking it was a bug but convince
bbudge 2014/06/07 21:28:33 I am still searching for some way to test this mor
168 }
169
170 void VideoDecoderShim::DecoderImpl::Reset() {
171 DCHECK(decoder_);
172 // Abort all pending decodes.
173 while (!pending_decodes_.empty()) {
174 const PendingDecode& decode = pending_decodes_.front();
175 scoped_ptr<PendingFrame> pending_frame(new PendingFrame(decode.decode_id));
176 main_message_loop_->PostTask(FROM_HERE,
177 base::Bind(&VideoDecoderShim::OnDecodeComplete,
178 shim_,
179 media::VideoDecoder::kAborted,
180 base::Passed(&pending_frame)));
181 pending_decodes_.pop();
182 }
183 decoder_->Reset(base::Bind(&VideoDecoderShim::DecoderImpl::OnResetComplete,
184 base::Unretained(this)));
185 }
186
187 void VideoDecoderShim::DecoderImpl::Stop() {
188 DCHECK(decoder_);
189 // Clear pending decodes now. We don't want OnDecodeComplete to call Decode
190 // again.
191 while (!pending_decodes_.empty())
192 pending_decodes_.pop();
193 decoder_->Stop();
194 // This instance is deleted once we exit this scope.
195 }
196
197 void VideoDecoderShim::DecoderImpl::DoDecode() {
198 while (!pending_decodes_.empty() &&
199 num_decodes_at_decoder_ < max_decodes_at_decoder_) {
200 num_decodes_at_decoder_++;
201 const PendingDecode& decode = pending_decodes_.front();
202 decoder_->Decode(
203 decode.buffer,
204 base::Bind(&VideoDecoderShim::DecoderImpl::OnDecodeComplete,
205 base::Unretained(this),
206 decode.decode_id));
207 pending_decodes_.pop();
208 }
209 }
210
211 void VideoDecoderShim::DecoderImpl::OnDecodeComplete(
212 uint32_t decode_id,
213 media::VideoDecoder::Status status,
214 const scoped_refptr<media::VideoFrame>& frame) {
215 num_decodes_at_decoder_--;
216 scoped_ptr<PendingFrame> pending_frame;
217 if (status == media::VideoDecoder::kOk && !frame->end_of_stream()) {
218 pending_frame.reset(new PendingFrame(decode_id, frame->coded_size()));
219 // Convert the VideoFrame pixels to ARGB.
220 libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
221 frame->stride(media::VideoFrame::kYPlane),
222 frame->data(media::VideoFrame::kUPlane),
223 frame->stride(media::VideoFrame::kUPlane),
224 frame->data(media::VideoFrame::kVPlane),
225 frame->stride(media::VideoFrame::kVPlane),
226 &pending_frame->argb_pixels.front(),
227 frame->coded_size().width() * 4,
228 frame->coded_size().width(),
229 frame->coded_size().height());
230 } else {
231 pending_frame.reset(new PendingFrame(decode_id));
232 }
233
234 int32_t result;
235 switch (status) {
236 case media::VideoDecoder::kOk:
237 // We allow partial frames, so this is OK.
238 case media::VideoDecoder::kNotEnoughData:
239 result = PP_OK;
240 break;
241 case media::VideoDecoder::kAborted:
242 result = PP_ERROR_ABORTED;
243 break;
244 case media::VideoDecoder::kDecodeError:
245 result = PP_ERROR_RESOURCE_FAILED;
246 break;
247 default:
248 NOTREACHED();
249 result = PP_ERROR_FAILED;
250 break;
251 }
252
253 main_message_loop_->PostTask(FROM_HERE,
254 base::Bind(&VideoDecoderShim::OnDecodeComplete,
255 shim_,
256 result,
257 base::Passed(&pending_frame)));
258
259 DoDecode();
260 }
261
262 void VideoDecoderShim::DecoderImpl::OnResetComplete() {
263 main_message_loop_->PostTask(
264 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_));
265 }
266
267 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host)
268 : state_(UNINITIALIZED),
269 host_(host),
270 media_message_loop_(
271 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
272 context_provider_(
273 RenderThreadImpl::current()->SharedMainThreadContextProvider()),
274 num_pending_decodes_(0),
275 texture_pool_size_(0),
276 weak_ptr_factory_(this) {
277 DCHECK(host_);
278 DCHECK(media_message_loop_);
279 DCHECK(context_provider_);
280 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr()));
281 }
282
283 VideoDecoderShim::~VideoDecoderShim() {
284 DCHECK(RenderThreadImpl::current());
285 // Delete any remaining textures.
286 TextureIdMap::iterator it = texture_id_map_.begin();
287 for (; it != texture_id_map_.end(); ++it)
288 DeleteTexture(it->second);
289 texture_id_map_.clear();
290
291 FlushCommandBuffer();
292
293 host_ = NULL;
294 // No methods can post tasks to the delegate now.
295 weak_ptr_factory_.InvalidateWeakPtrs();
296 // No more callbacks from the delegate will be received now.
297
298 // The callback now holds the only reference to the DecoderImpl, which will be
299 // deleted when Stop completes.
300 media_message_loop_->PostTask(
301 FROM_HERE,
302 base::Bind(&VideoDecoderShim::DecoderImpl::Stop,
303 base::Owned(decoder_impl_.release())));
304
305 }
306
307 bool VideoDecoderShim::Initialize(
308 media::VideoCodecProfile profile,
309 media::VideoDecodeAccelerator::Client* client) {
310 // Ignore the |client| parameter, which is the same as |host_|.
Ami GONE FROM CHROMIUM 2014/06/07 18:26:39 DCHECK this instead of the comment?
bbudge 2014/06/07 21:28:33 Done.
311 DCHECK(RenderThreadImpl::current());
312 DCHECK_EQ(state_, UNINITIALIZED);
313 media::VideoCodec codec = media::kUnknownVideoCodec;
314 if (profile <= media::H264PROFILE_MAX)
315 codec = media::kCodecH264;
316 else if (profile <= media::VP8PROFILE_MAX)
317 codec = media::kCodecVP8;
318 DCHECK_NE(codec, media::kUnknownVideoCodec);
319
320 media::VideoDecoderConfig config(
321 codec,
322 profile,
323 media::VideoFrame::YV12,
324 gfx::Size(32, 24), // Small sizes that won't fail.
325 gfx::Rect(32, 24),
326 gfx::Size(32, 24),
327 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed.
328 0 /* extra_data_size */,
329 false /* decryption */);
330
331 media_message_loop_->PostTask(
332 FROM_HERE,
333 base::Bind(&VideoDecoderShim::DecoderImpl::Initialize,
334 base::Unretained(decoder_impl_.get()),
335 config));
336 // Return success, even though we are asynchronous, to mimic
337 // media::VideoDecodeAccelerator.
338 return true;
339 }
340
341 void VideoDecoderShim::Decode(const media::BitstreamBuffer& bitstream_buffer) {
342 DCHECK(RenderThreadImpl::current());
343 DCHECK_EQ(state_, DECODING);
344
345 num_pending_decodes_++;
346
347 // We need the address of the shared memory, so we can copy into our buffer.
348 const uint8_t* buffer = host_->ShmHandleToAddress(bitstream_buffer.handle());
349 DCHECK(buffer);
350
351 media_message_loop_->PostTask(
352 FROM_HERE,
353 base::Bind(
354 &VideoDecoderShim::DecoderImpl::Decode,
355 base::Unretained(decoder_impl_.get()),
356 bitstream_buffer.id(),
357 media::DecoderBuffer::CopyFrom(buffer, bitstream_buffer.size())));
358 }
359
360 void VideoDecoderShim::AssignPictureBuffers(
361 const std::vector<media::PictureBuffer>& buffers) {
362 DCHECK(RenderThreadImpl::current());
363 DCHECK_EQ(state_, DECODING);
364 if (buffers.empty()) {
365 NOTREACHED();
366 return;
367 }
368 DCHECK_EQ(buffers.size(), pending_texture_mailboxes_.size());
369 GLuint num_textures = base::checked_cast<GLuint>(buffers.size());
370 std::vector<uint32_t> local_texture_ids(num_textures);
371 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
372 gles2->GenTextures(num_textures, &local_texture_ids.front());
373 for (uint32_t i = 0; i < num_textures; i++) {
374 gles2->ActiveTexture(GL_TEXTURE0);
375 gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
376 gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
377 pending_texture_mailboxes_[i].name);
378 // Map the plugin texture id to the local texture id.
379 uint32_t plugin_texture_id = buffers[i].texture_id();
380 texture_id_map_[plugin_texture_id] = local_texture_ids[i];
381 available_textures_.push_back(plugin_texture_id);
382 }
383 pending_texture_mailboxes_.clear();
384 SendPictures();
385 }
386
387 void VideoDecoderShim::ReusePictureBuffer(int32 picture_buffer_id) {
388 DCHECK(RenderThreadImpl::current());
389 uint32_t texture_id = static_cast<uint32_t>(picture_buffer_id);
390 if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
391 DismissTexture(texture_id);
392 } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
393 available_textures_.push_back(texture_id);
394 SendPictures();
395 } else {
396 NOTREACHED();
397 }
398 }
399
400 void VideoDecoderShim::Flush() {
401 DCHECK(RenderThreadImpl::current());
402 DCHECK_EQ(state_, DECODING);
403 state_ = FLUSHING;
404 }
405
406 void VideoDecoderShim::Reset() {
407 DCHECK(RenderThreadImpl::current());
408 DCHECK_EQ(state_, DECODING);
409 state_ = RESETTING;
410 media_message_loop_->PostTask(
411 FROM_HERE,
412 base::Bind(&VideoDecoderShim::DecoderImpl::Reset,
413 base::Unretained(decoder_impl_.get())));
414 }
415
416 void VideoDecoderShim::Destroy() {
417 // This will be called, but our destructor can do the actual work.
418 }
419
420 void VideoDecoderShim::OnInitializeComplete(int32_t result,
421 uint32_t texture_pool_size) {
422 DCHECK(RenderThreadImpl::current());
423 DCHECK(host_);
424
425 if (result == PP_OK) {
426 state_ = DECODING;
427 texture_pool_size_ = texture_pool_size;
428 }
429
430 host_->OnInitializeComplete(result);
431 }
432
433 void VideoDecoderShim::OnDecodeComplete(int32_t result,
434 scoped_ptr<PendingFrame> frame) {
435 DCHECK(RenderThreadImpl::current());
436 DCHECK(host_);
437
438 num_pending_decodes_--;
439
440 if (result == PP_OK && !frame->argb_pixels.empty()) {
441 if (texture_size_ != frame->size) {
442 // If the size has changed, all current textures must be dismissed. Add
443 // all textures to |textures_to_dismiss_| and dismiss any that aren't in
444 // use by the plugin. We dismiss the rest as they are recycled.
445 for (TextureIdMap::const_iterator it = texture_id_map_.begin();
446 it != texture_id_map_.end();
447 ++it) {
448 textures_to_dismiss_.insert(it->second);
449 }
450 for (std::vector<uint32_t>::const_iterator it =
451 available_textures_.begin();
452 it != available_textures_.end();
453 ++it) {
454 DismissTexture(*it);
455 }
456 available_textures_.clear();
457 FlushCommandBuffer();
458
459 DCHECK(pending_texture_mailboxes_.empty());
460 for (uint32_t i = 0; i < texture_pool_size_; i++)
461 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
462
463 host_->RequestTextures(texture_pool_size_,
464 frame->size,
465 GL_TEXTURE_2D,
466 pending_texture_mailboxes_);
467 texture_size_ = frame->size;
468 }
469
470 pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
471 SendPictures();
472 } else {
473 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
474 }
475
476 // Report any VDA type errors.
477 if (result == PP_ERROR_RESOURCE_FAILED)
478 host_->NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
479 }
480
481 void VideoDecoderShim::SendPictures() {
482 DCHECK(RenderThreadImpl::current());
483 DCHECK(host_);
484
485 while (!pending_frames_.empty() && !available_textures_.empty()) {
486 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
487
488 uint32_t texture_id = available_textures_.back();
489 available_textures_.pop_back();
490
491 uint32_t local_texture_id = texture_id_map_[texture_id];
492 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
493 gles2->ActiveTexture(GL_TEXTURE0);
494 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
495 gles2->TexImage2D(GL_TEXTURE_2D,
496 0,
497 GL_RGBA,
498 texture_size_.width(),
499 texture_size_.height(),
500 0,
501 GL_RGBA,
502 GL_UNSIGNED_BYTE,
503 &frame->argb_pixels.front());
504
505 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
506 host_->PictureReady(media::Picture(texture_id, frame->decode_id));
507 pending_frames_.pop();
508 }
509
510 FlushCommandBuffer();
511
512 if (state_ == FLUSHING && !num_pending_decodes_ && pending_frames_.empty()) {
513 state_ = DECODING;
514 host_->NotifyFlushDone();
515 }
516 }
517
518 void VideoDecoderShim::OnResetComplete() {
519 DCHECK(RenderThreadImpl::current());
520 DCHECK(host_);
521
522 while (!pending_frames_.empty()) {
523 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
524 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
525 pending_frames_.pop();
526 }
527
528 state_ = DECODING;
529 host_->NotifyResetDone();
530 }
531
532 void VideoDecoderShim::DismissTexture(uint32_t texture_id) {
533 DCHECK(host_);
534 textures_to_dismiss_.erase(texture_id);
535 DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
536 DeleteTexture(texture_id_map_[texture_id]);
537 texture_id_map_.erase(texture_id);
538 host_->DismissPictureBuffer(texture_id);
539 }
540
541 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) {
542 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
543 gles2->DeleteTextures(1, &texture_id);
544 }
545
546 void VideoDecoderShim::FlushCommandBuffer() {
547 context_provider_->ContextGL()->Flush();
548 }
549
550 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698