Chromium Code Reviews| 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 |