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

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: Move SoftwareDecoder to its own file, split into Proxy/Delegate classes, State enum. 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(VideoDecoderProxy* proxy)
46 : proxy_(proxy), main_message_loop_(base::MessageLoopProxy::current()) {
47 DCHECK(proxy_);
48 }
49
50 VideoDecoderProxy::Delegate::~Delegate() {
51 DCHECK(pending_decodes_.empty());
52 }
53
54 void VideoDecoderProxy::Delegate::Initialize(
55 media::VideoDecoderConfig config,
56 scoped_ptr<media::VideoDecoder> decoder) {
57 DCHECK(!decoder_);
58 decoder_.reset(decoder.release());
59 decoder_->Initialize(
60 config,
61 true /* low_delay */,
62 base::Bind(&VideoDecoderProxy::Delegate::OnPipelineStatus,
63 base::Unretained(this)));
64 }
65
66 void VideoDecoderProxy::Delegate::OnPipelineStatus(
67 media::PipelineStatus status) {
68 main_message_loop_->PostTask(FROM_HERE,
69 base::Bind(&VideoDecoderProxy::OnPipelineStatus,
70 base::Unretained(proxy_),
71 status));
72 }
73
74 void VideoDecoderProxy::Delegate::ReceiveBuffer(
75 uint32_t decode_id,
76 scoped_refptr<media::DecoderBuffer> buffer) {
77 bool decoder_busy = !pending_decodes_.empty();
78 pending_decodes_.push(PendingDecode(decode_id, buffer));
79 if (!decoder_busy)
80 Decode();
81 }
82
83 void VideoDecoderProxy::Delegate::Decode() {
84 DCHECK(!pending_decodes_.empty());
85 PendingDecode& next_decode = pending_decodes_.front();
86 decoder_->Decode(next_decode.buffer,
87 base::Bind(&VideoDecoderProxy::Delegate::ConvertFrame,
88 base::Unretained(this),
89 next_decode.decode_id));
90 pending_decodes_.pop();
91 }
92
93 void VideoDecoderProxy::Delegate::ConvertFrame(
94 uint32_t decode_id,
95 media::VideoDecoder::Status status,
96 const scoped_refptr<media::VideoFrame>& frame) {
97 scoped_ptr<PendingFrame> pending_frame(
98 new PendingFrame(decode_id, gfx::Size()));
99 if (frame) {
100 pending_frame->size = frame->coded_size();
101 pending_frame->pixels.resize(frame->coded_size().width() *
102 frame->coded_size().height() * 4);
103 // Convert the decoded frame to ARGB pixels.
104 libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
105 frame->stride(media::VideoFrame::kYPlane),
106 frame->data(media::VideoFrame::kUPlane),
107 frame->stride(media::VideoFrame::kUPlane),
108 frame->data(media::VideoFrame::kVPlane),
109 frame->stride(media::VideoFrame::kVPlane),
110 &pending_frame->pixels.front(),
111 frame->coded_size().width() * 4,
112 frame->coded_size().width(),
113 frame->coded_size().height());
114 }
115
116 main_message_loop_->PostTask(FROM_HERE,
117 base::Bind(&VideoDecoderProxy::ReceiveFrame,
118 base::Unretained(proxy_),
119 status,
120 base::Passed(&pending_frame)));
121
122 if (!pending_decodes_.empty())
123 Decode();
124 }
125
126 void VideoDecoderProxy::Delegate::Reset() {
127 decoder_->Reset(base::Bind(&VideoDecoderProxy::Delegate::OnResetComplete,
128 base::Unretained(this)));
129 }
130
131 void VideoDecoderProxy::Delegate::OnResetComplete() {
132 // Cancel all remaining decodes, and notify the host so it can free the shm
133 // buffers. We'll clear pending frames on the main thread.
134 while (!pending_decodes_.empty()) {
135 PendingDecode& next_decode = pending_decodes_.front();
136 scoped_ptr<PendingFrame> pending_frame(
137 new PendingFrame(next_decode.decode_id, gfx::Size()));
138 main_message_loop_->PostTask(FROM_HERE,
139 base::Bind(&VideoDecoderProxy::ReceiveFrame,
140 base::Unretained(proxy_),
141 media::VideoDecoder::kAborted,
142 base::Passed(&pending_frame)));
143 pending_decodes_.pop();
144 }
145 main_message_loop_->PostTask(FROM_HERE,
146 base::Bind(&VideoDecoderProxy::OnResetComplete,
147 base::Unretained(proxy_)));
148 }
149
150 void VideoDecoderProxy::Delegate::Destroy() {
151 DCHECK(decoder_);
152 decoder_->Stop();
153 // All callbacks have been called on the media thread, and thus all tasks
154 // posted for the main thread. This is our last task for the main thread.
155 main_message_loop_->PostTask(FROM_HERE,
156 base::Bind(&VideoDecoderProxy::OnDestroyComplete,
157 base::Unretained(proxy_)));
158 }
159
160 VideoDecoderProxy::VideoDecoderProxy(PepperVideoDecoderHost* host)
161 : delegate_(this),
162 state_(UNINITIALIZED),
163 host_(host),
164 media_message_loop_(
165 RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
166 context_provider_(
167 RenderThreadImpl::current()->SharedMainThreadContextProvider()),
168 num_pending_decodes_(0) {
169 DCHECK(host_);
170 DCHECK(media_message_loop_);
171 DCHECK(context_provider_);
172 }
173
174 VideoDecoderProxy::~VideoDecoderProxy() {
175 DCHECK(RenderThreadImpl::current());
176 DCHECK(!host_);
177 // Delete any remaining video frames.
178 while (!pending_frames_.empty()) {
179 delete pending_frames_.front();
180 pending_frames_.pop();
181 }
182 // Delete any remaining textures.
183 TextureIdMap::iterator it = texture_id_map_.begin();
184 for (; it != texture_id_map_.end(); ++it)
185 DeleteTexture(it->second);
186
187 FlushCommandBuffer();
188 }
189
190 void VideoDecoderProxy::Initialize(media::VideoCodecProfile profile) {
191 DCHECK(RenderThreadImpl::current());
192 DCHECK_EQ(state_, UNINITIALIZED);
193 media::VideoCodec codec = media::kUnknownVideoCodec;
194 if (profile <= media::H264PROFILE_MAX)
195 codec = media::kCodecH264;
196 else if (profile <= media::VP8PROFILE_MAX)
197 codec = media::kCodecVP8;
198 DCHECK_NE(codec, media::kUnknownVideoCodec);
199
200 media::VideoDecoderConfig config(
201 codec,
202 profile,
203 media::VideoFrame::YV12,
204 gfx::Size(32, 24), // Small sizes that won't fail.
205 gfx::Rect(32, 24),
206 gfx::Size(32, 24),
207 NULL /* extra_data */,
208 0 /* extra_data_size */,
209 false /* decryption */);
210
211 scoped_ptr<media::VideoDecoder> decoder(
212 new media::FFmpegVideoDecoder(media_message_loop_));
213
214 media_message_loop_->PostTask(
215 FROM_HERE,
216 base::Bind(&VideoDecoderProxy::Delegate::Initialize,
217 base::Unretained(&delegate_),
218 config,
219 base::Passed(&decoder)));
220 state_ = DECODING;
221 }
222
223 void VideoDecoderProxy::Decode(uint32_t decode_id,
224 const uint8_t* buffer,
225 uint32_t size) {
226 DCHECK(RenderThreadImpl::current());
227 DCHECK_EQ(state_, DECODING);
228
229 num_pending_decodes_++;
230
231 media_message_loop_->PostTask(
232 FROM_HERE,
233 base::Bind(&VideoDecoderProxy::Delegate::ReceiveBuffer,
234 base::Unretained(&delegate_),
235 decode_id,
236 media::DecoderBuffer::CopyFrom(buffer, size)));
237 }
238
239 void VideoDecoderProxy::AssignTextures(
240 const std::vector<uint32_t>& texture_ids) {
241 DCHECK(RenderThreadImpl::current());
242 DCHECK_EQ(state_, DECODING);
243 DCHECK(texture_ids.size());
244 DCHECK_EQ(texture_ids.size(), pending_texture_mailboxes_.size());
245 uint32_t num_textures = static_cast<GLuint>(texture_ids.size());
246 std::vector<uint32_t> local_texture_ids(num_textures);
247 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
248 gles2->GenTextures(num_textures, &local_texture_ids.front());
249 for (uint32_t i = 0; i < num_textures; i++) {
250 gles2->ActiveTexture(GL_TEXTURE0);
251 gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
252 gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
253 pending_texture_mailboxes_[i].name);
254 // Map the plugin texture id to the local texture id.
255 texture_id_map_.insert(
256 std::make_pair(texture_ids[i], local_texture_ids[i]));
257 }
258 pending_texture_mailboxes_.clear();
259 available_textures_.insert(
260 available_textures_.end(), texture_ids.begin(), texture_ids.end());
261 SendPictures();
262 }
263
264 void VideoDecoderProxy::RecycleTexture(uint32_t texture_id) {
265 DCHECK(RenderThreadImpl::current());
266 if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
267 DismissTexture(texture_id);
268 } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
269 available_textures_.push_back(texture_id);
270 SendPictures();
271 } else {
272 NOTREACHED();
273 }
274 }
275
276 void VideoDecoderProxy::Flush() {
277 DCHECK(RenderThreadImpl::current());
278 DCHECK_EQ(state_, DECODING);
279 state_ = FLUSHING;
280 }
281
282 void VideoDecoderProxy::Reset() {
283 DCHECK(RenderThreadImpl::current());
284 DCHECK_EQ(state_, DECODING);
285 state_ = RESETTING;
286 media_message_loop_->PostTask(FROM_HERE,
287 base::Bind(&VideoDecoderProxy::Delegate::Reset,
288 base::Unretained(&delegate_)));
289 }
290
291 void VideoDecoderProxy::Destroy() {
292 DCHECK(RenderThreadImpl::current());
293 DCHECK(host_);
294 host_ = NULL;
295 media_message_loop_->PostTask(
296 FROM_HERE,
297 base::Bind(&VideoDecoderProxy::Delegate::Destroy,
298 base::Unretained(&delegate_)));
299 }
300
301 void VideoDecoderProxy::OnPipelineStatus(media::PipelineStatus status) {
302 DCHECK(RenderThreadImpl::current());
303 if (!host_)
304 return;
305
306 int32_t result;
307 switch (status) {
308 case media::PIPELINE_OK:
309 result = PP_OK;
310 break;
311 case media::DECODER_ERROR_NOT_SUPPORTED:
312 result = PP_ERROR_NOTSUPPORTED;
313 break;
314 default:
315 result = PP_ERROR_FAILED;
316 break;
317 }
318 host_->OnInitializeComplete(result);
319 }
320
321 void VideoDecoderProxy::ReceiveFrame(media::VideoDecoder::Status status,
322 scoped_ptr<PendingFrame> frame) {
323 DCHECK(RenderThreadImpl::current());
324 if (!host_)
325 return;
326
327 num_pending_decodes_--;
328
329 if (frame->pixels.size()) {
330 if (texture_size_ != frame->size) {
331 // If the size has changed, all current textures must be dismissed. Add
332 // all textures to |textures_to_dismiss_| and dismiss any that aren't in
333 // use by the plugin. We dismiss the rest as they are recycled.
334 for (TextureIdMap::const_iterator it = texture_id_map_.begin();
335 it != texture_id_map_.end();
336 ++it) {
337 textures_to_dismiss_.insert(it->second);
338 }
339 for (std::vector<uint32_t>::const_iterator it =
340 available_textures_.begin();
341 it != available_textures_.end();
342 ++it) {
343 DismissTexture(*it);
344 }
345 available_textures_.clear();
346 FlushCommandBuffer();
347
348 DCHECK(pending_texture_mailboxes_.empty());
349 const uint32_t num_textures = 8;
350 for (uint32_t i = 0; i < num_textures; i++)
351 pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
352
353 host_->RequestTextures(
354 num_textures, frame->size, GL_TEXTURE_2D, pending_texture_mailboxes_);
355 texture_size_ = frame->size;
356 }
357
358 pending_frames_.push(frame.release());
359 SendPictures();
360 } else {
361 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
362 }
363
364 switch (status) {
365 case media::VideoDecoder::kOk:
366 case media::VideoDecoder::kAborted:
367 // This is not necessarily an error.
368 case media::VideoDecoder::kNotEnoughData:
369 break;
370 case media::VideoDecoder::kDecodeError:
371 case media::VideoDecoder::kDecryptError:
372 host_->NotifyError(PP_ERROR_RESOURCE_FAILED);
373 break;
374 // No default case, to catch unhandled status values.
375 }
376 }
377
378 void VideoDecoderProxy::SendPictures() {
379 DCHECK(RenderThreadImpl::current());
380 if (!host_)
381 return;
382
383 while (!pending_frames_.empty() && !available_textures_.empty()) {
384 scoped_ptr<PendingFrame> frame(pending_frames_.front());
385 pending_frames_.pop();
386
387 uint32_t texture_id = available_textures_.back();
388 available_textures_.pop_back();
389
390 uint32_t local_texture_id = texture_id_map_[texture_id];
391 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
392 gles2->ActiveTexture(GL_TEXTURE0);
393 gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
394 gles2->TexImage2D(GL_TEXTURE_2D,
395 0,
396 GL_RGBA,
397 texture_size_.width(),
398 texture_size_.height(),
399 0,
400 GL_RGBA,
401 GL_UNSIGNED_BYTE,
402 &frame->pixels.front());
403
404 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
405 host_->PictureReady(media::Picture(texture_id, frame->decode_id));
406 }
407
408 FlushCommandBuffer();
409
410 if (state_ == FLUSHING && !num_pending_decodes_ && pending_frames_.empty()) {
411 state_ = DECODING;
412 host_->NotifyFlushDone();
413 }
414 }
415
416 void VideoDecoderProxy::OnResetComplete() {
417 DCHECK(RenderThreadImpl::current());
418 if (!host_)
419 return;
420
421 while (!pending_frames_.empty()) {
422 scoped_ptr<PendingFrame> frame(pending_frames_.front());
423 host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
424 pending_frames_.pop();
425 }
426
427 state_ = DECODING;
428 host_->NotifyResetDone();
429 }
430
431 void VideoDecoderProxy::OnDestroyComplete() {
432 DCHECK(RenderThreadImpl::current());
433 DCHECK(!host_);
434 delete this;
435 }
436
437 void VideoDecoderProxy::DismissTexture(uint32_t texture_id) {
438 DCHECK(host_);
439 textures_to_dismiss_.erase(texture_id);
440 DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
441 DeleteTexture(texture_id_map_[texture_id]);
442 texture_id_map_.erase(texture_id);
443 host_->DismissPictureBuffer(texture_id);
444 }
445
446 void VideoDecoderProxy::DeleteTexture(uint32_t texture_id) {
447 gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
448 gles2->DeleteTextures(1, &texture_id);
449 }
450
451 void VideoDecoderProxy::FlushCommandBuffer() {
452 DCHECK(RenderThreadImpl::current());
453 context_provider_->ContextGL()->Flush();
454 }
455
456 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698