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

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: Deal with Encode() callbacks in the proxy 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..521eecb5b60251ae6707a5f0abc5996c4c7b970f 100644
--- a/ppapi/proxy/video_encoder_resource.cc
+++ b/ppapi/proxy/video_encoder_resource.cc
@@ -2,19 +2,79 @@
// 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 {
+
+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) {
+ 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),
+ closed_(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),
+ input_frame_count_(0),
+ input_coded_size_(PP_MakeSize(0, 0)),
+ buffer_manager_(this),
+ get_video_frame_data_(nullptr),
+ get_bitstream_buffer_data_(nullptr) {
+ SendCreate(RENDERER, PpapiHostMsg_VideoEncoder_Create());
}
VideoEncoderResource::~VideoEncoderResource() {
+ Close();
}
PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() {
@@ -24,7 +84,28 @@ PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() {
int32_t VideoEncoderResource::GetSupportedProfiles(
const PP_ArrayOutput& output,
const scoped_refptr<TrackedCallback>& callback) {
- return PP_ERROR_FAILED;
+ if (TrackedCallback::IsPending(get_supported_profiles_callback_))
+ return PP_ERROR_INPROGRESS;
+
+ get_supported_profiles_callback_ = callback;
+ Call<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply>(
+ RENDERER, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(),
+ base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply,
+ this, output));
+ return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t VideoEncoderResource::GetFramesRequired() {
+ if (encoder_last_error_)
+ return encoder_last_error_;
+ return input_frame_count_;
+}
+
+int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) {
+ if (encoder_last_error_)
+ return encoder_last_error_;
+ *size = input_coded_size_;
+ return PP_OK;
}
int32_t VideoEncoderResource::Initialize(
@@ -34,45 +115,304 @@ 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_;
+
+ if (TrackedCallback::IsPending(get_video_frame_callback_))
+ return PP_ERROR_INPROGRESS;
+
+ // Lazily ask for a shared memory buffer in which video frames are allocated.
+ if (buffer_manager_.number_of_buffers() == 0) {
+ get_video_frame_data_ = video_frame;
+ get_video_frame_callback_ = callback;
+ Call<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply>(
+ RENDERER, PpapiHostMsg_VideoEncoder_GetVideoFrames(),
+ base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply,
+ this));
+ return PP_OK_COMPLETIONPENDING;
+ }
+
+ if (!WriteVideoFrame(video_frame)) {
bbudge 2015/02/12 22:22:10 Suggestion: Move the next two statements to above
llandwerlin-old 2015/02/13 20:12:25 Done.
+ get_video_frame_data_ = video_frame;
+ get_video_frame_callback_ = callback;
+ return PP_OK_COMPLETIONPENDING;
+ }
+
+ return PP_OK;
}
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): accept MediaStreamVideoTrack's video frames.
+ return PP_ERROR_BADRESOURCE;
+
+ scoped_refptr<VideoFrameResource> frame_resource = it->second;
+
+ encode_callbacks_.push_back(callback);
+
+ Call<PpapiPluginMsg_VideoEncoder_EncodeReply>(
+ RENDERER,
+ PpapiHostMsg_VideoEncoder_Encode(frame_resource->GetBufferIndex(),
+ PP_ToBool(force_keyframe)),
+ base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this));
+
+ 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_bitstream_buffer_callback_))
+ return PP_ERROR_INPROGRESS;
+
+ if (available_bitstream_buffers_.empty()) {
+ get_bitstream_buffer_callback_ = callback;
+ get_bitstream_buffer_data_ = bitstream_buffer;
+ return PP_OK_COMPLETIONPENDING;
+ }
+
+ BitstreamBuffer buffer(available_bitstream_buffers_.front());
bbudge 2015/02/12 22:22:10 Could you make this const BitstreamBuffer& instead
llandwerlin-old 2015/02/13 20:12:25 Can't actually do that, because of reentrancy. We
+ available_bitstream_buffers_.pop_front();
+ WriteBitstreamBuffer(bitstream_buffer, buffer);
+
+ return PP_OK;
}
bbudge 2015/02/12 22:22:10 Suggestion: similarly to GetVideoFrame, you could
llandwerlin-old 2015/02/13 20:12:25 Done. Didn't rename WriteBitstreamBuffer as we're
void VideoEncoderResource::RecycleBitstreamBuffer(
- const PP_BitstreamBuffer* picture) {
+ const PP_BitstreamBuffer* bitstream_buffer) {
+ if (encoder_last_error_)
+ return;
+ BitstreamBufferMap::const_iterator iter =
+ bitstream_buffer_map_.find(bitstream_buffer->buffer);
+ if (iter != bitstream_buffer_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 (closed_)
+ return;
+ Post(RENDERER, PpapiHostMsg_VideoEncoder_Close());
+ closed_ = true;
+ if (!encoder_last_error_ || !initialized_)
+ NotifyError(PP_ERROR_ABORTED);
+ ReleaseFrames();
+}
+
+void VideoEncoderResource::OnReplyReceived(
+ const ResourceMessageReplyParams& params,
+ const IPC::Message& msg) {
+ PPAPI_BEGIN_MESSAGE_MAP(VideoEncoderResource, msg)
+ PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
+ PpapiPluginMsg_VideoEncoder_BitstreamBuffers,
+ OnPluginMsgBitstreamBuffers)
+ 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 ResourceMessageReplyParams& params,
+ const std::vector<PP_VideoProfileDescription>& profiles) {
+ void* ptr = output.GetDataBuffer(output.user_data, profiles.size(),
+ sizeof(PP_VideoProfileDescription));
+
+ if (!ptr) {
+ RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED);
+ return;
+ }
+
+ if (profiles.size() > 0)
+ memcpy(ptr, &profiles[0],
+ profiles.size() * sizeof(PP_VideoProfileDescription));
+ RunCallback(&get_supported_profiles_callback_, PP_OK);
+}
+
+void VideoEncoderResource::OnPluginMsgInitializeReply(
+ const ResourceMessageReplyParams& params) {
+ DCHECK(!initialized_);
+
bbudge 2015/02/12 22:22:10 initialized_ = true; Here, not below.
llandwerlin-old 2015/02/13 20:12:25 Done.
+ int32_t error = params.result();
+ if (error)
bbudge 2015/02/12 22:22:10 We should always run the callback here, not below.
llandwerlin-old 2015/02/13 20:12:26 Done.
+ RunCallback(&initialize_callback_, 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;
+ }
+
+ base::SharedMemoryHandle buffer_handle;
+ params.TakeSharedMemoryHandleAtIndex(0, &buffer_handle);
+
+ if (!buffer_manager_.SetBuffers(
+ frame_count, frame_length,
+ make_scoped_ptr(new base::SharedMemory(buffer_handle, false)),
+ true)) {
+ NotifyError(PP_ERROR_FAILED);
+ return;
+ }
+
+ WriteVideoFrame(get_video_frame_data_);
+ get_video_frame_data_ = nullptr;
+ RunCallback(&get_video_frame_callback_, PP_OK);
+}
+
+void VideoEncoderResource::OnPluginMsgEncodeReply(
+ const ResourceMessageReplyParams& params,
+ uint32_t frame_id) {
+ DCHECK_NE(encode_callbacks_.size(), 0U);
+ encoder_last_error_ = params.result();
+
+ scoped_refptr<TrackedCallback> callback = encode_callbacks_.front();
+ encode_callbacks_.pop_front();
+ RunCallback(&callback, encoder_last_error_);
+
+ buffer_manager_.EnqueueBuffer(frame_id);
+ // If the plugin is waiting for a video frame, we can give the one
+ // that just became available again.
+ if (TrackedCallback::IsPending(get_video_frame_callback_)) {
+ WriteVideoFrame(get_video_frame_data_);
+ RunCallback(&get_video_frame_callback_, PP_OK);
+ }
+}
+
+void VideoEncoderResource::OnPluginMsgBitstreamBuffers(
+ const ResourceMessageReplyParams& params,
+ uint32_t buffer_length,
+ uint32_t input_frame_count,
+ const PP_Size& input_coded_size) {
+ DCHECK(!initialized_);
+
+ std::vector<base::SharedMemoryHandle> shm_handles;
+ params.TakeAllSharedMemoryHandles(&shm_handles);
+
+ for (uint32_t i = 0; i < shm_handles.size(); ++i) {
+ ShmBuffer* buffer = new ShmBuffer(shm_handles[i], i, buffer_length);
+ shm_buffers_.push_back(buffer);
+ bitstream_buffer_map_.insert(
+ std::make_pair(buffer->shm->memory(), buffer->id));
+ }
+
+ input_frame_count_ = input_frame_count;
+ input_coded_size_ = input_coded_size;
+ initialized_ = true;
+ encoder_last_error_ = params.result();
+ RunCallback(&initialize_callback_, encoder_last_error_);
+}
+
+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_bitstream_buffer_callback_)) {
+ BitstreamBuffer buffer(available_bitstream_buffers_.front());
+ available_bitstream_buffers_.pop_front();
+ WriteBitstreamBuffer(get_bitstream_buffer_data_, buffer);
+ get_bitstream_buffer_data_ = nullptr;
+ RunCallback(&get_bitstream_buffer_callback_, PP_OK);
+ }
+}
+
+void VideoEncoderResource::OnPluginMsgNotifyError(
+ const ResourceMessageReplyParams& params,
+ int32_t error) {
+ NotifyError(error);
+}
+
+void VideoEncoderResource::NotifyError(int32_t error) {
+ encoder_last_error_ = error;
+ RunCallback(&get_supported_profiles_callback_, error);
+ RunCallback(&initialize_callback_, error);
+ RunCallback(&get_video_frame_callback_, error);
+ RunCallback(&get_bitstream_buffer_callback_, error);
+ while (!encode_callbacks_.empty()) {
+ scoped_refptr<TrackedCallback> callback = encode_callbacks_.front();
+ encode_callbacks_.pop_front();
+ RunCallback(&callback, error);
+ }
+}
+
+bool VideoEncoderResource::WriteVideoFrame(PP_Resource* output) {
+ int32_t frame_id = buffer_manager_.DequeueBuffer();
+ if (frame_id < 0)
+ return false;
+
+ 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));
+
+ *output = resource->GetReference();
+
+ return true;
+}
+
+void VideoEncoderResource::WriteBitstreamBuffer(
+ PP_BitstreamBuffer* bitstream_buffer,
+ const BitstreamBuffer& buffer) {
+ bitstream_buffer->size = buffer.size;
+ bitstream_buffer->buffer = shm_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;
+ }
bbudge 2015/02/12 22:22:10 Maybe clear video_frames_ for hygiene?
llandwerlin-old 2015/02/13 20:12:25 Done.
}
} // namespace proxy

Powered by Google App Engine
This is Rietveld 408576698