| 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..cc18887a0084b319c5409162b2c1b010a5b6bc65 100644
|
| --- a/content/renderer/pepper/pepper_video_decoder_host.cc
|
| +++ b/content/renderer/pepper/pepper_video_decoder_host.cc
|
| @@ -2,9 +2,14 @@
|
| // 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"
|
| +#include "base/memory/ref_counted.h"
|
| #include "base/memory/shared_memory.h"
|
| #include "content/common/gpu/client/gpu_channel_host.h"
|
| #include "content/public/renderer/render_thread.h"
|
| @@ -12,6 +17,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 +30,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;
|
| @@ -57,14 +67,503 @@ media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
|
| return media::H264PROFILE_MULTIVIEWHIGH;
|
| case PP_VIDEOPROFILE_VP8MAIN:
|
| return media::VP8PROFILE_MAIN;
|
| - // No default case, to catch unhandled PP_VideoProfile values.
|
| + // No default case, to catch unhandled PP_VideoProfile values.
|
| }
|
|
|
| return media::VIDEO_CODEC_PROFILE_UNKNOWN;
|
| }
|
|
|
| +int32_t MediaPipelineStatusToPepper(media::PipelineStatus status) {
|
| + switch (status) {
|
| + case media::PIPELINE_OK:
|
| + return PP_OK;
|
| + case media::DECODER_ERROR_NOT_SUPPORTED:
|
| + return PP_ERROR_NOTSUPPORTED;
|
| + default:
|
| + return PP_ERROR_FAILED;
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| +// TODO
|
| +// - variable # of shm buffers, depending on software or hardware.
|
| +
|
| +// 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);
|
| + ~SoftwareDecoder();
|
| +
|
| + void Initialize(media::VideoCodecProfile profile);
|
| + void Decode(uint32_t decode_id,
|
| + const scoped_refptr<media::DecoderBuffer>& buffer);
|
| + void AssignTextures(const std::vector<uint32_t>& plugin_texture_ids);
|
| + void RecycleTexture(uint32_t plugin_texture_id);
|
| + void Flush();
|
| + void Reset();
|
| + void Destroy();
|
| +
|
| + 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 DeleteTextures();
|
| + 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 std::map<uint32_t, uint32_t> SharedTextureMap;
|
| + SharedTextureMap shared_textures_;
|
| + // Track available textures using the plugin's ids.
|
| + std::vector<uint32_t> plugin_texture_ids_;
|
| + // 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_;
|
| +
|
| + // These members are accessed only on the media thread.
|
| +
|
| + scoped_ptr<media::VideoDecoder> decoder_;
|
| + scoped_refptr<base::MessageLoopProxy> main_message_loop_;
|
| + // Queue of pending decodes. The front element is the buffer currently in
|
| + // use by the software decoder.
|
| + typedef std::queue<PendingDecode> PendingDecodeQueue;
|
| + PendingDecodeQueue pending_decodes_;
|
| +};
|
| +
|
| +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());
|
| + while (!pending_frames_.empty()) {
|
| + delete pending_frames_.front();
|
| + pending_frames_.pop();
|
| + }
|
| + DeleteTextures();
|
| + 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>& plugin_texture_ids) {
|
| + DCHECK(RenderThreadImpl::current());
|
| + DCHECK(decoder_);
|
| + DCHECK(plugin_texture_ids.size());
|
| + DCHECK_EQ(plugin_texture_ids.size(), pending_texture_mailboxes_.size());
|
| + uint32_t num_textures = static_cast<GLuint>(plugin_texture_ids.size());
|
| + std::vector<uint32_t> local_ids(num_textures);
|
| + gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
|
| + gles2->GenTextures(num_textures, &local_ids.front());
|
| + for (uint32_t i = 0; i < num_textures; i++) {
|
| + gles2->ActiveTexture(GL_TEXTURE0);
|
| + gles2->BindTexture(GL_TEXTURE_2D, local_ids[i]);
|
| + gles2->ConsumeTextureCHROMIUM(GL_TEXTURE_2D,
|
| + pending_texture_mailboxes_[i].name);
|
| + // Map the plugin texture id to the local texture id.
|
| + shared_textures_.insert(
|
| + std::make_pair(plugin_texture_ids[i], local_ids[i]));
|
| + }
|
| + pending_texture_mailboxes_.clear();
|
| + plugin_texture_ids_ = plugin_texture_ids;
|
| + SendPicturesOnMainThread();
|
| +}
|
| +
|
| +void SoftwareDecoder::RecycleTexture(uint32_t plugin_texture_id) {
|
| + DCHECK(RenderThreadImpl::current());
|
| + DCHECK(decoder_);
|
| + // Make sure it is still a valid texture.
|
| + SharedTextureMap::iterator it = shared_textures_.find(plugin_texture_id);
|
| + if (it != shared_textures_.end()) {
|
| + plugin_texture_ids_.push_back(plugin_texture_id);
|
| + }
|
| + SendPicturesOnMainThread();
|
| +}
|
| +
|
| +void SoftwareDecoder::Flush() {
|
| + DCHECK(RenderThreadImpl::current());
|
| + DCHECK(decoder_);
|
| + DCHECK(!resetting_ && !flushing_);
|
| + flushing_ = true;
|
| +}
|
| +
|
| +void SoftwareDecoder::Reset() {
|
| + DCHECK(RenderThreadImpl::current());
|
| + DCHECK(decoder_);
|
| + DCHECK(!resetting_ && !flushing_);
|
| +printf("SoftwareDecoder::Reset\n");
|
| + 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;
|
| + host_->OnInitializeComplete(status);
|
| +}
|
| +
|
| +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, dismiss all textures. TODO what if some are
|
| + // in use? Dismiss free ones, mark busy ones, and wait for recycle?
|
| + for (size_t i = 0; i < plugin_texture_ids_.size(); i++)
|
| + host_->DismissPictureBuffer(plugin_texture_ids_[i]);
|
| + plugin_texture_ids_.clear();
|
| +
|
| + DeleteTextures();
|
| +
|
| + 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);
|
| + }
|
| +}
|
| +
|
| +void SoftwareDecoder::SendPicturesOnMainThread() {
|
| + DCHECK(RenderThreadImpl::current());
|
| + if (!host_)
|
| + return;
|
| + while (!pending_frames_.empty() && !plugin_texture_ids_.empty()) {
|
| + scoped_ptr<PendingFrame> frame(pending_frames_.front());
|
| + pending_frames_.pop();
|
| +
|
| + uint32_t plugin_texture_id = plugin_texture_ids_.back();
|
| + plugin_texture_ids_.pop_back();
|
| +
|
| + uint32_t local_id = shared_textures_[plugin_texture_id];
|
| + gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
|
| + gles2->ActiveTexture(GL_TEXTURE0);
|
| + gles2->BindTexture(GL_TEXTURE_2D, local_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(plugin_texture_id, frame->decode_id));
|
| + }
|
| +
|
| + FlushCommandBuffer();
|
| +
|
| + if (flushing_ && !num_pending_decodes_ && pending_frames_.empty()) {
|
| + flushing_ = false;
|
| + host_->NotifyFlushDone();
|
| + }
|
| +}
|
| +
|
| +void SoftwareDecoder::DoResetOnMediaThread() {
|
| +printf("SoftwareDecoder::DoResetOnMediaThread\n");
|
| + decoder_->Reset(base::Bind(&SoftwareDecoder::ResetCompleteOnMediaThread,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void SoftwareDecoder::ResetCompleteOnMediaThread() {
|
| +printf("SoftwareDecoder::ResetCompleteOnMediaThread\n");
|
| + // 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;
|
| +printf("SoftwareDecoder::ResetCompleteOnMainThread\n");
|
| +
|
| + 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::DeleteTextures() {
|
| + gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
|
| + SharedTextureMap::iterator it = shared_textures_.begin();
|
| + for (; it != shared_textures_.end(); ++it)
|
| + gles2->DeleteTextures(1, &it->second);
|
| + shared_textures_.clear();
|
| +}
|
| +
|
| +void SoftwareDecoder::FlushCommandBuffer() {
|
| + DCHECK(RenderThreadImpl::current());
|
| + context_provider_->ContextGL()->Flush();
|
| +}
|
| +
|
| PepperVideoDecoderHost::PendingDecode::PendingDecode(
|
| uint32_t shm_id,
|
| const ppapi::host::ReplyMessageContext& reply_context)
|
| @@ -83,26 +582,29 @@ PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost* host,
|
| }
|
|
|
| PepperVideoDecoderHost::~PepperVideoDecoderHost() {
|
| + // TODO DefaultDeleter.
|
| + if (software_decoder_)
|
| + software_decoder_.release()->Destroy();
|
| }
|
|
|
| int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
|
| const IPC::Message& msg,
|
| ppapi::host::HostMessageContext* context) {
|
| PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost, msg)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize,
|
| - OnHostMsgInitialize)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
|
| - OnHostMsgGetShm)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
|
| - OnHostMsgDecode)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures,
|
| - OnHostMsgAssignTextures)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture,
|
| - OnHostMsgRecyclePicture)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush,
|
| - OnHostMsgFlush)
|
| - PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
|
| - OnHostMsgReset)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize,
|
| + OnHostMsgInitialize)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
|
| + OnHostMsgGetShm)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
|
| + OnHostMsgDecode)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures,
|
| + OnHostMsgAssignTextures)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture,
|
| + OnHostMsgRecyclePicture)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush,
|
| + OnHostMsgFlush)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
|
| + OnHostMsgReset)
|
| PPAPI_END_MESSAGE_MAP()
|
| return PP_ERROR_FAILED;
|
| }
|
| @@ -119,9 +621,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 +632,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 +641,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 +717,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 +735,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 +753,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 +776,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 +792,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 +809,16 @@ 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;
|
| +printf("PepperVideoDecoderHost::OnHostMsgReset\n");
|
|
|
| reset_reply_context_ = context->MakeReplyMessageContext();
|
| - decoder_->Reset();
|
| + if (decoder_)
|
| + decoder_->Reset();
|
| + else
|
| + software_decoder_->Reset();
|
|
|
| return PP_OK_COMPLETIONPENDING;
|
| }
|
| @@ -298,13 +827,10 @@ 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) {
|
| @@ -343,6 +869,7 @@ void PepperVideoDecoderHost::NotifyError(
|
| }
|
|
|
| void PepperVideoDecoderHost::NotifyResetDone() {
|
| + printf("PepperVideoDecoderHost::NotifyResetDone:pending=%i\n", (int)pending_decodes_.size());
|
| DCHECK(RenderThreadImpl::current());
|
| host()->SendReply(reset_reply_context_,
|
| PpapiPluginMsg_VideoDecoder_ResetReply());
|
| @@ -372,4 +899,30 @@ void PepperVideoDecoderHost::NotifyFlushDone() {
|
| flush_reply_context_ = ppapi::host::ReplyMessageContext();
|
| }
|
|
|
| +void PepperVideoDecoderHost::OnInitializeComplete(
|
| + media::PipelineStatus status) {
|
| + if (!initialized_) {
|
| + initialized_ = true;
|
| + int32_t result = MediaPipelineStatusToPepper(status);
|
| + initialize_reply_context_.params.set_result(result);
|
| + host()->SendReply(initialize_reply_context_,
|
| + PpapiPluginMsg_VideoDecoder_InitializeReply());
|
| + }
|
| +}
|
| +
|
| +void PepperVideoDecoderHost::RequestTextures(
|
| + uint32 requested_num_of_buffers,
|
| + const gfx::Size& dimensions,
|
| + uint32 texture_target,
|
| + const std::vector<gpu::Mailbox>& mailboxes) {
|
| + DCHECK(RenderThreadImpl::current());
|
| + host()->SendUnsolicitedReply(
|
| + pp_resource(),
|
| + PpapiPluginMsg_VideoDecoder_RequestTextures(
|
| + requested_num_of_buffers,
|
| + PP_MakeSize(dimensions.width(), dimensions.height()),
|
| + texture_target,
|
| + mailboxes));
|
| +}
|
| +
|
| } // namespace content
|
|
|