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

Unified Diff: content/renderer/pepper/pepper_video_decoder_host.cc

Issue 311853005: Implement software fallback for PPB_VideoDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: content/renderer/pepper/pepper_video_decoder_host.cc
diff --git a/content/renderer/pepper/pepper_video_decoder_host.cc b/content/renderer/pepper/pepper_video_decoder_host.cc
index 16019f720b29e4e6be4a8e1d236043dfecd3802e..db3c114889121b90e178cdf9c4540235f3ddbd9e 100644
--- a/content/renderer/pepper/pepper_video_decoder_host.cc
+++ b/content/renderer/pepper/pepper_video_decoder_host.cc
@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2extchromium.h>
+
#include "content/renderer/pepper/pepper_video_decoder_host.h"
#include "base/bind.h"
@@ -12,6 +16,9 @@
#include "content/renderer/pepper/ppb_graphics_3d_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "media/base/decoder_buffer.h"
+#include "media/filters/ffmpeg_video_decoder.h"
#include "media/video/picture.h"
#include "media/video/video_decode_accelerator.h"
#include "ppapi/c/pp_completion_callback.h"
@@ -22,6 +29,8 @@
#include "ppapi/proxy/video_decoder_constants.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/ppb_graphics_3d_api.h"
+#include "third_party/libyuv/include/libyuv.h"
+#include "webkit/common/gpu/context_provider_web_context.h"
using ppapi::proxy::SerializedHandle;
using ppapi::thunk::EnterResourceNoLock;
@@ -65,6 +74,534 @@ media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
} // namespace
+// This wrapper class calls methods on a media::VideoDecoder on the media thread
+// and relays results back to the host on the main thread. It should be created,
+// used, and destroyed on the main (render) thread.
+class SoftwareDecoder {
+ public:
+ explicit SoftwareDecoder(PepperVideoDecoderHost* host);
+
+ void Initialize(media::VideoCodecProfile profile);
+ void Decode(uint32_t decode_id,
+ const scoped_refptr<media::DecoderBuffer>& buffer);
+ void AssignTextures(const std::vector<uint32_t>& texture_ids);
+ void RecycleTexture(uint32_t texture_id);
+ void Flush();
+ void Reset();
+ void Destroy();
+
+ protected:
+ // Do not delete directly; use Destroy() or own it with a scoped_ptr, which
+ // will Destroy() it properly by default.
+ ~SoftwareDecoder();
+
+ private:
+ struct PendingDecode {
+ PendingDecode(uint32_t decode_id,
+ const scoped_refptr<media::DecoderBuffer>& buffer);
+ ~PendingDecode();
+
+ uint32_t decode_id;
+ scoped_refptr<media::DecoderBuffer> buffer;
+ };
+
+ struct PendingFrame {
+ PendingFrame(uint32_t decode_id, const gfx::Size& size);
+ ~PendingFrame();
+
+ uint32_t decode_id;
+ gfx::Size size;
+ std::vector<uint8_t> pixels;
+ };
+
+ void InitializeOnMediaThread(media::VideoDecoderConfig config);
+ void PipelineStatusOnMediaThread(media::PipelineStatus status);
+ void PipelineStatusOnMainThread(media::PipelineStatus status);
+
+ void ReceiveBufferOnMediaThread(uint32_t decode_id,
+ scoped_refptr<media::DecoderBuffer> buffer);
+ void DecodeOnMediaThread();
+ void ConvertFrameOnMediaThread(uint32_t decode_id,
+ media::VideoDecoder::Status status,
+ const scoped_refptr<media::VideoFrame>& frame);
+ void ReceiveFrameOnMainThread(media::VideoDecoder::Status status,
+ scoped_ptr<PendingFrame> frame);
+ void SendPicturesOnMainThread();
+
+ void DoResetOnMediaThread();
+ void ResetCompleteOnMediaThread();
+ void ResetCompleteOnMainThread();
+
+ void DestroyOnMediaThread();
+ void DestroyOnMainThread();
+
+ void DismissTexture(uint32_t texture_id);
+ void DeleteTexture(uint32_t texture_id);
+ void FlushCommandBuffer();
+
+ // These members are accessed only on the main thread.
+
+ PepperVideoDecoderHost* host_;
+ scoped_refptr<base::MessageLoopProxy> media_message_loop_;
+ scoped_refptr<webkit::gpu::ContextProviderWebContext> context_provider_;
+ // The current decoded frame size.
+ gfx::Size texture_size_;
+ // Map that takes the plugin's GL texture id to the renderer's GL texture id.
+ typedef base::hash_map<uint32_t, uint32_t> TextureIdMap;
+ TextureIdMap texture_id_map_;
+ // Available textures (these are plugin ids.)
+ std::vector<uint32_t> available_textures_;
+ // Track textures that are no longer needed (these are plugin ids.)
+ typedef base::hash_set<uint32_t> TextureIdSet;
+ TextureIdSet textures_to_dismiss_;
+ // Mailboxes for pending texture requests, to write to plugin's textures.
+ std::vector<gpu::Mailbox> pending_texture_mailboxes_;
+ // Queue of pending decoded frames. These have been converted to RGB, and
+ // await upload to a GL texture.
+ typedef std::queue<PendingFrame*> PendingFrameQueue;
+ PendingFrameQueue pending_frames_;
+ uint32_t num_pending_decodes_;
+ bool flushing_;
+ bool resetting_;
dmichael (off chromium) 2014/06/03 22:27:47 suggestion: Might be clearer to use a 3-state enum
bbudge 2014/06/04 14:10:12 UNITIALIZED, DECODING, FLUSHING, RESETTING. Done.
+
+ // These members are accessed only on the media thread.
+
+ scoped_ptr<media::VideoDecoder> decoder_;
+ scoped_refptr<base::MessageLoopProxy> main_message_loop_;
+ // Queue of decodes waiting for the decoder.
+ typedef std::queue<PendingDecode> PendingDecodeQueue;
+ PendingDecodeQueue pending_decodes_;
dmichael (off chromium) 2014/06/03 22:27:47 This is a bit hard to follow with the mix of opera
bbudge 2014/06/04 14:10:12 I moved SoftwareDecoder to its own files (video_de
dmichael (off chromium) 2014/06/04 15:47:29 Do you mean you don't want to inherit SupportsWeak
bbudge 2014/06/04 20:52:46 Delegate now references VideoDecoderProxy (formerl
dmichael (off chromium) 2014/06/04 21:03:07 FYI, base::Callbacks have no restriction on the nu
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareDecoder);
+};
+
+SoftwareDecoder::PendingDecode::PendingDecode(
+ uint32_t decode_id,
+ const scoped_refptr<media::DecoderBuffer>& buffer)
+ : decode_id(decode_id), buffer(buffer) {
+}
+
+SoftwareDecoder::PendingDecode::~PendingDecode() {
+}
+
+SoftwareDecoder::PendingFrame::PendingFrame(uint32_t decode_id,
+ const gfx::Size& size)
+ : decode_id(decode_id),
+ size(size),
+ pixels(size.width() * size.height() * 4) {
+}
+
+SoftwareDecoder::PendingFrame::~PendingFrame() {
+}
+
+SoftwareDecoder::SoftwareDecoder(PepperVideoDecoderHost* host)
+ : host_(host),
+ media_message_loop_(
+ RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy()),
+ context_provider_(
+ RenderThreadImpl::current()->SharedMainThreadContextProvider()),
+ num_pending_decodes_(0),
+ flushing_(false),
+ resetting_(false),
+ main_message_loop_(base::MessageLoopProxy::current()) {
+ DCHECK(host_);
+ DCHECK(main_message_loop_);
+ DCHECK(media_message_loop_);
+ DCHECK(context_provider_);
+}
+
+SoftwareDecoder::~SoftwareDecoder() {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(!host_);
+ // Delete any remaining video frames.
+ while (!pending_frames_.empty()) {
+ delete pending_frames_.front();
+ pending_frames_.pop();
+ }
+ // Delete any remaining textures.
+ TextureIdMap::iterator it = texture_id_map_.begin();
+ for (; it != texture_id_map_.end(); ++it)
+ DeleteTexture(it->second);
+
+ FlushCommandBuffer();
+}
+
+void SoftwareDecoder::Initialize(media::VideoCodecProfile profile) {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(!decoder_);
+ media::VideoCodec codec = media::kUnknownVideoCodec;
+ if (profile <= media::H264PROFILE_MAX)
+ codec = media::kCodecH264;
+ else if (profile <= media::VP8PROFILE_MAX)
+ codec = media::kCodecVP8;
+ DCHECK_NE(codec, media::kUnknownVideoCodec);
+
+ media::VideoDecoderConfig config(
+ codec,
+ profile,
+ media::VideoFrame::YV12,
+ gfx::Size(32, 24), // Small sizes that won't fail.
+ gfx::Rect(32, 24),
+ gfx::Size(32, 24),
+ NULL /* extra_data */,
+ 0 /* extra_data_size */,
+ false /* decryption */);
+
+ decoder_.reset(new media::FFmpegVideoDecoder(media_message_loop_));
+
+ media_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::InitializeOnMediaThread,
+ base::Unretained(this),
+ config));
+}
+
+void SoftwareDecoder::Decode(
+ uint32_t decode_id,
+ const scoped_refptr<media::DecoderBuffer>& buffer) {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(decoder_);
+ DCHECK(!resetting_ && !flushing_);
+
+ num_pending_decodes_++;
+
+ media_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::ReceiveBufferOnMediaThread,
+ base::Unretained(this),
+ decode_id,
+ buffer));
+}
+
+void SoftwareDecoder::AssignTextures(const std::vector<uint32_t>& texture_ids) {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(decoder_);
+ DCHECK(texture_ids.size());
+ DCHECK_EQ(texture_ids.size(), pending_texture_mailboxes_.size());
+ uint32_t num_textures = static_cast<GLuint>(texture_ids.size());
+ std::vector<uint32_t> local_texture_ids(num_textures);
+ gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+ gles2->GenTextures(num_textures, &local_texture_ids.front());
+ for (uint32_t i = 0; i < num_textures; i++) {
+ gles2->ActiveTexture(GL_TEXTURE0);
+ gles2->BindTexture(GL_TEXTURE_2D, local_texture_ids[i]);
+ gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
+ pending_texture_mailboxes_[i].name);
+ // Map the plugin texture id to the local texture id.
+ texture_id_map_.insert(
+ std::make_pair(texture_ids[i], local_texture_ids[i]));
+ }
+ pending_texture_mailboxes_.clear();
+ available_textures_.insert(
+ available_textures_.end(), texture_ids.begin(), texture_ids.end());
+ SendPicturesOnMainThread();
+}
+
+void SoftwareDecoder::RecycleTexture(uint32_t texture_id) {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(decoder_);
+ if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
+ DismissTexture(texture_id);
+ } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
+ available_textures_.push_back(texture_id);
+ SendPicturesOnMainThread();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void SoftwareDecoder::Flush() {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(decoder_);
+ DCHECK(!resetting_ && !flushing_);
+ flushing_ = true;
+}
+
+void SoftwareDecoder::Reset() {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(decoder_);
+ DCHECK(!resetting_ && !flushing_);
+ resetting_ = true;
+ media_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::DoResetOnMediaThread,
+ base::Unretained(this)));
+}
+
+void SoftwareDecoder::Destroy() {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(decoder_);
+ DCHECK(host_);
+ host_ = NULL;
+ media_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::DestroyOnMediaThread,
+ base::Unretained(this)));
+}
+
+void SoftwareDecoder::InitializeOnMediaThread(
+ media::VideoDecoderConfig config) {
+ DCHECK(decoder_);
+ decoder_->Initialize(config,
+ true /* low_delay */,
+ base::Bind(&SoftwareDecoder::PipelineStatusOnMediaThread,
+ base::Unretained(this)));
+}
+
+void SoftwareDecoder::PipelineStatusOnMediaThread(
+ media::PipelineStatus status) {
+ if (!host_)
+ return;
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::PipelineStatusOnMainThread,
+ base::Unretained(this),
+ status));
+}
+
+void SoftwareDecoder::PipelineStatusOnMainThread(media::PipelineStatus status) {
+ if (!host_)
+ return;
+ int32_t result;
+ switch (status) {
+ case media::PIPELINE_OK:
+ result = PP_OK;
+ break;
+ case media::DECODER_ERROR_NOT_SUPPORTED:
+ result = PP_ERROR_NOTSUPPORTED;
+ break;
+ default:
+ result = PP_ERROR_FAILED;
+ break;
+ }
+ host_->OnInitializeComplete(result);
+}
+
+void SoftwareDecoder::ReceiveBufferOnMediaThread(
+ uint32_t decode_id,
+ scoped_refptr<media::DecoderBuffer> buffer) {
+ bool decoder_busy = !pending_decodes_.empty();
+ pending_decodes_.push(PendingDecode(decode_id, buffer));
+ if (!decoder_busy)
+ DecodeOnMediaThread();
+}
+
+void SoftwareDecoder::DecodeOnMediaThread() {
+ DCHECK(!pending_decodes_.empty());
+ PendingDecode& next_decode = pending_decodes_.front();
+ decoder_->Decode(next_decode.buffer,
+ base::Bind(&SoftwareDecoder::ConvertFrameOnMediaThread,
+ base::Unretained(this),
+ next_decode.decode_id));
+ pending_decodes_.pop();
+}
+
+void SoftwareDecoder::ConvertFrameOnMediaThread(
+ uint32_t decode_id,
+ media::VideoDecoder::Status status,
+ const scoped_refptr<media::VideoFrame>& frame) {
+ if (!host_)
+ return;
+ scoped_ptr<PendingFrame> pending_frame(
+ new PendingFrame(decode_id, gfx::Size()));
+ if (frame) {
+ pending_frame->size = frame->coded_size();
+ pending_frame->pixels.resize(frame->coded_size().width() *
+ frame->coded_size().height() * 4);
+ // Convert the decoded frame to ARGB pixels.
+ libyuv::I420ToARGB(frame->data(media::VideoFrame::kYPlane),
+ frame->stride(media::VideoFrame::kYPlane),
+ frame->data(media::VideoFrame::kUPlane),
+ frame->stride(media::VideoFrame::kUPlane),
+ frame->data(media::VideoFrame::kVPlane),
+ frame->stride(media::VideoFrame::kVPlane),
+ &pending_frame->pixels.front(),
+ frame->coded_size().width() * 4,
+ frame->coded_size().width(),
+ frame->coded_size().height());
+ }
+
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::ReceiveFrameOnMainThread,
+ base::Unretained(this),
+ status,
+ base::Passed(&pending_frame)));
+
+ if (!pending_decodes_.empty())
+ DecodeOnMediaThread();
+}
+
+void SoftwareDecoder::ReceiveFrameOnMainThread(
+ media::VideoDecoder::Status status,
+ scoped_ptr<PendingFrame> frame) {
+ DCHECK(RenderThreadImpl::current());
+ if (!host_)
+ return;
+
+ num_pending_decodes_--;
+
+ if (frame->pixels.size()) {
+ if (texture_size_ != frame->size) {
+ // If the size has changed, all current textures must be dismissed. Add
+ // all textures to |textures_to_dismiss_| and dismiss any that aren't in
+ // use by the plugin. We dismiss the rest as they are recycled.
+ for (TextureIdMap::const_iterator it = texture_id_map_.begin();
+ it != texture_id_map_.end();
+ ++it) {
+ textures_to_dismiss_.insert(it->second);
+ }
+ for (std::vector<uint32_t>::const_iterator it =
+ available_textures_.begin();
+ it != available_textures_.end();
+ ++it) {
+ DismissTexture(*it);
+ }
+ available_textures_.clear();
+ FlushCommandBuffer();
+
+ DCHECK(pending_texture_mailboxes_.empty());
+ const uint32_t num_textures = 8;
+ for (uint32_t i = 0; i < num_textures; i++)
+ pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
+
+ host_->RequestTextures(
+ num_textures, frame->size, GL_TEXTURE_2D, pending_texture_mailboxes_);
+ texture_size_ = frame->size;
+ }
+
+ pending_frames_.push(frame.release());
+ SendPicturesOnMainThread();
+ } else {
+ host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
+ }
+
+ switch (status) {
+ case media::VideoDecoder::kOk:
+ case media::VideoDecoder::kAborted:
+ break;
+ case media::VideoDecoder::kNotEnoughData:
igorc 2014/06/03 20:26:32 This is not an error in H264 case when we feed ind
bbudge 2014/06/03 20:59:20 Do you mean that we should silently ignore this er
bbudge 2014/06/04 14:10:12 Changed to not call NotifyError on kNotEnoughData.
+ host_->NotifyError(PP_ERROR_MALFORMED_INPUT);
+ break;
+ case media::VideoDecoder::kDecodeError:
+ case media::VideoDecoder::kDecryptError:
+ host_->NotifyError(PP_ERROR_RESOURCE_FAILED);
+ break;
+ // No default case, to catch unhandled status values.
+ }
+}
+
+void SoftwareDecoder::SendPicturesOnMainThread() {
+ DCHECK(RenderThreadImpl::current());
+ if (!host_)
+ return;
+ while (!pending_frames_.empty() && !available_textures_.empty()) {
+ scoped_ptr<PendingFrame> frame(pending_frames_.front());
+ pending_frames_.pop();
+
+ uint32_t texture_id = available_textures_.back();
+ available_textures_.pop_back();
+
+ uint32_t local_texture_id = texture_id_map_[texture_id];
+ gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+ gles2->ActiveTexture(GL_TEXTURE0);
+ gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
+ gles2->TexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ texture_size_.width(),
+ texture_size_.height(),
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ &frame->pixels.front());
+
+ host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
+ host_->PictureReady(media::Picture(texture_id, frame->decode_id));
+ }
+
+ FlushCommandBuffer();
+
+ if (flushing_ && !num_pending_decodes_ && pending_frames_.empty()) {
+ flushing_ = false;
+ host_->NotifyFlushDone();
+ }
+}
+
+void SoftwareDecoder::DoResetOnMediaThread() {
+ decoder_->Reset(base::Bind(&SoftwareDecoder::ResetCompleteOnMediaThread,
+ base::Unretained(this)));
+}
+
+void SoftwareDecoder::ResetCompleteOnMediaThread() {
+ // Cancel all remaining decodes, and notify the host so it can free the shm
+ // buffers. We'll clear pending frames on the main thread.
+ while (!pending_decodes_.empty()) {
+ PendingDecode& next_decode = pending_decodes_.front();
+ scoped_ptr<PendingFrame> pending_frame(
+ new PendingFrame(next_decode.decode_id, gfx::Size()));
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::ReceiveFrameOnMainThread,
+ base::Unretained(this),
+ media::VideoDecoder::kAborted,
+ base::Passed(&pending_frame)));
+ pending_decodes_.pop();
+ }
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SoftwareDecoder::ResetCompleteOnMainThread,
+ base::Unretained(this)));
+}
+
+void SoftwareDecoder::ResetCompleteOnMainThread() {
+ if (!host_)
+ return;
+ while (!pending_frames_.empty()) {
+ scoped_ptr<PendingFrame> frame(pending_frames_.front());
+ host_->NotifyEndOfBitstreamBuffer(frame->decode_id);
+ pending_frames_.pop();
+ }
+
+ resetting_ = false;
+ host_->NotifyResetDone();
+}
+
+void SoftwareDecoder::DestroyOnMediaThread() {
+ DCHECK(decoder_);
+ decoder_->Stop();
+ // All callbacks have been called on the media thread, and thus all tasks
+ // posted for the main thread. This is our last task for the main thread.
+ main_message_loop_->PostTask(FROM_HERE,
+ base::Bind(&SoftwareDecoder::DestroyOnMainThread,
+ base::Unretained(this)));
+}
+
+void SoftwareDecoder::DestroyOnMainThread() {
+ DCHECK(RenderThreadImpl::current());
+ DCHECK(!host_);
+ delete this;
+}
+
+void SoftwareDecoder::DismissTexture(uint32_t texture_id) {
+ DCHECK(host_);
+ textures_to_dismiss_.erase(texture_id);
+ DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
+ DeleteTexture(texture_id_map_[texture_id]);
+ texture_id_map_.erase(texture_id);
+ host_->DismissPictureBuffer(texture_id);
+}
+
+void SoftwareDecoder::DeleteTexture(uint32_t texture_id) {
+ gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
+ gles2->DeleteTextures(1, &texture_id);
+}
+
+void SoftwareDecoder::FlushCommandBuffer() {
+ DCHECK(RenderThreadImpl::current());
+ context_provider_->ContextGL()->Flush();
+}
+
dmichael (off chromium) 2014/06/03 22:27:46 I think this class should be in its own file.
bbudge 2014/06/04 14:10:12 Done.
PepperVideoDecoderHost::PendingDecode::PendingDecode(
uint32_t shm_id,
const ppapi::host::ReplyMessageContext& reply_context)
@@ -119,9 +656,10 @@ int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
graphics_context.host_resource(), true);
if (enter_graphics.failed())
return PP_ERROR_FAILED;
- graphics3d_ = static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
+ PPB_Graphics3D_Impl* graphics3d =
+ static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
- int command_buffer_route_id = graphics3d_->GetCommandBufferRouteId();
+ int command_buffer_route_id = graphics3d->GetCommandBufferRouteId();
if (!command_buffer_route_id)
return PP_ERROR_FAILED;
@@ -129,7 +667,7 @@ int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
// This is not synchronous, but subsequent IPC messages will be buffered, so
// it is okay to immediately send IPC messages through the returned channel.
- GpuChannelHost* channel = graphics3d_->channel();
+ GpuChannelHost* channel = graphics3d->channel();
DCHECK(channel);
decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
if (decoder_ && decoder_->Initialize(media_profile, this)) {
@@ -138,8 +676,14 @@ int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
}
decoder_.reset();
- // TODO(bbudge) Implement software fallback.
- return PP_ERROR_NOTSUPPORTED;
+ if (!allow_software_fallback)
+ return PP_ERROR_NOTSUPPORTED;
+
+ software_decoder_.reset(new SoftwareDecoder(this));
+ initialize_reply_context_ = context->MakeReplyMessageContext();
+ software_decoder_->Initialize(media_profile);
+
+ return PP_OK_COMPLETIONPENDING;
}
int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
@@ -208,7 +752,7 @@ int32_t PepperVideoDecoderHost::OnHostMsgDecode(
int32_t decode_id) {
if (!initialized_)
return PP_ERROR_FAILED;
- DCHECK(decoder_);
+ DCHECK(decoder_ || software_decoder_);
// |shm_id| is just an index into shm_buffers_. Make sure it's in range.
if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
return PP_ERROR_FAILED;
@@ -226,8 +770,14 @@ int32_t PepperVideoDecoderHost::OnHostMsgDecode(
decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
shm_buffer_busy_[shm_id] = true;
- decoder_->Decode(
- media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
+ base::SharedMemory* shm = shm_buffers_[shm_id];
+ if (decoder_) {
+ decoder_->Decode(media::BitstreamBuffer(decode_id, shm->handle(), size));
+ } else {
+ software_decoder_->Decode(decode_id,
+ media::DecoderBuffer::CopyFrom(
+ static_cast<uint8_t*>(shm->memory()), size));
+ }
return PP_OK_COMPLETIONPENDING;
}
@@ -238,17 +788,21 @@ int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
const std::vector<uint32_t>& texture_ids) {
if (!initialized_)
return PP_ERROR_FAILED;
- DCHECK(decoder_);
-
- std::vector<media::PictureBuffer> picture_buffers;
- for (uint32 i = 0; i < texture_ids.size(); i++) {
- media::PictureBuffer buffer(
- texture_ids[i], // Use the texture_id to identify the buffer.
- gfx::Size(size.width, size.height),
- texture_ids[i]);
- picture_buffers.push_back(buffer);
+ DCHECK(decoder_ || software_decoder_);
+
+ if (decoder_) {
+ std::vector<media::PictureBuffer> picture_buffers;
+ for (uint32 i = 0; i < texture_ids.size(); i++) {
+ media::PictureBuffer buffer(
+ texture_ids[i], // Use the texture_id to identify the buffer.
+ gfx::Size(size.width, size.height),
+ texture_ids[i]);
+ picture_buffers.push_back(buffer);
+ }
+ decoder_->AssignPictureBuffers(picture_buffers);
+ } else {
+ software_decoder_->AssignTextures(texture_ids);
}
- decoder_->AssignPictureBuffers(picture_buffers);
return PP_OK;
}
@@ -257,11 +811,14 @@ int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
uint32_t texture_id) {
if (!initialized_)
return PP_ERROR_FAILED;
- DCHECK(decoder_);
+ DCHECK(decoder_ || software_decoder_);
if (reset_reply_context_.is_valid())
return PP_ERROR_FAILED;
-
- decoder_->ReusePictureBuffer(texture_id);
+ if (decoder_) {
+ decoder_->ReusePictureBuffer(texture_id);
+ } else {
+ software_decoder_->RecycleTexture(texture_id);
+ }
return PP_OK;
}
@@ -270,12 +827,15 @@ int32_t PepperVideoDecoderHost::OnHostMsgFlush(
ppapi::host::HostMessageContext* context) {
if (!initialized_)
return PP_ERROR_FAILED;
- DCHECK(decoder_);
+ DCHECK(decoder_ || software_decoder_);
if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
return PP_ERROR_FAILED;
flush_reply_context_ = context->MakeReplyMessageContext();
- decoder_->Flush();
+ if (decoder_)
+ decoder_->Flush();
+ else
+ software_decoder_->Flush();
return PP_OK_COMPLETIONPENDING;
}
@@ -284,12 +844,15 @@ int32_t PepperVideoDecoderHost::OnHostMsgReset(
ppapi::host::HostMessageContext* context) {
if (!initialized_)
return PP_ERROR_FAILED;
- DCHECK(decoder_);
+ DCHECK(decoder_ || software_decoder_);
if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
return PP_ERROR_FAILED;
reset_reply_context_ = context->MakeReplyMessageContext();
- decoder_->Reset();
+ if (decoder_)
+ decoder_->Reset();
+ else
+ software_decoder_->Reset();
return PP_OK_COMPLETIONPENDING;
}
@@ -298,17 +861,13 @@ void PepperVideoDecoderHost::ProvidePictureBuffers(
uint32 requested_num_of_buffers,
const gfx::Size& dimensions,
uint32 texture_target) {
- DCHECK(RenderThreadImpl::current());
- host()->SendUnsolicitedReply(
- pp_resource(),
- PpapiPluginMsg_VideoDecoder_RequestTextures(
- requested_num_of_buffers,
- PP_MakeSize(dimensions.width(), dimensions.height()),
- texture_target));
+ RequestTextures(requested_num_of_buffers,
+ dimensions,
+ texture_target,
+ std::vector<gpu::Mailbox>());
}
void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
- DCHECK(RenderThreadImpl::current());
host()->SendUnsolicitedReply(
pp_resource(),
PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
@@ -316,15 +875,40 @@ void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
}
void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
- DCHECK(RenderThreadImpl::current());
host()->SendUnsolicitedReply(
pp_resource(),
PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
}
+void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
+ int32 bitstream_buffer_id) {
+ PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
+ if (it == pending_decodes_.end()) {
+ NOTREACHED();
+ return;
+ }
+ const PendingDecode& pending_decode = it->second;
+ host()->SendReply(
+ pending_decode.reply_context,
+ PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
+ shm_buffer_busy_[pending_decode.shm_id] = false;
+ pending_decodes_.erase(it);
+}
+
+void PepperVideoDecoderHost::NotifyFlushDone() {
+ host()->SendReply(flush_reply_context_,
+ PpapiPluginMsg_VideoDecoder_FlushReply());
+ flush_reply_context_ = ppapi::host::ReplyMessageContext();
+}
+
+void PepperVideoDecoderHost::NotifyResetDone() {
+ host()->SendReply(reset_reply_context_,
+ PpapiPluginMsg_VideoDecoder_ResetReply());
+ reset_reply_context_ = ppapi::host::ReplyMessageContext();
+}
+
void PepperVideoDecoderHost::NotifyError(
media::VideoDecodeAccelerator::Error error) {
- DCHECK(RenderThreadImpl::current());
int32_t pp_error = PP_ERROR_FAILED;
switch (error) {
case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
@@ -338,38 +922,45 @@ void PepperVideoDecoderHost::NotifyError(
break;
// No default case, to catch unhandled enum values.
}
- host()->SendUnsolicitedReply(
- pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
+ NotifyError(pp_error);
}
-void PepperVideoDecoderHost::NotifyResetDone() {
- DCHECK(RenderThreadImpl::current());
- host()->SendReply(reset_reply_context_,
- PpapiPluginMsg_VideoDecoder_ResetReply());
- reset_reply_context_ = ppapi::host::ReplyMessageContext();
+void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
+ if (!initialized_) {
+ initialized_ = true;
+ initialize_reply_context_.params.set_result(result);
+ host()->SendReply(initialize_reply_context_,
+ PpapiPluginMsg_VideoDecoder_InitializeReply());
+ }
}
-void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
- int32 bitstream_buffer_id) {
+void PepperVideoDecoderHost::RequestTextures(
+ uint32 requested_num_of_buffers,
+ const gfx::Size& dimensions,
+ uint32 texture_target,
+ const std::vector<gpu::Mailbox>& mailboxes) {
DCHECK(RenderThreadImpl::current());
- PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
- if (it == pending_decodes_.end()) {
- NOTREACHED();
- return;
- }
- const PendingDecode& pending_decode = it->second;
- host()->SendReply(
- pending_decode.reply_context,
- PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
- shm_buffer_busy_[pending_decode.shm_id] = false;
- pending_decodes_.erase(it);
+ host()->SendUnsolicitedReply(
+ pp_resource(),
+ PpapiPluginMsg_VideoDecoder_RequestTextures(
+ requested_num_of_buffers,
+ PP_MakeSize(dimensions.width(), dimensions.height()),
+ texture_target,
+ mailboxes));
}
-void PepperVideoDecoderHost::NotifyFlushDone() {
- DCHECK(RenderThreadImpl::current());
- host()->SendReply(flush_reply_context_,
- PpapiPluginMsg_VideoDecoder_FlushReply());
- flush_reply_context_ = ppapi::host::ReplyMessageContext();
+void PepperVideoDecoderHost::NotifyError(int32_t pp_error) {
+ host()->SendUnsolicitedReply(
+ pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
}
} // namespace content
+
+namespace base {
+
+void DefaultDeleter<content::SoftwareDecoder>::operator()(
+ void* software_decoder) const {
+ static_cast<content::SoftwareDecoder*>(software_decoder)->Destroy();
+}
+
+} // namespace base

Powered by Google App Engine
This is Rietveld 408576698