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

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: Initialize GL to fix Windows tests. 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 <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 scoped_ptr<media::FFmpegVideoDecoder> ffmpeg_video_decoder(
124 new media::FFmpegVideoDecoder(base::MessageLoopProxy::current()));
125 ffmpeg_video_decoder->set_decode_nalus(true);
Ami GONE FROM CHROMIUM 2014/06/08 17:30:38 Still concerned that this conflicts with the true
bbudge 2014/06/09 23:33:01 I'll work this out with Igor.
126 decoder_ = ffmpeg_video_decoder.Pass();
127 max_decodes_at_decoder_ = decoder_->GetMaxDecodeRequests();
128 // We can use base::Unretained() safely in decoder callbacks because we call
129 // VideoDecoder::Stop() before deletion. Stop() guarantees there will be no
130 // outstanding callbacks after it returns.
131 decoder_->Initialize(
132 config,
133 true /* low_delay */,
134 base::Bind(&VideoDecoderShim::DecoderImpl::OnPipelineStatus,
135 base::Unretained(this)));
136 }
137
138 void VideoDecoderShim::DecoderImpl::OnPipelineStatus(
139 media::PipelineStatus status) {
140 int32_t result;
141 switch (status) {
142 case media::PIPELINE_OK:
143 result = PP_OK;
144 break;
145 case media::DECODER_ERROR_NOT_SUPPORTED:
146 result = PP_ERROR_NOTSUPPORTED;
147 break;
148 default:
149 result = PP_ERROR_FAILED;
150 break;
151 }
152
153 // Calculate how many textures the shim should create.
154 uint32_t texture_pool_size =
155 max_decodes_at_decoder_ + media::limits::kMaxVideoFrames;
156 main_message_loop_->PostTask(
157 FROM_HERE,
158 base::Bind(&VideoDecoderShim::OnInitializeComplete,
159 shim_,
160 result,
161 texture_pool_size));
162 }
163
164 void VideoDecoderShim::DecoderImpl::Decode(
165 uint32_t decode_id,
166 scoped_refptr<media::DecoderBuffer> buffer) {
167 DCHECK(decoder_);
168 pending_decodes_.push(PendingDecode(decode_id, buffer));
169 DoDecode();
Ami GONE FROM CHROMIUM 2014/06/08 17:30:38 There are a bunch of encoded test streams in media
bbudge 2014/06/09 23:33:01 I was thinking along the lines of some very simple
170 }
171
172 void VideoDecoderShim::DecoderImpl::Reset() {
173 DCHECK(decoder_);
174 // Abort all pending decodes.
175 while (!pending_decodes_.empty()) {
176 const PendingDecode& decode = pending_decodes_.front();
177 scoped_ptr<PendingFrame> pending_frame(new PendingFrame(decode.decode_id));
178 main_message_loop_->PostTask(FROM_HERE,
179 base::Bind(&VideoDecoderShim::OnDecodeComplete,
180 shim_,
181 media::VideoDecoder::kAborted,
182 base::Passed(&pending_frame)));
183 pending_decodes_.pop();
184 }
185 decoder_->Reset(base::Bind(&VideoDecoderShim::DecoderImpl::OnResetComplete,
186 base::Unretained(this)));
187 }
188
189 void VideoDecoderShim::DecoderImpl::Stop() {
190 DCHECK(decoder_);
191 // Clear pending decodes now. We don't want OnDecodeComplete to call Decode
192 // again.
193 while (!pending_decodes_.empty())
194 pending_decodes_.pop();
195 decoder_->Stop();
196 // This instance is deleted once we exit this scope.
197 }
198
199 void VideoDecoderShim::DecoderImpl::DoDecode() {
200 while (!pending_decodes_.empty() &&
201 num_decodes_at_decoder_ < max_decodes_at_decoder_) {
202 num_decodes_at_decoder_++;
203 const PendingDecode& decode = pending_decodes_.front();
204 decoder_->Decode(
205 decode.buffer,
206 base::Bind(&VideoDecoderShim::DecoderImpl::OnDecodeComplete,
207 base::Unretained(this),
208 decode.decode_id));
209 pending_decodes_.pop();
210 }
211 }
212
213 void VideoDecoderShim::DecoderImpl::OnDecodeComplete(
214 uint32_t decode_id,
215 media::VideoDecoder::Status status,
216 const scoped_refptr<media::VideoFrame>& frame) {
217 num_decodes_at_decoder_--;
218 scoped_ptr<PendingFrame> pending_frame;
219 if (status == media::VideoDecoder::kOk && !frame->end_of_stream()) {
220 pending_frame.reset(new PendingFrame(decode_id, frame->coded_size()));
221 // Convert the VideoFrame pixels to ARGB.
222 libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
223 frame->stride(media::VideoFrame::kYPlane),
224 frame->data(media::VideoFrame::kUPlane),
225 frame->stride(media::VideoFrame::kUPlane),
226 frame->data(media::VideoFrame::kVPlane),
227 frame->stride(media::VideoFrame::kVPlane),
228 &pending_frame->argb_pixels.front(),
229 frame->coded_size().width() * 4,
230 frame->coded_size().width(),
231 frame->coded_size().height());
232 } else {
233 pending_frame.reset(new PendingFrame(decode_id));
234 }
235
236 int32_t result;
237 switch (status) {
238 case media::VideoDecoder::kOk:
239 // We allow partial frames, so this is OK.
240 case media::VideoDecoder::kNotEnoughData:
241 result = PP_OK;
242 break;
243 case media::VideoDecoder::kAborted:
244 result = PP_ERROR_ABORTED;
245 break;
246 case media::VideoDecoder::kDecodeError:
247 result = PP_ERROR_RESOURCE_FAILED;
248 break;
249 default:
250 NOTREACHED();
251 result = PP_ERROR_FAILED;
252 break;
253 }
254
255 main_message_loop_->PostTask(FROM_HERE,
256 base::Bind(&VideoDecoderShim::OnDecodeComplete,
257 shim_,
258 result,
259 base::Passed(&pending_frame)));
260
261 DoDecode();
262 }
263
264 void VideoDecoderShim::DecoderImpl::OnResetComplete() {
265 main_message_loop_->PostTask(
266 FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_));
267 }
268
269 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host)
270 : state_(UNINITIALIZED),
271 host_(host),
272 media_message_loop_(
273 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
274 context_provider_(
275 RenderThreadImpl::current()->SharedMainThreadContextProvider()),
276 num_pending_decodes_(0),
277 texture_pool_size_(0),
278 weak_ptr_factory_(this) {
279 DCHECK(host_);
280 DCHECK(media_message_loop_);
281 DCHECK(context_provider_);
282 decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr()));
283 }
284
285 VideoDecoderShim::~VideoDecoderShim() {
286 DCHECK(RenderThreadImpl::current());
287 // Delete any remaining textures.
288 TextureIdMap::iterator it = texture_id_map_.begin();
289 for (; it != texture_id_map_.end(); ++it)
290 DeleteTexture(it->second);
291 texture_id_map_.clear();
292
293 FlushCommandBuffer();
294
295 host_ = NULL;
296 // No methods can post tasks to the delegate now.
297 weak_ptr_factory_.InvalidateWeakPtrs();
298 // No more callbacks from the delegate will be received now.
299
300 // The callback now holds the only reference to the DecoderImpl, which will be
301 // deleted when Stop completes.
302 media_message_loop_->PostTask(
303 FROM_HERE,
304 base::Bind(&VideoDecoderShim::DecoderImpl::Stop,
305 base::Owned(decoder_impl_.release())));
306
307 }
308
309 bool VideoDecoderShim::Initialize(
310 media::VideoCodecProfile profile,
311 media::VideoDecodeAccelerator::Client* client) {
312 DCHECK_EQ(client, host_);
313 DCHECK(RenderThreadImpl::current());
314 DCHECK_EQ(state_, UNINITIALIZED);
315 media::VideoCodec codec = media::kUnknownVideoCodec;
316 if (profile <= media::H264PROFILE_MAX)
317 codec = media::kCodecH264;
318 else if (profile <= media::VP8PROFILE_MAX)
319 codec = media::kCodecVP8;
320 DCHECK_NE(codec, media::kUnknownVideoCodec);
321
322 media::VideoDecoderConfig config(
323 codec,
324 profile,
325 media::VideoFrame::YV12,
326 gfx::Size(32, 24), // Small sizes that won't fail.
327 gfx::Rect(32, 24),
328 gfx::Size(32, 24),
329 NULL /* extra_data */, // TODO(bbudge) Verify this isn't needed.
330 0 /* extra_data_size */,
331 false /* decryption */);
332
333 media_message_loop_->PostTask(
334 FROM_HERE,
335 base::Bind(&VideoDecoderShim::DecoderImpl::Initialize,
336 base::Unretained(decoder_impl_.get()),
337 config));
338 // Return success, even though we are asynchronous, to mimic
339 // media::VideoDecodeAccelerator.
340 return true;
341 }
342
343 void VideoDecoderShim::Decode(const media::BitstreamBuffer& bitstream_buffer) {
344 DCHECK(RenderThreadImpl::current());
345 DCHECK_EQ(state_, DECODING);
346
347 num_pending_decodes_++;
348
349 // We need the address of the shared memory, so we can copy into our buffer.
350 const uint8_t* buffer = host_->DecodeIdToAddress(bitstream_buffer.id());
351 DCHECK(buffer);
352
353 media_message_loop_->PostTask(
354 FROM_HERE,
355 base::Bind(
356 &VideoDecoderShim::DecoderImpl::Decode,
357 base::Unretained(decoder_impl_.get()),
358 bitstream_buffer.id(),
359 media::DecoderBuffer::CopyFrom(buffer, bitstream_buffer.size())));
360 }
361
362 void VideoDecoderShim::AssignPictureBuffers(
363 const std::vector<media::PictureBuffer>& buffers) {
364 DCHECK(RenderThreadImpl::current());
365 DCHECK_EQ(state_, DECODING);
366 if (buffers.empty()) {
367 NOTREACHED();
368 return;
369 }
370 DCHECK_EQ(buffers.size(), pending_texture_mailboxes_.size());
371 GLuint num_textures = base::checked_cast<GLuint>(buffers.size());
372 std::vector<uint32_t> local_texture_ids(num_textures);
373 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
374 gles2->GenTextures(num_textures, &local_texture_ids.front());
375 for (uint32_t i = 0; i < num_textures; i++) {
376 gles2->ActiveTexture(GL_TEXTURE0);
377 gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
378 gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
379 pending_texture_mailboxes_[i].name);
380 // Map the plugin texture id to the local texture id.
381 uint32_t plugin_texture_id = buffers[i].texture_id();
382 texture_id_map_[plugin_texture_id] = local_texture_ids[i];
383 available_textures_.push_back(plugin_texture_id);
384 }
385 pending_texture_mailboxes_.clear();
386 SendPictures();
387 }
388
389 void VideoDecoderShim::ReusePictureBuffer(int32 picture_buffer_id) {
390 DCHECK(RenderThreadImpl::current());
391 uint32_t texture_id = static_cast<uint32_t>(picture_buffer_id);
392 if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
393 DismissTexture(texture_id);
394 } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
395 available_textures_.push_back(texture_id);
396 SendPictures();
397 } else {
398 NOTREACHED();
399 }
400 }
401
402 void VideoDecoderShim::Flush() {
403 DCHECK(RenderThreadImpl::current());
404 DCHECK_EQ(state_, DECODING);
405 state_ = FLUSHING;
406 }
407
408 void VideoDecoderShim::Reset() {
409 DCHECK(RenderThreadImpl::current());
410 DCHECK_EQ(state_, DECODING);
411 state_ = RESETTING;
412 media_message_loop_->PostTask(
413 FROM_HERE,
414 base::Bind(&VideoDecoderShim::DecoderImpl::Reset,
415 base::Unretained(decoder_impl_.get())));
416 }
417
418 void VideoDecoderShim::Destroy() {
419 // This will be called, but our destructor can do the actual work.
420 }
421
422 void VideoDecoderShim::OnInitializeComplete(int32_t result,
423 uint32_t texture_pool_size) {
424 DCHECK(RenderThreadImpl::current());
425 DCHECK(host_);
426
427 if (result == PP_OK) {
428 state_ = DECODING;
429 texture_pool_size_ = texture_pool_size;
430 }
431
432 host_->OnInitializeComplete(result);
433 }
434
435 void VideoDecoderShim::OnDecodeComplete(int32_t result,
436 scoped_ptr<PendingFrame> frame) {
437 DCHECK(RenderThreadImpl::current());
438 DCHECK(host_);
439
440 num_pending_decodes_--;
441
442 if (result == PP_OK && !frame->argb_pixels.empty()) {
443 if (texture_size_ != frame->size) {
444 // If the size has changed, all current textures must be dismissed. Add
445 // all textures to |textures_to_dismiss_| and dismiss any that aren't in
446 // use by the plugin. We dismiss the rest as they are recycled.
447 for (TextureIdMap::const_iterator it = texture_id_map_.begin();
448 it != texture_id_map_.end();
449 ++it) {
450 textures_to_dismiss_.insert(it->second);
451 }
452 for (std::vector<uint32_t>::const_iterator it =
453 available_textures_.begin();
454 it != available_textures_.end();
455 ++it) {
456 DismissTexture(*it);
457 }
458 available_textures_.clear();
459 FlushCommandBuffer();
460
461 DCHECK(pending_texture_mailboxes_.empty());
462 for (uint32_t i = 0; i < texture_pool_size_; i++)
463 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
464
465 host_->RequestTextures(texture_pool_size_,
466 frame->size,
467 GL_TEXTURE_2D,
468 pending_texture_mailboxes_);
469 texture_size_ = frame->size;
470 }
471
472 pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
473 SendPictures();
474 } else {
475 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
476 }
477
478 // Report any VDA type errors.
479 if (result == PP_ERROR_RESOURCE_FAILED)
480 host_->NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
481 }
482
483 void VideoDecoderShim::SendPictures() {
484 DCHECK(RenderThreadImpl::current());
485 DCHECK(host_);
486
487 while (!pending_frames_.empty() && !available_textures_.empty()) {
488 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
489
490 uint32_t texture_id = available_textures_.back();
491 available_textures_.pop_back();
492
493 uint32_t local_texture_id = texture_id_map_[texture_id];
494 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
495 gles2->ActiveTexture(GL_TEXTURE0);
496 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
497 gles2->TexImage2D(GL_TEXTURE_2D,
498 0,
499 GL_RGBA,
500 texture_size_.width(),
501 texture_size_.height(),
502 0,
503 GL_RGBA,
504 GL_UNSIGNED_BYTE,
505 &frame->argb_pixels.front());
506
507 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
508 host_->PictureReady(media::Picture(texture_id, frame->decode_id));
509 pending_frames_.pop();
510 }
511
512 FlushCommandBuffer();
513
514 if (state_ == FLUSHING && !num_pending_decodes_ && pending_frames_.empty()) {
515 state_ = DECODING;
516 host_->NotifyFlushDone();
517 }
518 }
519
520 void VideoDecoderShim::OnResetComplete() {
521 DCHECK(RenderThreadImpl::current());
522 DCHECK(host_);
523
524 while (!pending_frames_.empty()) {
525 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
526 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
527 pending_frames_.pop();
528 }
529
530 state_ = DECODING;
531 host_->NotifyResetDone();
532 }
533
534 void VideoDecoderShim::DismissTexture(uint32_t texture_id) {
535 DCHECK(host_);
536 textures_to_dismiss_.erase(texture_id);
537 DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
538 DeleteTexture(texture_id_map_[texture_id]);
539 texture_id_map_.erase(texture_id);
540 host_->DismissPictureBuffer(texture_id);
541 }
542
543 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) {
544 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
545 gles2->DeleteTextures(1, &texture_id);
546 }
547
548 void VideoDecoderShim::FlushCommandBuffer() {
549 context_provider_->ContextGL()->Flush();
550 }
551
552 } // 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