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

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

Issue 337683002: Revert of Implement software fallback for PPB_VideoDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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
« no previous file with comments | « content/renderer/pepper/video_decoder_shim.h ('k') | content/test/ppapi/ppapi_browsertest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "content/renderer/pepper/video_decoder_shim.h"
6
7 #include <GLES2/gl2.h>
8 #include <GLES2/gl2ext.h>
9 #include <GLES2/gl2extchromium.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, media::VideoDecoder::Status status);
93 void OnOutputComplete(const scoped_refptr<media::VideoFrame>& frame);
94 void OnResetComplete();
95
96 // WeakPtr is bound to main_message_loop_. Use only in shim callbacks.
97 base::WeakPtr<VideoDecoderShim> shim_;
98 scoped_ptr<media::VideoDecoder> decoder_;
99 scoped_refptr<base::MessageLoopProxy> main_message_loop_;
100 // Queue of decodes waiting for the decoder.
101 typedef std::queue<PendingDecode> PendingDecodeQueue;
102 PendingDecodeQueue pending_decodes_;
103 int max_decodes_at_decoder_;
104 int num_decodes_at_decoder_;
105 // VideoDecoder returns pictures without information about the decode buffer
106 // that generated it. Save the decode_id from the last decode that completed,
107 // which is close for most decoders, which only decode one buffer at a time.
108 uint32_t decode_id_;
109 };
110
111 VideoDecoderShim::DecoderImpl::DecoderImpl(
112 const base::WeakPtr<VideoDecoderShim>& proxy)
113 : shim_(proxy),
114 main_message_loop_(base::MessageLoopProxy::current()),
115 max_decodes_at_decoder_(0),
116 num_decodes_at_decoder_(0),
117 decode_id_(0) {
118 }
119
120 VideoDecoderShim::DecoderImpl::~DecoderImpl() {
121 DCHECK(pending_decodes_.empty());
122 }
123
124 void VideoDecoderShim::DecoderImpl::Initialize(
125 media::VideoDecoderConfig config) {
126 DCHECK(!decoder_);
127 scoped_ptr<media::FFmpegVideoDecoder> ffmpeg_video_decoder(
128 new media::FFmpegVideoDecoder(base::MessageLoopProxy::current()));
129 ffmpeg_video_decoder->set_decode_nalus(true);
130 decoder_ = ffmpeg_video_decoder.Pass();
131 max_decodes_at_decoder_ = decoder_->GetMaxDecodeRequests();
132 // We can use base::Unretained() safely in decoder callbacks because we call
133 // VideoDecoder::Stop() before deletion. Stop() guarantees there will be no
134 // outstanding callbacks after it returns.
135 decoder_->Initialize(
136 config,
137 true /* low_delay */,
138 base::Bind(&VideoDecoderShim::DecoderImpl::OnPipelineStatus,
139 base::Unretained(this)),
140 base::Bind(&VideoDecoderShim::DecoderImpl::OnOutputComplete,
141 base::Unretained(this)));
142 }
143
144 void VideoDecoderShim::DecoderImpl::Decode(
145 uint32_t decode_id,
146 scoped_refptr<media::DecoderBuffer> buffer) {
147 DCHECK(decoder_);
148 pending_decodes_.push(PendingDecode(decode_id, buffer));
149 DoDecode();
150 }
151
152 void VideoDecoderShim::DecoderImpl::Reset() {
153 DCHECK(decoder_);
154 // Abort all pending decodes.
155 while (!pending_decodes_.empty()) {
156 const PendingDecode& decode = pending_decodes_.front();
157 scoped_ptr<PendingFrame> pending_frame(new PendingFrame(decode.decode_id));
158 main_message_loop_->PostTask(FROM_HERE,
159 base::Bind(&VideoDecoderShim::OnDecodeComplete,
160 shim_,
161 media::VideoDecoder::kAborted,
162 decode.decode_id));
163 pending_decodes_.pop();
164 }
165 decoder_->Reset(base::Bind(&VideoDecoderShim::DecoderImpl::OnResetComplete,
166 base::Unretained(this)));
167 }
168
169 void VideoDecoderShim::DecoderImpl::Stop() {
170 DCHECK(decoder_);
171 // Clear pending decodes now. We don't want OnDecodeComplete to call DoDecode
172 // again.
173 while (!pending_decodes_.empty())
174 pending_decodes_.pop();
175 decoder_->Stop();
176 // This instance is deleted once we exit this scope.
177 }
178
179 void VideoDecoderShim::DecoderImpl::OnPipelineStatus(
180 media::PipelineStatus status) {
181 int32_t result;
182 switch (status) {
183 case media::PIPELINE_OK:
184 result = PP_OK;
185 break;
186 case media::DECODER_ERROR_NOT_SUPPORTED:
187 result = PP_ERROR_NOTSUPPORTED;
188 break;
189 default:
190 result = PP_ERROR_FAILED;
191 break;
192 }
193
194 // Calculate how many textures the shim should create.
195 uint32_t shim_texture_pool_size =
196 max_decodes_at_decoder_ + media::limits::kMaxVideoFrames;
197 main_message_loop_->PostTask(
198 FROM_HERE,
199 base::Bind(&VideoDecoderShim::OnInitializeComplete,
200 shim_,
201 result,
202 shim_texture_pool_size));
203 }
204
205 void VideoDecoderShim::DecoderImpl::DoDecode() {
206 while (!pending_decodes_.empty() &&
207 num_decodes_at_decoder_ < max_decodes_at_decoder_) {
208 num_decodes_at_decoder_++;
209 const PendingDecode& decode = pending_decodes_.front();
210 decoder_->Decode(
211 decode.buffer,
212 base::Bind(&VideoDecoderShim::DecoderImpl::OnDecodeComplete,
213 base::Unretained(this),
214 decode.decode_id));
215 pending_decodes_.pop();
216 }
217 }
218
219 void VideoDecoderShim::DecoderImpl::OnDecodeComplete(
220 uint32_t decode_id,
221 media::VideoDecoder::Status status) {
222 num_decodes_at_decoder_--;
223 decode_id_ = decode_id;
224
225 int32_t result;
226 switch (status) {
227 case media::VideoDecoder::kOk:
228 case media::VideoDecoder::kAborted:
229 result = PP_OK;
230 break;
231 case media::VideoDecoder::kDecodeError:
232 result = PP_ERROR_RESOURCE_FAILED;
233 break;
234 default:
235 NOTREACHED();
236 result = PP_ERROR_FAILED;
237 break;
238 }
239
240 main_message_loop_->PostTask(
241 FROM_HERE,
242 base::Bind(
243 &VideoDecoderShim::OnDecodeComplete, shim_, result, decode_id));
244
245 DoDecode();
246 }
247
248 void VideoDecoderShim::DecoderImpl::OnOutputComplete(
249 const scoped_refptr<media::VideoFrame>& frame) {
250 scoped_ptr<PendingFrame> pending_frame;
251 if (!frame->end_of_stream()) {
252 pending_frame.reset(new PendingFrame(decode_id_, frame->coded_size()));
253 // Convert the VideoFrame pixels to ARGB.
254 libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
255 frame->stride(media::VideoFrame::kYPlane),
256 frame->data(media::VideoFrame::kUPlane),
257 frame->stride(media::VideoFrame::kUPlane),
258 frame->data(media::VideoFrame::kVPlane),
259 frame->stride(media::VideoFrame::kVPlane),
260 &pending_frame->argb_pixels.front(),
261 frame->coded_size().width() * 4,
262 frame->coded_size().width(),
263 frame->coded_size().height());
264 } else {
265 pending_frame.reset(new PendingFrame(decode_id_));
266 }
267
268 main_message_loop_->PostTask(FROM_HERE,
269 base::Bind(&VideoDecoderShim::OnOutputComplete,
270 shim_,
271 base::Passed(&pending_frame)));
272 }
273
274 void VideoDecoderShim::DecoderImpl::OnResetComplete() {
275 main_message_loop_->PostTask(
276 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_));
277 }
278
279 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host)
280 : state_(UNINITIALIZED),
281 host_(host),
282 media_message_loop_(
283 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
284 context_provider_(
285 RenderThreadImpl::current()->SharedMainThreadContextProvider()),
286 texture_pool_size_(0),
287 num_pending_decodes_(0),
288 weak_ptr_factory_(this) {
289 DCHECK(host_);
290 DCHECK(media_message_loop_);
291 DCHECK(context_provider_);
292 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr()));
293 }
294
295 VideoDecoderShim::~VideoDecoderShim() {
296 DCHECK(RenderThreadImpl::current());
297 // Delete any remaining textures.
298 TextureIdMap::iterator it = texture_id_map_.begin();
299 for (; it != texture_id_map_.end(); ++it)
300 DeleteTexture(it->second);
301 texture_id_map_.clear();
302
303 FlushCommandBuffer();
304
305 weak_ptr_factory_.InvalidateWeakPtrs();
306 // No more callbacks from the delegate will be received now.
307
308 // The callback now holds the only reference to the DecoderImpl, which will be
309 // deleted when Stop completes.
310 media_message_loop_->PostTask(
311 FROM_HERE,
312 base::Bind(&VideoDecoderShim::DecoderImpl::Stop,
313 base::Owned(decoder_impl_.release())));
314 }
315
316 bool VideoDecoderShim::Initialize(
317 media::VideoCodecProfile profile,
318 media::VideoDecodeAccelerator::Client* client) {
319 DCHECK_EQ(client, host_);
320 DCHECK(RenderThreadImpl::current());
321 DCHECK_EQ(state_, UNINITIALIZED);
322 media::VideoCodec codec = media::kUnknownVideoCodec;
323 if (profile <= media::H264PROFILE_MAX)
324 codec = media::kCodecH264;
325 else if (profile <= media::VP8PROFILE_MAX)
326 codec = media::kCodecVP8;
327 DCHECK_NE(codec, media::kUnknownVideoCodec);
328
329 media::VideoDecoderConfig config(
330 codec,
331 profile,
332 media::VideoFrame::YV12,
333 gfx::Size(32, 24), // Small sizes that won't fail.
334 gfx::Rect(32, 24),
335 gfx::Size(32, 24),
336 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed.
337 0 /* extra_data_size */,
338 false /* decryption */);
339
340 media_message_loop_->PostTask(
341 FROM_HERE,
342 base::Bind(&VideoDecoderShim::DecoderImpl::Initialize,
343 base::Unretained(decoder_impl_.get()),
344 config));
345 // Return success, even though we are asynchronous, to mimic
346 // media::VideoDecodeAccelerator.
347 return true;
348 }
349
350 void VideoDecoderShim::Decode(const media::BitstreamBuffer& bitstream_buffer) {
351 DCHECK(RenderThreadImpl::current());
352 DCHECK_EQ(state_, DECODING);
353
354 // We need the address of the shared memory, so we can copy the buffer.
355 const uint8_t* buffer = host_->DecodeIdToAddress(bitstream_buffer.id());
356 DCHECK(buffer);
357
358 media_message_loop_->PostTask(
359 FROM_HERE,
360 base::Bind(
361 &VideoDecoderShim::DecoderImpl::Decode,
362 base::Unretained(decoder_impl_.get()),
363 bitstream_buffer.id(),
364 media::DecoderBuffer::CopyFrom(buffer, bitstream_buffer.size())));
365 num_pending_decodes_++;
366 }
367
368 void VideoDecoderShim::AssignPictureBuffers(
369 const std::vector<media::PictureBuffer>& buffers) {
370 DCHECK(RenderThreadImpl::current());
371 DCHECK_EQ(state_, DECODING);
372 if (buffers.empty()) {
373 NOTREACHED();
374 return;
375 }
376 DCHECK_EQ(buffers.size(), pending_texture_mailboxes_.size());
377 GLuint num_textures = base::checked_cast<GLuint>(buffers.size());
378 std::vector<uint32_t> local_texture_ids(num_textures);
379 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
380 gles2->GenTextures(num_textures, &local_texture_ids.front());
381 for (uint32_t i = 0; i < num_textures; i++) {
382 gles2->ActiveTexture(GL_TEXTURE0);
383 gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
384 gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
385 pending_texture_mailboxes_[i].name);
386 // Map the plugin texture id to the local texture id.
387 uint32_t plugin_texture_id = buffers[i].texture_id();
388 texture_id_map_[plugin_texture_id] = local_texture_ids[i];
389 available_textures_.push_back(plugin_texture_id);
390 }
391 pending_texture_mailboxes_.clear();
392 SendPictures();
393 }
394
395 void VideoDecoderShim::ReusePictureBuffer(int32 picture_buffer_id) {
396 DCHECK(RenderThreadImpl::current());
397 uint32_t texture_id = static_cast<uint32_t>(picture_buffer_id);
398 if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
399 DismissTexture(texture_id);
400 } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
401 available_textures_.push_back(texture_id);
402 SendPictures();
403 } else {
404 NOTREACHED();
405 }
406 }
407
408 void VideoDecoderShim::Flush() {
409 DCHECK(RenderThreadImpl::current());
410 DCHECK_EQ(state_, DECODING);
411 state_ = FLUSHING;
412 }
413
414 void VideoDecoderShim::Reset() {
415 DCHECK(RenderThreadImpl::current());
416 DCHECK_EQ(state_, DECODING);
417 state_ = RESETTING;
418 media_message_loop_->PostTask(
419 FROM_HERE,
420 base::Bind(&VideoDecoderShim::DecoderImpl::Reset,
421 base::Unretained(decoder_impl_.get())));
422 }
423
424 void VideoDecoderShim::Destroy() {
425 // This will be called, but our destructor does the actual work.
426 }
427
428 void VideoDecoderShim::OnInitializeComplete(int32_t result,
429 uint32_t texture_pool_size) {
430 DCHECK(RenderThreadImpl::current());
431 DCHECK(host_);
432
433 if (result == PP_OK) {
434 state_ = DECODING;
435 texture_pool_size_ = texture_pool_size;
436 }
437
438 host_->OnInitializeComplete(result);
439 }
440
441 void VideoDecoderShim::OnDecodeComplete(int32_t result, uint32_t decode_id) {
442 DCHECK(RenderThreadImpl::current());
443 DCHECK(host_);
444
445 num_pending_decodes_--;
446 completed_decodes_.push(decode_id);
447
448 if (result == PP_OK) {
449 // If frames are being queued because we're out of textures, don't notify
450 // the host that decode has completed. This exerts "back pressure" to keep
451 // the host from sending buffers that will cause pending_frames_ to grow.
452 if (pending_frames_.empty())
453 NotifyCompletedDecodes();
454 } else if (result == PP_ERROR_RESOURCE_FAILED) {
455 host_->NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
456 }
457 }
458
459 void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) {
460 DCHECK(RenderThreadImpl::current());
461 DCHECK(host_);
462
463 if (!frame->argb_pixels.empty()) {
464 if (texture_size_ != frame->size) {
465 // If the size has changed, all current textures must be dismissed. Add
466 // all textures to |textures_to_dismiss_| and dismiss any that aren't in
467 // use by the plugin. We will dismiss the rest as they are recycled.
468 for (TextureIdMap::const_iterator it = texture_id_map_.begin();
469 it != texture_id_map_.end();
470 ++it) {
471 textures_to_dismiss_.insert(it->second);
472 }
473 for (std::vector<uint32_t>::const_iterator it =
474 available_textures_.begin();
475 it != available_textures_.end();
476 ++it) {
477 DismissTexture(*it);
478 }
479 available_textures_.clear();
480 FlushCommandBuffer();
481
482 DCHECK(pending_texture_mailboxes_.empty());
483 for (uint32_t i = 0; i < texture_pool_size_; i++)
484 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
485
486 host_->RequestTextures(texture_pool_size_,
487 frame->size,
488 GL_TEXTURE_2D,
489 pending_texture_mailboxes_);
490 texture_size_ = frame->size;
491 }
492
493 pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
494 SendPictures();
495 }
496 }
497
498 void VideoDecoderShim::SendPictures() {
499 DCHECK(RenderThreadImpl::current());
500 DCHECK(host_);
501 while (!pending_frames_.empty() && !available_textures_.empty()) {
502 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
503
504 uint32_t texture_id = available_textures_.back();
505 available_textures_.pop_back();
506
507 uint32_t local_texture_id = texture_id_map_[texture_id];
508 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
509 gles2->ActiveTexture(GL_TEXTURE0);
510 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
511 gles2->TexImage2D(GL_TEXTURE_2D,
512 0,
513 GL_RGBA,
514 texture_size_.width(),
515 texture_size_.height(),
516 0,
517 GL_RGBA,
518 GL_UNSIGNED_BYTE,
519 &frame->argb_pixels.front());
520
521 host_->PictureReady(media::Picture(texture_id, frame->decode_id));
522 pending_frames_.pop();
523 }
524
525 FlushCommandBuffer();
526
527 if (pending_frames_.empty()) {
528 // If frames aren't backing up, notify the host of any completed decodes so
529 // it can send more buffers.
530 NotifyCompletedDecodes();
531
532 if (state_ == FLUSHING && !num_pending_decodes_) {
533 state_ = DECODING;
534 host_->NotifyFlushDone();
535 }
536 }
537 }
538
539 void VideoDecoderShim::OnResetComplete() {
540 DCHECK(RenderThreadImpl::current());
541 DCHECK(host_);
542
543 while (!pending_frames_.empty())
544 pending_frames_.pop();
545 NotifyCompletedDecodes();
546
547 state_ = DECODING;
548 host_->NotifyResetDone();
549 }
550
551 void VideoDecoderShim::NotifyCompletedDecodes() {
552 while (!completed_decodes_.empty()) {
553 host_->NotifyEndOfBitstreamBuffer(completed_decodes_.front());
554 completed_decodes_.pop();
555 }
556 }
557
558 void VideoDecoderShim::DismissTexture(uint32_t texture_id) {
559 DCHECK(host_);
560 textures_to_dismiss_.erase(texture_id);
561 DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
562 DeleteTexture(texture_id_map_[texture_id]);
563 texture_id_map_.erase(texture_id);
564 host_->DismissPictureBuffer(texture_id);
565 }
566
567 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) {
568 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
569 gles2->DeleteTextures(1, &texture_id);
570 }
571
572 void VideoDecoderShim::FlushCommandBuffer() {
573 context_provider_->ContextGL()->Flush();
574 }
575
576 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/pepper/video_decoder_shim.h ('k') | content/test/ppapi/ppapi_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698