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 |