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

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

Issue 311853005: Implement software fallback for PPB_VideoDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fischman's and dmichael's review comments. 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_adapter.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/filters/ffmpeg_video_decoder.h"
19 #include "media/video/picture.h"
20 #include "media/video/video_decode_accelerator.h"
21 #include "ppapi/c/pp_errors.h"
22 #include "third_party/libyuv/include/libyuv.h"
23 #include "webkit/common/gpu/context_provider_web_context.h"
24
25 namespace content {
26
27 struct VideoDecoderAdapter::PendingDecode {
28 PendingDecode(uint32_t decode_id,
29 const scoped_refptr<media::DecoderBuffer>& buffer);
30 ~PendingDecode();
31
32 const uint32_t decode_id;
33 const scoped_refptr<media::DecoderBuffer> buffer;
34 };
35
36 VideoDecoderAdapter::PendingDecode::PendingDecode(
37 uint32_t decode_id,
38 const scoped_refptr<media::DecoderBuffer>& buffer)
39 : decode_id(decode_id), buffer(buffer) {
40 }
41
42 VideoDecoderAdapter::PendingDecode::~PendingDecode() {
43 }
44
45 struct VideoDecoderAdapter::PendingFrame {
46 explicit PendingFrame(uint32_t decode_id);
47 PendingFrame(uint32_t decode_id, const gfx::Size& size);
48 ~PendingFrame();
49
50 const uint32_t decode_id;
51 const gfx::Size size;
52 std::vector<uint8_t> argb_pixels;
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 Experimentation is the only authority :(
dmichael (off chromium) 2014/06/06 17:24:00 private: DISALLOW_COPY_AND_ASSIGN(PendingFrame);
bbudge 2014/06/07 00:31:09 Done.
bbudge 2014/06/07 00:31:09 Looks like ARGB. I guess as long as there are an e
53 };
54
55 VideoDecoderAdapter::PendingFrame::PendingFrame(uint32_t decode_id)
56 : decode_id(decode_id) {
57 }
58
59 VideoDecoderAdapter::PendingFrame::PendingFrame(uint32_t decode_id,
60 const gfx::Size& size)
61 : decode_id(decode_id),
62 size(size),
63 argb_pixels(size.width() * size.height() * 4) {
64 }
65
66 VideoDecoderAdapter::PendingFrame::~PendingFrame() {
67 }
68
69 VideoDecoderAdapter::Delegate::Delegate(
70 const base::WeakPtr<VideoDecoderAdapter>& proxy)
71 : adapter_(proxy),
72 main_message_loop_(base::MessageLoopProxy::current()),
73 max_pending_decodes_(0),
74 num_pending_decodes_(0) {
75 }
76
77 VideoDecoderAdapter::Delegate::~Delegate() {
78 DCHECK(pending_decodes_.empty());
79 }
80
81 void VideoDecoderAdapter::Delegate::Initialize(
82 media::VideoDecoderConfig config) {
83 DCHECK(!decoder_);
84 decoder_.reset(
85 new media::FFmpegVideoDecoder(base::MessageLoopProxy::current()));
86 max_pending_decodes_ = decoder_->GetMaxDecodeRequests();
87 // We can use base::Unretained() safely in decoder callbacks because we call
88 // VideoDecoder::Stop() before deletion. Stop() guarantees there will be no
89 // outstanding callbacks after it returns.
90 decoder_->Initialize(
91 config,
92 true /* low_delay */,
93 base::Bind(&VideoDecoderAdapter::Delegate::OnPipelineStatus,
94 base::Unretained(this)));
95 }
96
97 void VideoDecoderAdapter::Delegate::OnPipelineStatus(
98 media::PipelineStatus status) {
99 main_message_loop_->PostTask(
100 FROM_HERE,
101 base::Bind(&VideoDecoderAdapter::OnPipelineStatus, adapter_, status));
102 }
103
104 void VideoDecoderAdapter::Delegate::Decode(
105 uint32_t decode_id,
106 scoped_refptr<media::DecoderBuffer> buffer) {
107 DCHECK(decoder_);
108 if (num_pending_decodes_ < max_pending_decodes_) {
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 num_pending_decodes_ holding a value other than pe
bbudge 2014/06/07 00:31:09 Done.
109 num_pending_decodes_++;
110 decoder_->Decode(
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 DCHECK(pending_decodes_.empty());
bbudge 2014/06/07 00:31:09 It might not be true. I think you identified a rea
111 buffer,
112 base::Bind(&VideoDecoderAdapter::Delegate::OnDecodeComplete,
113 base::Unretained(this),
114 decode_id));
115 } else {
116 pending_decodes_.push(PendingDecode(decode_id, buffer));
117 }
118 }
119
120 void VideoDecoderAdapter::Delegate::Reset() {
121 DCHECK(decoder_);
122 // Abort all pending decodes.
123 while (!pending_decodes_.empty()) {
124 PendingDecode& decode = pending_decodes_.front();
dmichael (off chromium) 2014/06/06 17:24:00 nit: const& ?
bbudge 2014/06/07 00:31:09 Done.
125 scoped_ptr<PendingFrame> pending_frame(new PendingFrame(decode.decode_id));
126 main_message_loop_->PostTask(
127 FROM_HERE,
128 base::Bind(&VideoDecoderAdapter::OnDecodeComplete,
129 adapter_,
130 media::VideoDecoder::kAborted,
131 base::Passed(&pending_frame)));
132 pending_decodes_.pop();
133 }
134 decoder_->Reset(base::Bind(&VideoDecoderAdapter::Delegate::OnResetComplete,
135 base::Unretained(this)));
136 }
137
138 void VideoDecoderAdapter::Delegate::Stop() {
139 DCHECK(decoder_);
140 // Clear pending decodes now. We don't want OnDecodeComplete to call Decode
141 // again.
142 while (!pending_decodes_.empty())
143 pending_decodes_.pop();
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 pending_decodes_.clear() instead of the previous t
bbudge 2014/06/07 00:31:09 Can't, it's std::queue.
144 decoder_->Stop();
145 // This instance is deleted once we exit this scope.
146 }
147
148 void VideoDecoderAdapter::Delegate::OnDecodeComplete(
149 uint32_t decode_id,
150 media::VideoDecoder::Status status,
151 const scoped_refptr<media::VideoFrame>& frame) {
152 num_pending_decodes_--;
153 scoped_ptr<PendingFrame> pending_frame;
154 if (status == media::VideoDecoder::kOk && frame && !frame->end_of_stream()) {
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 && frame is unnecessary here.
bbudge 2014/06/07 00:31:09 Done.
155 pending_frame.reset(new PendingFrame(decode_id, frame->coded_size()));
156 // Convert the VideoFrame pixels to ARGB.
157 libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
158 frame->stride(media::VideoFrame::kYPlane),
159 frame->data(media::VideoFrame::kUPlane),
160 frame->stride(media::VideoFrame::kUPlane),
161 frame->data(media::VideoFrame::kVPlane),
162 frame->stride(media::VideoFrame::kVPlane),
163 &pending_frame->argb_pixels.front(),
164 frame->coded_size().width() * 4,
165 frame->coded_size().width(),
166 frame->coded_size().height());
167 } else {
168 pending_frame.reset(new PendingFrame(decode_id));
169 }
170
171 main_message_loop_->PostTask(
172 FROM_HERE,
173 base::Bind(&VideoDecoderAdapter::OnDecodeComplete,
174 adapter_,
175 status,
176 base::Passed(&pending_frame)));
177
178 if (!pending_decodes_.empty()) {
179 const PendingDecode& decode = pending_decodes_.front();
180 Decode(decode.decode_id, decode.buffer);
181 pending_decodes_.pop();
182 }
183 }
184
185 void VideoDecoderAdapter::Delegate::OnResetComplete() {
186 main_message_loop_->PostTask(
187 FROM_HERE, base::Bind(&VideoDecoderAdapter::OnResetComplete, adapter_));
188 }
189
190 VideoDecoderAdapter::VideoDecoderAdapter(PepperVideoDecoderHost* host)
191 : state_(UNINITIALIZED),
192 host_(host),
193 media_message_loop_(
194 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
195 context_provider_(
196 RenderThreadImpl::current()->SharedMainThreadContextProvider()),
197 num_pending_decodes_(0),
198 weak_ptr_factory_(this) {
199 DCHECK(host_);
200 DCHECK(media_message_loop_);
201 DCHECK(context_provider_);
202 delegate_.reset(new Delegate(weak_ptr_factory_.GetWeakPtr()));
203 }
204
205 VideoDecoderAdapter::~VideoDecoderAdapter() {
206 DCHECK(RenderThreadImpl::current());
207 DCHECK(!host_);
208 // Delete any remaining textures.
209 TextureIdMap::iterator it = texture_id_map_.begin();
210 for (; it != texture_id_map_.end(); ++it)
211 DeleteTexture(it->second);
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 Yeah, I realize; still that's what I'd do. (SEGV's
bbudge 2014/06/07 00:31:09 Done.
212
213 FlushCommandBuffer();
214 }
215
216 void VideoDecoderAdapter::Initialize(media::VideoCodecProfile profile) {
217 DCHECK(RenderThreadImpl::current());
218 DCHECK_EQ(state_, UNINITIALIZED);
219 media::VideoCodec codec = media::kUnknownVideoCodec;
220 if (profile <= media::H264PROFILE_MAX)
221 codec = media::kCodecH264;
222 else if (profile <= media::VP8PROFILE_MAX)
223 codec = media::kCodecVP8;
224 DCHECK_NE(codec, media::kUnknownVideoCodec);
225
226 media::VideoDecoderConfig config(
227 codec,
228 profile,
229 media::VideoFrame::YV12,
230 gfx::Size(32, 24), // Small sizes that won't fail.
231 gfx::Rect(32, 24),
232 gfx::Size(32, 24),
233 NULL /* extra_data */,
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 Please add a TODO.
bbudge 2014/06/07 00:31:09 Done.
234 0 /* extra_data_size */,
235 false /* decryption */);
236
237 media_message_loop_->PostTask(
238 FROM_HERE,
239 base::Bind(&VideoDecoderAdapter::Delegate::Initialize,
240 base::Unretained(delegate_.get()),
241 config));
242 }
243
244 void VideoDecoderAdapter::Decode(uint32_t decode_id,
245 const uint8_t* buffer,
246 uint32_t size) {
247 DCHECK(RenderThreadImpl::current());
248 DCHECK_EQ(state_, DECODING);
249
250 num_pending_decodes_++;
251
252 media_message_loop_->PostTask(
253 FROM_HERE,
254 base::Bind(&VideoDecoderAdapter::Delegate::Decode,
255 base::Unretained(delegate_.get()),
256 decode_id,
257 media::DecoderBuffer::CopyFrom(buffer, size)));
258 }
259
260 void VideoDecoderAdapter::AssignTextures(
261 const std::vector<uint32_t>& texture_ids) {
262 DCHECK(RenderThreadImpl::current());
263 DCHECK_EQ(state_, DECODING);
264 DCHECK(texture_ids.size());
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 Wouldn't such a texture be in textures_to_dismiss_
dmichael (off chromium) 2014/06/06 17:24:00 I think you should maybe reformat this to somethin
bbudge 2014/06/07 00:31:09 Done.
bbudge 2014/06/07 00:31:09 It would be in both. textures_to_dismiss_ may cont
265 DCHECK_EQ(texture_ids.size(), pending_texture_mailboxes_.size());
266 GLuint num_textures = base::checked_cast<GLuint>(texture_ids.size());
267 std::vector<uint32_t> local_texture_ids(num_textures);
268 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
269 gles2->GenTextures(num_textures, &local_texture_ids.front());
270 for (uint32_t i = 0; i < num_textures; i++) {
271 gles2->ActiveTexture(GL_TEXTURE0);
272 gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
273 gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
274 pending_texture_mailboxes_[i].name);
275 // Map the plugin texture id to the local texture id.
276 texture_id_map_[texture_ids[i]] = local_texture_ids[i];
277 }
278 pending_texture_mailboxes_.clear();
279 available_textures_.insert(
280 available_textures_.end(), texture_ids.begin(), texture_ids.end());
281 SendPictures();
282 }
283
284 void VideoDecoderAdapter::RecycleTexture(uint32_t texture_id) {
285 DCHECK(RenderThreadImpl::current());
286 if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
287 DismissTexture(texture_id);
288 } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
289 available_textures_.push_back(texture_id);
290 SendPictures();
291 } else {
292 NOTREACHED();
293 }
294 }
295
296 void VideoDecoderAdapter::Flush() {
297 DCHECK(RenderThreadImpl::current());
298 DCHECK_EQ(state_, DECODING);
299 state_ = FLUSHING;
300 }
301
302 void VideoDecoderAdapter::Reset() {
303 DCHECK(RenderThreadImpl::current());
304 DCHECK_EQ(state_, DECODING);
305 state_ = RESETTING;
306 media_message_loop_->PostTask(
307 FROM_HERE,
308 base::Bind(&VideoDecoderAdapter::Delegate::Reset,
309 base::Unretained(delegate_.get())));
310 }
311
312 void VideoDecoderAdapter::Destroy() {
313 DCHECK(RenderThreadImpl::current());
314 DCHECK(host_);
315 host_ = NULL;
316 state_ = DESTROYED;
317 // No methods can post tasks to the delegate now.
318 weak_ptr_factory_.InvalidateWeakPtrs();
319 // No more callbacks from the delegate will be received now.
320
321 // The callback now holds the only reference to the delegate, which will be
322 // deleted when Stop completes.
323 media_message_loop_->PostTask(FROM_HERE,
324 base::Bind(&VideoDecoderAdapter::Delegate::Stop,
325 base::Owned(delegate_.release())));
dmichael (off chromium) 2014/06/06 17:24:00 You convinced me that VideoDecoderAdapter::Delegat
bbudge 2014/06/07 00:31:09 We're posting a task to the media loop to Stop the
bbudge 2014/06/07 01:01:23 Ugh, there you go being right again. Yeah, since I
326
327 delete this;
328 }
329
330 void VideoDecoderAdapter::OnPipelineStatus(media::PipelineStatus status) {
331 DCHECK(RenderThreadImpl::current());
332 DCHECK(host_);
333
334 int32_t result;
335 switch (status) {
336 case media::PIPELINE_OK:
337 result = PP_OK;
338 state_ = DECODING;
339 break;
340 case media::DECODER_ERROR_NOT_SUPPORTED:
341 result = PP_ERROR_NOTSUPPORTED;
342 break;
343 default:
344 result = PP_ERROR_FAILED;
345 break;
346 }
347 host_->OnInitializeComplete(result);
348 }
349
350 void VideoDecoderAdapter::OnDecodeComplete(media::VideoDecoder::Status status,
351 scoped_ptr<PendingFrame> frame) {
352 DCHECK(RenderThreadImpl::current());
353 DCHECK(host_);
354
355 num_pending_decodes_--;
356
357 if (!frame->argb_pixels.empty()) {
358 if (texture_size_ != frame->size) {
359 // If the size has changed, all current textures must be dismissed. Add
360 // all textures to |textures_to_dismiss_| and dismiss any that aren't in
361 // use by the plugin. We dismiss the rest as they are recycled.
362 for (TextureIdMap::const_iterator it = texture_id_map_.begin();
363 it != texture_id_map_.end();
364 ++it) {
365 textures_to_dismiss_.insert(it->second);
366 }
367 for (std::vector<uint32_t>::const_iterator it =
368 available_textures_.begin();
369 it != available_textures_.end();
370 ++it) {
371 DismissTexture(*it);
372 }
373 available_textures_.clear();
374 FlushCommandBuffer();
375
376 DCHECK(pending_texture_mailboxes_.empty());
377 const uint32_t num_textures = 8;
Ami GONE FROM CHROMIUM 2014/06/06 17:14:33 In order to saturate both the decoder and the rend
bbudge 2014/06/07 00:31:09 Done.
378 for (uint32_t i = 0; i < num_textures; i++)
379 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
380
381 host_->RequestTextures(
382 num_textures, frame->size, GL_TEXTURE_2D, pending_texture_mailboxes_);
383 texture_size_ = frame->size;
384 }
385
386 pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
387 SendPictures();
388 } else {
389 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
390 }
391
392 switch (status) {
393 case media::VideoDecoder::kOk:
394 case media::VideoDecoder::kAborted:
395 // This is not necessarily an error.
396 case media::VideoDecoder::kNotEnoughData:
397 break;
398 case media::VideoDecoder::kDecodeError:
399 host_->NotifyError(PP_ERROR_RESOURCE_FAILED);
400 break;
401 case media::VideoDecoder::kDecryptError:
402 NOTREACHED();
403 break;
404 // No default case, to catch unhandled status values.
405 }
406 }
407
408 void VideoDecoderAdapter::SendPictures() {
409 DCHECK(RenderThreadImpl::current());
410 DCHECK(host_);
411
412 while (!pending_frames_.empty() && !available_textures_.empty()) {
413 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
414
415 uint32_t texture_id = available_textures_.back();
416 available_textures_.pop_back();
417
418 uint32_t local_texture_id = texture_id_map_[texture_id];
419 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
420 gles2->ActiveTexture(GL_TEXTURE0);
421 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
422 gles2->TexImage2D(GL_TEXTURE_2D,
423 0,
424 GL_RGBA,
425 texture_size_.width(),
426 texture_size_.height(),
427 0,
428 GL_RGBA,
429 GL_UNSIGNED_BYTE,
430 &frame->argb_pixels.front());
431
432 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
433 host_->PictureReady(media::Picture(texture_id, frame->decode_id));
434 pending_frames_.pop();
435 }
436
437 FlushCommandBuffer();
438
439 if (state_ == FLUSHING && !num_pending_decodes_ && pending_frames_.empty()) {
440 state_ = DECODING;
441 host_->NotifyFlushDone();
442 }
443 }
444
445 void VideoDecoderAdapter::OnResetComplete() {
446 DCHECK(RenderThreadImpl::current());
447 DCHECK(host_);
448
449 while (!pending_frames_.empty()) {
450 const linked_ptr<PendingFrame>& frame = pending_frames_.front();
451 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
452 pending_frames_.pop();
453 }
454
455 state_ = DECODING;
456 host_->NotifyResetDone();
457 }
458
459 void VideoDecoderAdapter::DismissTexture(uint32_t texture_id) {
460 DCHECK(host_);
461 textures_to_dismiss_.erase(texture_id);
462 DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
463 DeleteTexture(texture_id_map_[texture_id]);
464 texture_id_map_.erase(texture_id);
465 host_->DismissPictureBuffer(texture_id);
466 }
467
468 void VideoDecoderAdapter::DeleteTexture(uint32_t texture_id) {
469 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
470 gles2->DeleteTextures(1, &texture_id);
471 }
472
473 void VideoDecoderAdapter::FlushCommandBuffer() {
474 context_provider_->ContextGL()->Flush();
475 }
476
477 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698