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

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

Issue 311853005: Implement software fallback for PPB_VideoDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use WeakPtr to tie VideoDecoderProxy and Delegate. 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_proxy.h"
10
11 #include "base/bind.h"
12 #include "content/public/renderer/render_thread.h"
13 #include "content/renderer/pepper/pepper_video_decoder_host.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "gpu/command_buffer/client/gles2_implementation.h"
16 #include "media/base/decoder_buffer.h"
17 #include "media/filters/ffmpeg_video_decoder.h"
18 #include "media/video/picture.h"
19 #include "media/video/video_decode_accelerator.h"
20 #include "ppapi/c/pp_errors.h"
21 #include "third_party/libyuv/include/libyuv.h"
22 #include "webkit/common/gpu/context_provider_web_context.h"
23
24 namespace content {
25
26 VideoDecoderProxy::PendingDecode::PendingDecode(
27 uint32_t decode_id,
28 const scoped_refptr<media::DecoderBuffer>& buffer)
29 : decode_id(decode_id), buffer(buffer) {
30 }
31
32 VideoDecoderProxy::PendingDecode::~PendingDecode() {
33 }
34
35 VideoDecoderProxy::PendingFrame::PendingFrame(uint32_t decode_id,
36 const gfx::Size& size)
37 : decode_id(decode_id),
38 size(size),
39 pixels(size.width() * size.height() * 4) {
40 }
41
42 VideoDecoderProxy::PendingFrame::~PendingFrame() {
43 }
44
45 VideoDecoderProxy::Delegate::Delegate(
46 const base::WeakPtr<VideoDecoderProxy>& proxy)
47 : proxy_(proxy), main_message_loop_(base::MessageLoopProxy::current()) {
48 }
49
50 VideoDecoderProxy::Delegate::~Delegate() {
51 DCHECK(pending_decodes_.empty());
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 what guarantees this? I think you might be relying
bbudge 2014/06/06 02:03:45 Done.
52 }
53
54 void VideoDecoderProxy::Delegate::Initialize(
55 scoped_ptr<media::VideoDecoder> decoder,
56 media::VideoDecoderConfig config) {
57 DCHECK(!decoder_);
58 decoder_.reset(decoder.release());
dmichael (off chromium) 2014/06/05 23:00:43 tiny nit: I think I would write this as decoder_ =
bbudge 2014/06/06 02:03:45 This is no longer an argument.
59 decoder_->Initialize(
60 config,
61 true /* low_delay */,
62 base::Bind(&VideoDecoderProxy::Delegate::OnPipelineStatus,
63 base::Unretained(this)));
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 I guess the only reason this isn't a UAF waiting t
bbudge 2014/06/06 02:03:45 Done.
64 }
65
66 void VideoDecoderProxy::Delegate::OnPipelineStatus(
67 media::PipelineStatus status) {
68 main_message_loop_->PostTask(
69 FROM_HERE,
70 base::Bind(&VideoDecoderProxy::OnPipelineStatus, proxy_, status));
71 }
72
73 void VideoDecoderProxy::Delegate::ReceiveBuffer(
74 uint32_t decode_id,
75 scoped_refptr<media::DecoderBuffer> buffer) {
76 bool decoder_busy = !pending_decodes_.empty();
77 pending_decodes_.push(PendingDecode(decode_id, buffer));
78 if (!decoder_busy)
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 FWIW can drop decoder_busy if you do if (pending_d
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 AFAICT a client that never calls Reset() can never
bbudge 2014/06/06 02:03:44 I changed this and now call VD::GetMaxDecodeReques
bbudge 2014/06/06 02:03:45 It happens at startup and on reset. In the steady
79 Decode();
80 }
81
82 void VideoDecoderProxy::Delegate::Decode() {
83 DCHECK(!pending_decodes_.empty());
84 PendingDecode& next_decode = pending_decodes_.front();
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 const& is less surprising
bbudge 2014/06/06 02:03:45 Done.
85 decoder_->Decode(next_decode.buffer,
86 base::Bind(&VideoDecoderProxy::Delegate::ConvertFrame,
87 base::Unretained(this),
88 next_decode.decode_id));
89 pending_decodes_.pop();
90 }
91
92 void VideoDecoderProxy::Delegate::ConvertFrame(
93 uint32_t decode_id,
94 media::VideoDecoder::Status status,
95 const scoped_refptr<media::VideoFrame>& frame) {
96 scoped_ptr<PendingFrame> pending_frame(
97 new PendingFrame(decode_id, gfx::Size()));
98 if (frame) {
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 You're using frame!=NULL as a proxy for status==kO
bbudge 2014/06/06 02:03:45 So: if (status == kOk && frame && !frame->end_of_s
99 pending_frame->size = frame->coded_size();
100 pending_frame->pixels.resize(frame->coded_size().width() *
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 It's strange that you do the w*h*4 calc here _and_
bbudge 2014/06/06 02:03:45 Added a ctor to create an empty frame, and defer c
101 frame->coded_size().height() * 4);
102 // Convert the decoded frame to ARGB pixels.
103 libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
104 frame->stride(media::VideoFrame::kYPlane),
105 frame->data(media::VideoFrame::kUPlane),
106 frame->stride(media::VideoFrame::kUPlane),
107 frame->data(media::VideoFrame::kVPlane),
108 frame->stride(media::VideoFrame::kVPlane),
109 &pending_frame->pixels.front(),
110 frame->coded_size().width() * 4,
111 frame->coded_size().width(),
112 frame->coded_size().height());
113 }
114
115 main_message_loop_->PostTask(FROM_HERE,
116 base::Bind(&VideoDecoderProxy::ReceiveFrame,
117 proxy_,
118 status,
119 base::Passed(&pending_frame)));
120
121 if (!pending_decodes_.empty())
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 per comment at l.78, can this arise in the absence
bbudge 2014/06/06 02:03:45 Stop() and Reset() now clear pending frames first,
122 Decode();
123 }
124
125 void VideoDecoderProxy::Delegate::Reset() {
126 decoder_->Reset(base::Bind(&VideoDecoderProxy::Delegate::OnResetComplete,
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 set some state so that decodes that arrive from no
bbudge 2014/06/06 02:03:44 No decodes can arrive until Reset completes becaus
127 base::Unretained(this)));
128 }
129
130 void VideoDecoderProxy::Delegate::OnResetComplete() {
131 // Cancel all remaining decodes, and notify the host so it can free the shm
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 is it obvious why this shouldn't have been done in
bbudge 2014/06/06 02:03:44 No, it should be done in Reset() as you point out.
132 // buffers. We'll clear pending frames on the main thread.
133 while (!pending_decodes_.empty()) {
134 PendingDecode& next_decode = pending_decodes_.front();
135 scoped_ptr<PendingFrame> pending_frame(
136 new PendingFrame(next_decode.decode_id, gfx::Size()));
137 main_message_loop_->PostTask(FROM_HERE,
138 base::Bind(&VideoDecoderProxy::ReceiveFrame,
139 proxy_,
140 media::VideoDecoder::kAborted,
141 base::Passed(&pending_frame)));
dmichael (off chromium) 2014/06/05 23:00:43 Any reason you can't pass a null pointer instead o
bbudge 2014/06/06 02:03:44 The frame has the decode_id, which is used to iden
142 pending_decodes_.pop();
143 }
144 main_message_loop_->PostTask(
145 FROM_HERE, base::Bind(&VideoDecoderProxy::OnResetComplete, proxy_));
146 }
147
148 void VideoDecoderProxy::Delegate::Destroy() {
149 DCHECK(decoder_);
150 decoder_->Stop();
dmichael (off chromium) 2014/06/05 23:00:43 Would it be safe to instead put this in the destru
bbudge 2014/06/06 02:03:45 Stop can cause callbacks to run, and I'd rather no
151 // By now, our owning VideoDecoderProxy has invalidated our weak_ptr to it.
152 }
153
154 VideoDecoderProxy::VideoDecoderProxy(PepperVideoDecoderHost* host)
155 : state_(UNINITIALIZED),
156 host_(host),
157 media_message_loop_(
158 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
159 context_provider_(
160 RenderThreadImpl::current()->SharedMainThreadContextProvider()),
161 num_pending_decodes_(0),
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 the logic around this member requires that Decode(
bbudge 2014/06/06 02:03:44 Done.
162 weak_ptr_factory_(this) {
163 DCHECK(host_);
164 DCHECK(media_message_loop_);
165 DCHECK(context_provider_);
166 delegate_.reset(new Delegate(weak_ptr_factory_.GetWeakPtr()));
167 }
168
169 VideoDecoderProxy::~VideoDecoderProxy() {
170 DCHECK(RenderThreadImpl::current());
171 DCHECK(!host_);
172 // Delete any remaining video frames.
173 while (!pending_frames_.empty()) {
174 delete pending_frames_.front();
175 pending_frames_.pop();
176 }
177 // Delete any remaining textures.
178 TextureIdMap::iterator it = texture_id_map_.begin();
179 for (; it != texture_id_map_.end(); ++it)
180 DeleteTexture(it->second);
181
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 clear the map once done?
bbudge 2014/06/06 02:03:44 It will be destroyed once we exit the body.
182 FlushCommandBuffer();
183 }
184
185 void VideoDecoderProxy::Initialize(media::VideoCodecProfile profile) {
186 DCHECK(RenderThreadImpl::current());
187 DCHECK_EQ(state_, UNINITIALIZED);
188 media::VideoCodec codec = media::kUnknownVideoCodec;
189 if (profile <= media::H264PROFILE_MAX)
190 codec = media::kCodecH264;
191 else if (profile <= media::VP8PROFILE_MAX)
192 codec = media::kCodecVP8;
193 DCHECK_NE(codec, media::kUnknownVideoCodec);
194
195 media::VideoDecoderConfig config(
196 codec,
197 profile,
198 media::VideoFrame::YV12,
199 gfx::Size(32, 24), // Small sizes that won't fail.
200 gfx::Rect(32, 24),
201 gfx::Size(32, 24),
202 NULL /* extra_data */,
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 durr, does this really work? e.g. does the h264 te
bbudge 2014/06/06 02:03:45 I don't know. I'll have to try it. I have no idea
203 0 /* extra_data_size */,
204 false /* decryption */);
205
206 scoped_ptr<media::VideoDecoder> decoder(
207 new media::FFmpegVideoDecoder(media_message_loop_));
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 Why not VpxVideoDecoder (for VP9) or one of the de
bbudge 2014/06/06 02:03:45 You're right. I moved VideoDecoder construction in
208
209 media_message_loop_->PostTask(
210 FROM_HERE,
211 base::Bind(&VideoDecoderProxy::Delegate::Initialize,
212 base::Unretained(delegate_.get()),
213 base::Passed(&decoder),
214 config));
215 state_ = DECODING;
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 shouldn't this transition only happen in the OK ca
bbudge 2014/06/06 02:03:45 Yep.
216 }
217
218 void VideoDecoderProxy::Decode(uint32_t decode_id,
219 const uint8_t* buffer,
220 uint32_t size) {
221 DCHECK(RenderThreadImpl::current());
222 DCHECK_EQ(state_, DECODING);
223
224 num_pending_decodes_++;
225
226 media_message_loop_->PostTask(
227 FROM_HERE,
228 base::Bind(&VideoDecoderProxy::Delegate::ReceiveBuffer,
229 base::Unretained(delegate_.get()),
230 decode_id,
231 media::DecoderBuffer::CopyFrom(buffer, size)));
232 }
233
234 void VideoDecoderProxy::AssignTextures(
235 const std::vector<uint32_t>& texture_ids) {
236 DCHECK(RenderThreadImpl::current());
237 DCHECK_EQ(state_, DECODING);
238 DCHECK(texture_ids.size());
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 DCHECK(texture_id_map_.empty()); ?
dmichael (off chromium) 2014/06/05 23:00:44 With a "!", of course :)
bbudge 2014/06/06 02:03:45 I don't think so. We AssignTextures when the video
239 DCHECK_EQ(texture_ids.size(), pending_texture_mailboxes_.size());
240 uint32_t num_textures = static_cast<GLuint>(texture_ids.size());
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 checked_cast to avoid silent overflow
bbudge 2014/06/06 02:03:45 Done.
241 std::vector<uint32_t> local_texture_ids(num_textures);
242 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
243 gles2->GenTextures(num_textures, &local_texture_ids.front());
244 for (uint32_t i = 0; i < num_textures; i++) {
245 gles2->ActiveTexture(GL_TEXTURE0);
246 gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
247 gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
248 pending_texture_mailboxes_[i].name);
249 // Map the plugin texture id to the local texture id.
250 texture_id_map_.insert(
251 std::make_pair(texture_ids[i], local_texture_ids[i]));
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 since you're ignoring return value you may as well
bbudge 2014/06/06 02:03:44 Done.
252 }
253 pending_texture_mailboxes_.clear();
254 available_textures_.insert(
255 available_textures_.end(), texture_ids.begin(), texture_ids.end());
256 SendPictures();
257 }
258
259 void VideoDecoderProxy::RecycleTexture(uint32_t texture_id) {
260 DCHECK(RenderThreadImpl::current());
261 if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
262 DismissTexture(texture_id);
263 } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
264 available_textures_.push_back(texture_id);
265 SendPictures();
266 } else {
267 NOTREACHED();
268 }
269 }
270
271 void VideoDecoderProxy::Flush() {
272 DCHECK(RenderThreadImpl::current());
273 DCHECK_EQ(state_, DECODING);
274 state_ = FLUSHING;
275 }
276
277 void VideoDecoderProxy::Reset() {
278 DCHECK(RenderThreadImpl::current());
279 DCHECK_EQ(state_, DECODING);
280 state_ = RESETTING;
281 media_message_loop_->PostTask(FROM_HERE,
282 base::Bind(&VideoDecoderProxy::Delegate::Reset,
283 base::Unretained(delegate_.get())));
284 }
285
286 void VideoDecoderProxy::Destroy() {
287 DCHECK(RenderThreadImpl::current());
288 DCHECK(host_);
289 host_ = NULL;
290 // Cut the delegate loose.
291 weak_ptr_factory_.InvalidateWeakPtrs();
292 media_message_loop_->PostTask(
293 FROM_HERE,
294 base::Bind(&VideoDecoderProxy::Delegate::Destroy,
295 base::Owned(delegate_.release())));
296
297 delete this;
dmichael (off chromium) 2014/06/05 23:00:43 Can we just do all this in a destructor? (aside fr
bbudge 2014/06/06 02:03:44 That's too late. There might be pending callbacks
298 }
299
300 void VideoDecoderProxy::OnPipelineStatus(media::PipelineStatus status) {
301 DCHECK(RenderThreadImpl::current());
302 DCHECK(host_);
303
304 int32_t result;
305 switch (status) {
306 case media::PIPELINE_OK:
307 result = PP_OK;
308 break;
309 case media::DECODER_ERROR_NOT_SUPPORTED:
310 result = PP_ERROR_NOTSUPPORTED;
311 break;
312 default:
313 result = PP_ERROR_FAILED;
314 break;
315 }
316 host_->OnInitializeComplete(result);
317 }
318
319 void VideoDecoderProxy::ReceiveFrame(media::VideoDecoder::Status status,
320 scoped_ptr<PendingFrame> frame) {
321 DCHECK(RenderThreadImpl::current());
322 DCHECK(host_);
323
324 num_pending_decodes_--;
325
326 if (frame->pixels.size()) {
327 if (texture_size_ != frame->size) {
328 // If the size has changed, all current textures must be dismissed. Add
329 // all textures to |textures_to_dismiss_| and dismiss any that aren't in
330 // use by the plugin. We dismiss the rest as they are recycled.
331 for (TextureIdMap::const_iterator it = texture_id_map_.begin();
332 it != texture_id_map_.end();
333 ++it) {
334 textures_to_dismiss_.insert(it->second);
335 }
336 for (std::vector<uint32_t>::const_iterator it =
337 available_textures_.begin();
338 it != available_textures_.end();
339 ++it) {
340 DismissTexture(*it);
341 }
342 available_textures_.clear();
343 FlushCommandBuffer();
344
345 DCHECK(pending_texture_mailboxes_.empty());
346 const uint32_t num_textures = 8;
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 o rly 8?
bbudge 2014/06/06 02:03:44 Yeah, this is just a guess. It seems like 1 might
347 for (uint32_t i = 0; i < num_textures; i++)
348 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
349
350 host_->RequestTextures(
351 num_textures, frame->size, GL_TEXTURE_2D, pending_texture_mailboxes_);
352 texture_size_ = frame->size;
353 }
354
355 pending_frames_.push(frame.release());
356 SendPictures();
357 } else {
358 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 In case of error this is wrong.
bbudge 2014/06/06 02:03:44 I don't see why. With VideoDecodeAccelerator we no
359 }
360
361 switch (status) {
362 case media::VideoDecoder::kOk:
363 case media::VideoDecoder::kAborted:
364 // This is not necessarily an error.
365 case media::VideoDecoder::kNotEnoughData:
366 break;
367 case media::VideoDecoder::kDecodeError:
368 case media::VideoDecoder::kDecryptError:
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 _decrypt_ error should be unreachable, right?
bbudge 2014/06/06 02:03:44 Done.
369 host_->NotifyError(PP_ERROR_RESOURCE_FAILED);
370 break;
371 // No default case, to catch unhandled status values.
372 }
373 }
374
375 void VideoDecoderProxy::SendPictures() {
376 DCHECK(RenderThreadImpl::current());
377 DCHECK(host_);
378
379 while (!pending_frames_.empty() && !available_textures_.empty()) {
380 scoped_ptr<PendingFrame> frame(pending_frames_.front());
381 pending_frames_.pop();
382
383 uint32_t texture_id = available_textures_.back();
384 available_textures_.pop_back();
385
386 uint32_t local_texture_id = texture_id_map_[texture_id];
387 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
388 gles2->ActiveTexture(GL_TEXTURE0);
389 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
390 gles2->TexImage2D(GL_TEXTURE_2D,
391 0,
392 GL_RGBA,
393 texture_size_.width(),
394 texture_size_.height(),
395 0,
396 GL_RGBA,
397 GL_UNSIGNED_BYTE,
398 &frame->pixels.front());
399
400 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
401 host_->PictureReady(media::Picture(texture_id, frame->decode_id));
402 }
403
404 FlushCommandBuffer();
405
406 if (state_ == FLUSHING && !num_pending_decodes_ && pending_frames_.empty()) {
407 state_ = DECODING;
408 host_->NotifyFlushDone();
409 }
410 }
411
412 void VideoDecoderProxy::OnResetComplete() {
413 DCHECK(RenderThreadImpl::current());
414 DCHECK(host_);
415
416 while (!pending_frames_.empty()) {
417 scoped_ptr<PendingFrame> frame(pending_frames_.front());
418 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
Ami GONE FROM CHROMIUM 2014/06/05 00:06:24 Not sure this is necessary, FWIW.
bbudge 2014/06/06 02:03:44 It makes it a little simpler in the host, since I
419 pending_frames_.pop();
420 }
421
422 state_ = DECODING;
423 host_->NotifyResetDone();
424 }
425
426 void VideoDecoderProxy::DismissTexture(uint32_t texture_id) {
427 DCHECK(host_);
428 textures_to_dismiss_.erase(texture_id);
429 DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
430 DeleteTexture(texture_id_map_[texture_id]);
431 texture_id_map_.erase(texture_id);
432 host_->DismissPictureBuffer(texture_id);
433 }
434
435 void VideoDecoderProxy::DeleteTexture(uint32_t texture_id) {
436 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
437 gles2->DeleteTextures(1, &texture_id);
438 }
439
440 void VideoDecoderProxy::FlushCommandBuffer() {
441 DCHECK(RenderThreadImpl::current());
442 context_provider_->ContextGL()->Flush();
443 }
444
445 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698