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

Unified Diff: ppapi/proxy/video_encoder_resource.cc

Issue 905023005: Pepper: PPB_VideoEncoder implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 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: ppapi/proxy/video_encoder_resource.cc
diff --git a/ppapi/proxy/video_encoder_resource.cc b/ppapi/proxy/video_encoder_resource.cc
index 5d7913d71c985b6204d1e32af23f9792e7585116..1bfaddcc489fd49a118c1e6f0b9de211b5fd4173 100644
--- a/ppapi/proxy/video_encoder_resource.cc
+++ b/ppapi/proxy/video_encoder_resource.cc
@@ -2,19 +2,92 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/shared_memory.h"
+#include "ppapi/c/pp_array_output.h"
+#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/video_encoder_resource.h"
+#include "ppapi/proxy/video_frame_resource.h"
+#include "ppapi/shared_impl/media_stream_buffer.h"
+#include "ppapi/shared_impl/media_stream_buffer_manager.h"
+#include "ppapi/thunk/enter.h"
+using ppapi::proxy::SerializedHandle;
+using ppapi::thunk::EnterResourceNoLock;
using ppapi::thunk::PPB_VideoEncoder_API;
namespace ppapi {
namespace proxy {
+namespace {
+
+class BufferManagerDelegate : public MediaStreamBufferManager::Delegate {
+ public:
+ BufferManagerDelegate() {}
+ ~BufferManagerDelegate() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BufferManagerDelegate);
+};
bbudge 2015/02/10 01:31:08 Same comment as on PepperVideoEncoderHost.
llandwerlin-old 2015/02/10 14:28:13 Done.
+
+void RunCallback(scoped_refptr<TrackedCallback>* callback, int32_t error) {
+ if (!TrackedCallback::IsPending(*callback))
+ return;
+
+ scoped_refptr<TrackedCallback> temp;
+ callback->swap(temp);
+ temp->Run(error);
+}
+
+} // namespace
+
+VideoEncoderResource::ShmBuffer::ShmBuffer(base::SharedMemoryHandle handle,
+ uint32_t id,
+ uint32_t size)
+ : shm(new base::SharedMemory(handle, true)), id(id), size(size) {
+ if (shm)
bbudge 2015/02/10 01:31:08 You can assume shm is non-null here.
llandwerlin-old 2015/02/10 14:28:13 Done.
+ shm->Map(size);
+}
+
+VideoEncoderResource::ShmBuffer::~ShmBuffer() {
+}
+
+VideoEncoderResource::BitstreamBuffer::BitstreamBuffer(uint32_t id,
+ uint32_t size,
+ bool key_frame)
+ : id(id), size(size), key_frame(key_frame) {
+}
+
+VideoEncoderResource::BitstreamBuffer::BitstreamBuffer(
+ const BitstreamBuffer& other)
+ : id(other.id), size(other.size), key_frame(other.key_frame) {
+}
+
+VideoEncoderResource::BitstreamBuffer::~BitstreamBuffer() {
+}
+
VideoEncoderResource::VideoEncoderResource(Connection connection,
PP_Instance instance)
- : PluginResource(connection, instance) {
+ : PluginResource(connection, instance),
+ initialized_(false),
+ // Set |encoder_last_error_| to PP_OK after successful initialization.
+ // This makes error checking a little more concise, since we can check
+ // that the encoder has been initialized and hasn't returned an error by
+ // just testing |encoder_last_error_|.
+ encoder_last_error_(PP_ERROR_FAILED),
+ encoder_frame_count_(0),
bbudge 2015/02/10 01:31:08 initialize encoder_coded_size_.
llandwerlin-old 2015/02/10 14:28:13 Done.
+ waiting_for_video_frames_(false),
+ buffer_manager_delegate_(new BufferManagerDelegate()),
+ buffer_manager_(
+ new MediaStreamBufferManager(buffer_manager_delegate_.get())) {
+ SendCreate(RENDERER, PpapiHostMsg_VideoEncoder_Create());
}
VideoEncoderResource::~VideoEncoderResource() {
+ encoder_last_error_ = PP_ERROR_ABORTED;
bbudge 2015/02/10 01:31:08 I think it's clearer if encoder_last_error_ repres
llandwerlin-old 2015/02/10 14:28:12 Done.
+ RunCallback(&initialize_callback_, encoder_last_error_);
+ RunCallback(&get_bitstreamer_buffer_callback_, encoder_last_error_);
+ NotifyGetVideoFrameCallbacksError(encoder_last_error_);
+ ReleaseFrames();
bbudge 2015/02/10 01:31:07 I think Close() should be called somewhere in here
llandwerlin-old 2015/02/10 14:28:12 Done.
}
PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() {
@@ -24,7 +97,25 @@ PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() {
int32_t VideoEncoderResource::GetSupportedProfiles(
const PP_ArrayOutput& output,
const scoped_refptr<TrackedCallback>& callback) {
- return PP_ERROR_FAILED;
+ // TODO(llandwerlin): should we prevent concurrent calls?
bbudge 2015/02/10 01:31:08 The IDL leaves it unspecified but I don't think co
llandwerlin-old 2015/02/10 14:28:13 Acknowledged.
+ Call<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply>(
+ RENDERER, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(),
+ base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply,
+ this, output, callback));
+ return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t VideoEncoderResource::GetFramesRequired() {
+ if (encoder_last_error_)
+ return encoder_last_error_;
+ return encoder_frame_count_;
+}
+
+int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) {
+ if (encoder_last_error_)
+ return encoder_last_error_;
+ *size = encoder_coded_size_;
+ return PP_OK;
}
int32_t VideoEncoderResource::Initialize(
@@ -34,45 +125,298 @@ int32_t VideoEncoderResource::Initialize(
uint32_t initial_bitrate,
PP_HardwareAcceleration acceleration,
const scoped_refptr<TrackedCallback>& callback) {
- return PP_ERROR_FAILED;
-}
-
-int32_t VideoEncoderResource::GetFramesRequired() {
- return PP_ERROR_FAILED;
-}
+ if (initialized_)
+ return PP_ERROR_FAILED;
+ if (TrackedCallback::IsPending(initialize_callback_))
+ return PP_ERROR_INPROGRESS;
-int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) {
- return PP_ERROR_FAILED;
+ initialize_callback_ = callback;
+ Call<PpapiPluginMsg_VideoEncoder_InitializeReply>(
+ RENDERER, PpapiHostMsg_VideoEncoder_Initialize(
+ input_format, *input_visible_size, output_profile,
+ initial_bitrate, acceleration),
+ base::Bind(&VideoEncoderResource::OnPluginMsgInitializeReply, this));
+ return PP_OK_COMPLETIONPENDING;
}
int32_t VideoEncoderResource::GetVideoFrame(
PP_Resource* video_frame,
const scoped_refptr<TrackedCallback>& callback) {
- return PP_ERROR_FAILED;
+ if (encoder_last_error_)
+ return encoder_last_error_;
+
+ get_video_frame_cbs_.push_back(std::make_pair(video_frame, callback));
+
+ // Lazily ask for video frames buffers.
bbudge 2015/02/10 01:31:08 The terminology here is a little confusing. You're
llandwerlin-old 2015/02/10 14:28:13 Done.
+ if (buffer_manager_->number_of_buffers() < 1) {
+ // Check that we haven't ask for the buffers already.
+ if (!waiting_for_video_frames_) {
+ waiting_for_video_frames_ = true;
+ Call<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply>(
+ RENDERER, PpapiHostMsg_VideoEncoder_GetVideoFrames(),
+ base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply,
+ this));
+ }
+ return PP_OK_COMPLETIONPENDING;
+ }
+
+ NotifyGetVideoFrameCallbacks();
+
+ return PP_OK_COMPLETIONPENDING;
}
int32_t VideoEncoderResource::Encode(
PP_Resource video_frame,
PP_Bool force_keyframe,
const scoped_refptr<TrackedCallback>& callback) {
- return PP_ERROR_FAILED;
+ if (encoder_last_error_)
+ return encoder_last_error_;
+
+ VideoFrameMap::iterator it = video_frames_.find(video_frame);
+ if (it == video_frames_.end())
+ // TODO(llandwerlin): deal MediaStreamVideoTrack's video frames.
+ return PP_ERROR_BADRESOURCE;
+
+ scoped_refptr<VideoFrameResource> frame_resource = it->second;
+
+ Call<PpapiPluginMsg_VideoEncoder_EncodeReply>(
+ RENDERER,
+ PpapiHostMsg_VideoEncoder_Encode(frame_resource->GetBufferIndex(),
+ PP_ToBool(force_keyframe)),
+ base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this,
+ callback));
+
+ return PP_OK_COMPLETIONPENDING;
}
int32_t VideoEncoderResource::GetBitstreamBuffer(
- PP_BitstreamBuffer* picture,
+ PP_BitstreamBuffer* bitstream_buffer,
const scoped_refptr<TrackedCallback>& callback) {
- return PP_ERROR_FAILED;
+ if (encoder_last_error_)
+ return encoder_last_error_;
+ if (TrackedCallback::IsPending(get_bitstreamer_buffer_callback_))
+ return PP_ERROR_INPROGRESS;
+
+ if (available_bitstream_buffers_.empty()) {
+ get_bitstreamer_buffer_callback_ = callback;
+ get_bitstreamer_buffer_data_ = bitstream_buffer;
+ return PP_OK_COMPLETIONPENDING;
+ }
+
+ BitstreamBuffer buffer(available_bitstream_buffers_.front());
+
+ available_bitstream_buffers_.pop_front();
+ WriteBitstreamerBuffer(bitstream_buffer, buffer);
+
+ return PP_OK;
}
void VideoEncoderResource::RecycleBitstreamBuffer(
- const PP_BitstreamBuffer* picture) {
+ const PP_BitstreamBuffer* bitstream_buffer) {
+ if (encoder_last_error_)
+ return;
+ BitstreamBufferMap::const_iterator iter =
+ bitstream_buffers_map_.find(bitstream_buffer->buffer);
+ if (iter != bitstream_buffers_map_.end())
+ Post(RENDERER,
+ PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer(iter->second));
}
void VideoEncoderResource::RequestEncodingParametersChange(uint32_t bitrate,
uint32_t framerate) {
+ if (encoder_last_error_)
+ return;
+ Post(RENDERER, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange(
+ bitrate, framerate));
}
void VideoEncoderResource::Close() {
+ if (encoder_last_error_)
+ return;
+ // Synchronously call into the host to make sure all in flight
+ // Encode request are properly cancelled. This will also trigger the
+ // NotifyError callback before SyncCall() returns.
+ SyncCall<PpapiPluginMsg_VideoEncoder_CloseReply>(
+ RENDERER, PpapiHostMsg_VideoEncoder_Close());
+}
+
+void VideoEncoderResource::OnReplyReceived(
+ const ResourceMessageReplyParams& params,
+ const IPC::Message& msg) {
+ PPAPI_BEGIN_MESSAGE_MAP(VideoEncoderResource, msg)
+ PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
+ PpapiPluginMsg_VideoEncoder_BitstreamBufferReady,
+ OnPluginMsgBitstreamBufferReady)
+ PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoEncoder_NotifyError,
+ OnPluginMsgNotifyError)
+ PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
+ PluginResource::OnReplyReceived(params, msg))
+ PPAPI_END_MESSAGE_MAP()
+}
+
+void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply(
+ const PP_ArrayOutput& output,
+ const scoped_refptr<TrackedCallback>& callback,
+ const ResourceMessageReplyParams& params,
+ const std::vector<PP_VideoProfileDescription>& profiles) {
+ void* ptr = output.GetDataBuffer(output.user_data, profiles.size(),
+ sizeof(PP_VideoProfileDescription));
+
+ if (!ptr) {
+ callback->Run(PP_ERROR_BADARGUMENT);
bbudge 2015/02/10 01:31:07 The plugin is allowed to return null, so I think P
llandwerlin-old 2015/02/10 14:28:13 Done.
+ return;
+ }
+
+ memcpy(ptr, &profiles[0],
bbudge 2015/02/10 01:31:08 profiles could be empty in theory, in which case &
llandwerlin-old 2015/02/10 14:28:12 Thanks for spotting this. Done.
+ profiles.size() * sizeof(PP_VideoProfileDescription));
+ callback->Run(PP_OK);
+}
+
+void VideoEncoderResource::OnPluginMsgInitializeReply(
+ const ResourceMessageReplyParams& params,
+ uint32_t buffer_count,
+ uint32_t buffer_length,
+ uint32_t input_frame_count,
+ const PP_Size& input_coded_size) {
+ encoder_last_error_ = params.result();
+ if (encoder_last_error_) {
+ RunCallback(&initialize_callback_, encoder_last_error_);
+ return;
+ }
+
+ std::vector<base::SharedMemoryHandle> buffer_handles;
bbudge 2015/02/10 01:31:08 shm_handles to match shm_buffers_?
llandwerlin-old 2015/02/10 14:28:12 Done.
+ params.TakeAllSharedMemoryHandles(&buffer_handles);
+
+ CHECK(buffer_handles.size() >= buffer_count);
bbudge 2015/02/10 01:31:08 This implies that buffer_handles is the source of
llandwerlin-old 2015/02/10 14:28:12 Make sense. Done.
+
+ bitstream_buffers_.clear();
+ bitstream_buffers_map_.clear();
bbudge 2015/02/10 01:31:08 I don't see how these two collections could be non
llandwerlin-old 2015/02/10 14:28:13 I was thinking that maybe after the encoder is clo
bbudge 2015/02/10 22:47:36 In Pepper the convention is that calling Close is
+ for (uint32_t i = 0; i < buffer_count; ++i) {
+ ShmBuffer* buffer = new ShmBuffer(buffer_handles[i], i, buffer_length);
+ bitstream_buffers_.push_back(buffer);
+ bitstream_buffers_map_.insert(
+ std::make_pair(buffer->shm->memory(), buffer->id));
+ }
+
+ encoder_frame_count_ = input_frame_count;
+ encoder_coded_size_ = input_coded_size;
bbudge 2015/02/10 01:31:08 Looking at this, I think 'input_frame_count_' and
llandwerlin-old 2015/02/10 14:28:13 Done.
+ initialized_ = true;
+ RunCallback(&initialize_callback_, encoder_last_error_);
+}
+
+void VideoEncoderResource::OnPluginMsgGetVideoFramesReply(
+ const ResourceMessageReplyParams& params,
+ uint32_t frame_count,
+ uint32_t frame_length,
+ const PP_Size& frame_size) {
+ int32_t error = params.result();
+ if (error) {
+ NotifyError(error);
+ return;
+ }
+
+ std::vector<base::SharedMemoryHandle> buffer_handles;
+ params.TakeAllSharedMemoryHandles(&buffer_handles);
+ CHECK(buffer_handles.size() == 1);
+
+ if (!buffer_manager_->SetBuffers(
+ frame_count, frame_length,
+ make_scoped_ptr(new base::SharedMemory(buffer_handles[0], false)),
+ true)) {
+ NotifyError(PP_ERROR_FAILED);
+ return;
+ }
+
+ waiting_for_video_frames_ = false;
+
+ NotifyGetVideoFrameCallbacks();
+}
+
+void VideoEncoderResource::OnPluginMsgEncodeReply(
+ const scoped_refptr<TrackedCallback>& callback,
+ const ResourceMessageReplyParams& params,
+ uint32_t frame_id) {
+ encoder_last_error_ = params.result();
+ callback->Run(encoder_last_error_);
+ buffer_manager_->EnqueueBuffer(frame_id);
+ NotifyGetVideoFrameCallbacks();
+}
+
+void VideoEncoderResource::OnPluginMsgBitstreamBufferReady(
+ const ResourceMessageReplyParams& params,
+ uint32_t buffer_id,
+ uint32_t buffer_size,
+ bool key_frame) {
+ available_bitstream_buffers_.push_back(
+ BitstreamBuffer(buffer_id, buffer_size, PP_FromBool(key_frame)));
+
+ if (TrackedCallback::IsPending(get_bitstreamer_buffer_callback_)) {
+ BitstreamBuffer buffer(available_bitstream_buffers_.front());
+ available_bitstream_buffers_.pop_front();
+ WriteBitstreamerBuffer(get_bitstreamer_buffer_data_, buffer);
+ get_bitstreamer_buffer_data_ = nullptr;
+
+ scoped_refptr<TrackedCallback> callback;
+ get_bitstreamer_buffer_callback_.swap(callback);
+ callback->Run(PP_OK);
+ }
+}
+
+void VideoEncoderResource::OnPluginMsgNotifyError(
+ const ResourceMessageReplyParams& params,
+ int32_t error) {
+ NotifyError(error);
+}
+
+void VideoEncoderResource::NotifyError(int32_t error) {
+ encoder_last_error_ = error;
+ RunCallback(&initialize_callback_, error);
+ RunCallback(&get_bitstreamer_buffer_callback_, error);
+ NotifyGetVideoFrameCallbacksError(error);
+}
+
+void VideoEncoderResource::NotifyGetVideoFrameCallbacks() {
+ int32_t frame_id;
+
+ while (!get_video_frame_cbs_.empty() &&
+ (frame_id = buffer_manager_->DequeueBuffer()) >= 0) {
+ std::pair<PP_Resource*, scoped_refptr<TrackedCallback> > cb =
+ get_video_frame_cbs_.front();
+ get_video_frame_cbs_.pop_front();
+
+ scoped_refptr<VideoFrameResource> resource = new VideoFrameResource(
+ pp_instance(), frame_id, buffer_manager_->GetBufferPointer(frame_id));
+ video_frames_.insert(
+ VideoFrameMap::value_type(resource->pp_resource(), resource));
+
+ *cb.first = resource->GetReference();
+ cb.second->Run(PP_OK);
+ }
+}
+
+void VideoEncoderResource::NotifyGetVideoFrameCallbacksError(int32_t error) {
+ while (!get_video_frame_cbs_.empty()) {
+ std::pair<PP_Resource*, scoped_refptr<TrackedCallback> > cb =
+ get_video_frame_cbs_.front();
+ get_video_frame_cbs_.pop_front();
+ cb.second->Run(error);
+ }
+}
+
+void VideoEncoderResource::WriteBitstreamerBuffer(
+ PP_BitstreamBuffer* bitstream_buffer,
+ const BitstreamBuffer& buffer) {
+ bitstream_buffer->size = buffer.size;
+ bitstream_buffer->buffer = bitstream_buffers_[buffer.id]->shm->memory();
+ bitstream_buffer->key_frame = PP_FromBool(buffer.key_frame);
+}
+
+void VideoEncoderResource::ReleaseFrames() {
+ for (VideoFrameMap::iterator it = video_frames_.begin();
+ it != video_frames_.end(); ++it) {
+ it->second->Invalidate();
+ it->second = nullptr;
+ }
}
} // namespace proxy

Powered by Google App Engine
This is Rietveld 408576698