| Index: ppapi/proxy/video_encoder_resource.cc
|
| diff --git a/ppapi/proxy/video_encoder_resource.cc b/ppapi/proxy/video_encoder_resource.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9b2d97dafcc9039623206ff0f6c803112207c62c
|
| --- /dev/null
|
| +++ b/ppapi/proxy/video_encoder_resource.cc
|
| @@ -0,0 +1,401 @@
|
| +// Copyright (c) 2015 The Chromium Authors. All rights reserved.
|
| +// 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);
|
| +};
|
| +
|
| +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)
|
| + 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),
|
| + 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),
|
| + waiting_for_video_frames_(false),
|
| + buffer_manager_delegate_(new BufferManagerDelegate()),
|
| + media_stream_buffer_manager_(
|
| + new MediaStreamBufferManager(buffer_manager_delegate_.get())) {
|
| + SendCreate(RENDERER, PpapiHostMsg_VideoEncoder_Create());
|
| +}
|
| +
|
| +VideoEncoderResource::~VideoEncoderResource() {
|
| + encoder_last_error_ = PP_ERROR_ABORTED;
|
| + RunCallback(&initialize_callback_, encoder_last_error_);
|
| + RunCallback(&get_bitstreamer_buffer_callback_, encoder_last_error_);
|
| + NotifyGetVideoFrameCallbacksError(encoder_last_error_);
|
| +
|
| + for (VideoFrameMap::iterator it = video_frames_.begin();
|
| + it != video_frames_.end(); ++it) {
|
| + it->second->Invalidate();
|
| + it->second = nullptr;
|
| + }
|
| +}
|
| +
|
| +PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() {
|
| + return this;
|
| +}
|
| +
|
| +int32_t VideoEncoderResource::GetSupportedProfiles(
|
| + const PP_ArrayOutput& output,
|
| + const scoped_refptr<TrackedCallback>& callback) {
|
| + // TODO(llandwerlin): should we prevent concurrent calls?
|
| + 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(
|
| + PP_VideoFrame_Format input_format,
|
| + const PP_Size* input_visible_size,
|
| + PP_VideoProfile output_profile,
|
| + uint32_t initial_bitrate,
|
| + PP_HardwareAcceleration acceleration,
|
| + const scoped_refptr<TrackedCallback>& callback) {
|
| + if (initialized_)
|
| + return PP_ERROR_FAILED;
|
| + if (TrackedCallback::IsPending(initialize_callback_))
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + 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) {
|
| + 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.
|
| + if (media_stream_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) {
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| +
|
| + EnterResourceNoLock<thunk::PPB_VideoFrame_API> enter(video_frame, true);
|
| + if (enter.failed())
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + thunk::PPB_VideoFrame_API* ppb_video_frame = enter.object();
|
| +
|
| + if (media_stream_buffer_manager_->ContainsBuffer(
|
| + ppb_video_frame->GetBuffer())) {
|
| + int32_t frame_id = ppb_video_frame->GetBufferIndex();
|
| + Call<PpapiPluginMsg_VideoEncoder_EncodeReply>(
|
| + RENDERER, PpapiHostMsg_VideoEncoder_Encode(frame_id, force_keyframe),
|
| + base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this,
|
| + callback));
|
| + } else {
|
| + // TODO(llandwerlin): deal MediaStreamVideoTrack's video frames.
|
| + return PP_ERROR_BADARGUMENT;
|
| + }
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +int32_t VideoEncoderResource::GetBitstreamBuffer(
|
| + PP_BitstreamBuffer* bitstream_buffer,
|
| + const scoped_refptr<TrackedCallback>& callback) {
|
| + 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* bitstream_buffer) {
|
| + 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) {
|
| + Post(RENDERER, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange(
|
| + bitrate, framerate));
|
| +}
|
| +
|
| +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_VideoDecoder_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_SupportedVideoProfile>& profiles) {
|
| + void* ptr = output.GetDataBuffer(output.user_data, profiles.size(),
|
| + sizeof(PP_SupportedVideoProfile));
|
| +
|
| + if (!ptr) {
|
| + callback->Run(PP_ERROR_BADARGUMENT);
|
| + return;
|
| + }
|
| +
|
| + memcpy(ptr, &profiles[0], profiles.size() * sizeof(PP_SupportedVideoProfile));
|
| + 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;
|
| + params.TakeAllSharedMemoryHandles(&buffer_handles);
|
| +
|
| + CHECK(buffer_handles.size() >= buffer_count);
|
| +
|
| + bitstream_buffers_.clear();
|
| + bitstream_buffers_map_.clear();
|
| + 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;
|
| + 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) {
|
| + encoder_last_error_ = params.result();
|
| + if (encoder_last_error_) {
|
| + OnPluginMsgNotifyError(params, encoder_last_error_);
|
| + return;
|
| + }
|
| +
|
| + std::vector<base::SharedMemoryHandle> buffer_handles;
|
| + params.TakeAllSharedMemoryHandles(&buffer_handles);
|
| + CHECK(buffer_handles.size() == 1);
|
| +
|
| + if (!media_stream_buffer_manager_->SetBuffers(
|
| + frame_count, frame_length,
|
| + make_scoped_ptr(new base::SharedMemory(buffer_handles[0], false)),
|
| + true)) {
|
| + OnPluginMsgNotifyError(params, PP_ERROR_FAILED);
|
| + return;
|
| + }
|
| +
|
| + for (uint32_t frame_id = 0; frame_id < frame_count; frame_id++) {
|
| + video_frames_[frame_id] = make_scoped_refptr(new VideoFrameResource(
|
| + pp_instance(), frame_id,
|
| + media_stream_buffer_manager_->GetBufferPointer(frame_id)));
|
| + }
|
| + waiting_for_video_frames_ = false;
|
| +
|
| + NotifyGetVideoFrameCallbacks();
|
| +}
|
| +
|
| +void VideoEncoderResource::OnPluginMsgEncodeReply(
|
| + const scoped_refptr<TrackedCallback>& callback,
|
| + const ResourceMessageReplyParams& params,
|
| + uint32_t frame_id) {
|
| + callback->Run(encoder_last_error_);
|
| + media_stream_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, 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) {
|
| + encoder_last_error_ = error;
|
| + RunCallback(&initialize_callback_, error);
|
| + RunCallback(&get_bitstreamer_buffer_callback_, error);
|
| + NotifyGetVideoFrameCallbacksError(error);
|
| +}
|
| +
|
| +void VideoEncoderResource::NotifyGetVideoFrameCallbacks() {
|
| + while (!get_video_frame_cbs_.empty() &&
|
| + media_stream_buffer_manager_->BuffersAvailable() > 0) {
|
| + std::pair<PP_Resource*, scoped_refptr<TrackedCallback>> cb =
|
| + get_video_frame_cbs_.front();
|
| + get_video_frame_cbs_.pop_front();
|
| +
|
| + int32_t frame_id = media_stream_buffer_manager_->DequeueBuffer();
|
| + *cb.first = video_frames_[frame_id]->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);
|
| +}
|
| +
|
| +} // namespace proxy
|
| +} // namespace ppapi
|
|
|