
If updated files don't use base::WrapUnique, base/memory/ptr_util.h includes are changed to <memory>. Bug: 755727 Change-Id: Ifb9ad8d3ee41130ac5c159cf084c979a304b80ed Reviewed-on: https://chromium-review.googlesource.com/885383 Commit-Queue: Gyuyoung Kim <gyuyoung.kim@lge.com> Reviewed-by: Scott Violet <sky@chromium.org> Reviewed-by: Jochen Eisinger <jochen@chromium.org> Cr-Commit-Position: refs/heads/master@{#532173}
486 lines
16 KiB
C++
486 lines
16 KiB
C++
// Copyright 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 "ppapi/proxy/video_encoder_resource.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/memory/shared_memory.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "ppapi/c/pp_array_output.h"
|
|
#include "ppapi/proxy/ppapi_messages.h"
|
|
#include "ppapi/proxy/video_frame_resource.h"
|
|
#include "ppapi/shared_impl/array_writer.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);
|
|
}
|
|
|
|
std::vector<PP_VideoProfileDescription_0_1> PP_VideoProfileDescriptionTo_0_1(
|
|
std::vector<PP_VideoProfileDescription> profiles) {
|
|
std::vector<PP_VideoProfileDescription_0_1> profiles_0_1;
|
|
|
|
for (uint32_t i = 0; i < profiles.size(); ++i) {
|
|
const PP_VideoProfileDescription& profile = profiles[i];
|
|
PP_VideoProfileDescription_0_1 profile_0_1;
|
|
|
|
profile_0_1.profile = profile.profile;
|
|
profile_0_1.max_resolution = profile.max_resolution;
|
|
profile_0_1.max_framerate_numerator = profile.max_framerate_numerator;
|
|
profile_0_1.max_framerate_denominator = profile.max_framerate_denominator;
|
|
profile_0_1.acceleration = profile.hardware_accelerated == PP_TRUE
|
|
? PP_HARDWAREACCELERATION_ONLY
|
|
: PP_HARDWAREACCELERATION_NONE;
|
|
|
|
profiles_0_1.push_back(profile_0_1);
|
|
}
|
|
|
|
return profiles_0_1;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
VideoEncoderResource::ShmBuffer::ShmBuffer(
|
|
uint32_t id,
|
|
std::unique_ptr<base::SharedMemory> shm)
|
|
: id(id), shm(std::move(shm)) {}
|
|
|
|
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() {
|
|
}
|
|
|
|
VideoEncoderResource::VideoEncoderResource(Connection connection,
|
|
PP_Instance 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() {
|
|
return this;
|
|
}
|
|
|
|
int32_t VideoEncoderResource::GetSupportedProfiles(
|
|
const PP_ArrayOutput& output,
|
|
const scoped_refptr<TrackedCallback>& callback) {
|
|
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, false));
|
|
return PP_OK_COMPLETIONPENDING;
|
|
}
|
|
|
|
int32_t VideoEncoderResource::GetSupportedProfiles0_1(
|
|
const PP_ArrayOutput& output,
|
|
const scoped_refptr<TrackedCallback>& callback) {
|
|
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, true));
|
|
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(
|
|
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_;
|
|
|
|
if (TrackedCallback::IsPending(get_video_frame_callback_))
|
|
return PP_ERROR_INPROGRESS;
|
|
|
|
get_video_frame_data_ = video_frame;
|
|
get_video_frame_callback_ = callback;
|
|
|
|
// Lazily ask for a shared memory buffer in which video frames are allocated.
|
|
if (buffer_manager_.number_of_buffers() == 0) {
|
|
Call<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply>(
|
|
RENDERER, PpapiHostMsg_VideoEncoder_GetVideoFrames(),
|
|
base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply,
|
|
this));
|
|
} else {
|
|
TryWriteVideoFrame();
|
|
}
|
|
|
|
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_;
|
|
|
|
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_.insert(std::make_pair(video_frame, callback));
|
|
|
|
Call<PpapiPluginMsg_VideoEncoder_EncodeReply>(
|
|
RENDERER,
|
|
PpapiHostMsg_VideoEncoder_Encode(frame_resource->GetBufferIndex(),
|
|
PP_ToBool(force_keyframe)),
|
|
base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this,
|
|
video_frame));
|
|
|
|
// Invalidate the frame to prevent the plugin from modifying it.
|
|
it->second->Invalidate();
|
|
video_frames_.erase(it);
|
|
|
|
return PP_OK_COMPLETIONPENDING;
|
|
}
|
|
|
|
int32_t VideoEncoderResource::GetBitstreamBuffer(
|
|
PP_BitstreamBuffer* bitstream_buffer,
|
|
const scoped_refptr<TrackedCallback>& callback) {
|
|
if (encoder_last_error_)
|
|
return encoder_last_error_;
|
|
if (TrackedCallback::IsPending(get_bitstream_buffer_callback_))
|
|
return PP_ERROR_INPROGRESS;
|
|
|
|
get_bitstream_buffer_callback_ = callback;
|
|
get_bitstream_buffer_data_ = bitstream_buffer;
|
|
|
|
if (!available_bitstream_buffers_.empty()) {
|
|
BitstreamBuffer buffer(available_bitstream_buffers_.front());
|
|
available_bitstream_buffers_.pop_front();
|
|
WriteBitstreamBuffer(buffer);
|
|
}
|
|
|
|
return PP_OK_COMPLETIONPENDING;
|
|
}
|
|
|
|
void VideoEncoderResource::RecycleBitstreamBuffer(
|
|
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,
|
|
bool version0_1,
|
|
const ResourceMessageReplyParams& params,
|
|
const std::vector<PP_VideoProfileDescription>& profiles) {
|
|
int32_t error = params.result();
|
|
if (error) {
|
|
NotifyError(error);
|
|
return;
|
|
}
|
|
|
|
ArrayWriter writer(output);
|
|
if (!writer.is_valid()) {
|
|
RunCallback(&get_supported_profiles_callback_, PP_ERROR_BADARGUMENT);
|
|
return;
|
|
}
|
|
|
|
bool write_result;
|
|
if (version0_1)
|
|
write_result =
|
|
writer.StoreVector(PP_VideoProfileDescriptionTo_0_1(profiles));
|
|
else
|
|
write_result = writer.StoreVector(profiles);
|
|
|
|
if (!write_result) {
|
|
RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED);
|
|
return;
|
|
}
|
|
|
|
RunCallback(&get_supported_profiles_callback_,
|
|
base::checked_cast<int32_t>(profiles.size()));
|
|
}
|
|
|
|
void VideoEncoderResource::OnPluginMsgInitializeReply(
|
|
const ResourceMessageReplyParams& params,
|
|
uint32_t input_frame_count,
|
|
const PP_Size& input_coded_size) {
|
|
DCHECK(!initialized_);
|
|
|
|
encoder_last_error_ = params.result();
|
|
if (!encoder_last_error_)
|
|
initialized_ = true;
|
|
|
|
input_frame_count_ = input_frame_count;
|
|
input_coded_size_ = input_coded_size;
|
|
|
|
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;
|
|
}
|
|
|
|
base::SharedMemoryHandle buffer_handle;
|
|
params.TakeSharedMemoryHandleAtIndex(0, &buffer_handle);
|
|
|
|
if (!buffer_manager_.SetBuffers(
|
|
frame_count, frame_length,
|
|
std::make_unique<base::SharedMemory>(buffer_handle, false), true)) {
|
|
NotifyError(PP_ERROR_FAILED);
|
|
return;
|
|
}
|
|
|
|
if (TrackedCallback::IsPending(get_video_frame_callback_))
|
|
TryWriteVideoFrame();
|
|
}
|
|
|
|
void VideoEncoderResource::OnPluginMsgEncodeReply(
|
|
PP_Resource video_frame,
|
|
const ResourceMessageReplyParams& params,
|
|
uint32_t frame_id) {
|
|
// We need to ensure there are still callbacks to be called before
|
|
// processing this message. We might receive a EncodeReply message
|
|
// after having sent a Close message to the renderer. In this case,
|
|
// we don't have any callback left to call.
|
|
if (encode_callbacks_.empty())
|
|
return;
|
|
encoder_last_error_ = params.result();
|
|
|
|
EncodeMap::iterator it = encode_callbacks_.find(video_frame);
|
|
DCHECK(encode_callbacks_.end() != it);
|
|
|
|
scoped_refptr<TrackedCallback> callback = it->second;
|
|
encode_callbacks_.erase(it);
|
|
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_))
|
|
TryWriteVideoFrame();
|
|
}
|
|
|
|
void VideoEncoderResource::OnPluginMsgBitstreamBuffers(
|
|
const ResourceMessageReplyParams& params,
|
|
uint32_t buffer_length) {
|
|
std::vector<base::SharedMemoryHandle> shm_handles;
|
|
params.TakeAllSharedMemoryHandles(&shm_handles);
|
|
if (shm_handles.size() == 0) {
|
|
NotifyError(PP_ERROR_FAILED);
|
|
return;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < shm_handles.size(); ++i) {
|
|
std::unique_ptr<base::SharedMemory> shm(
|
|
new base::SharedMemory(shm_handles[i], true));
|
|
CHECK(shm->Map(buffer_length));
|
|
|
|
auto buffer = std::make_unique<ShmBuffer>(i, std::move(shm));
|
|
bitstream_buffer_map_.insert(
|
|
std::make_pair(buffer->shm->memory(), buffer->id));
|
|
shm_buffers_.push_back(std::move(buffer));
|
|
}
|
|
}
|
|
|
|
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_bitstream_buffer_callback_)) {
|
|
BitstreamBuffer buffer(available_bitstream_buffers_.front());
|
|
available_bitstream_buffers_.pop_front();
|
|
WriteBitstreamBuffer(buffer);
|
|
}
|
|
}
|
|
|
|
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);
|
|
get_video_frame_data_ = nullptr;
|
|
RunCallback(&get_bitstream_buffer_callback_, error);
|
|
get_bitstream_buffer_data_ = nullptr;
|
|
for (EncodeMap::iterator it = encode_callbacks_.begin();
|
|
it != encode_callbacks_.end(); ++it) {
|
|
scoped_refptr<TrackedCallback> callback = it->second;
|
|
RunCallback(&callback, error);
|
|
}
|
|
encode_callbacks_.clear();
|
|
}
|
|
|
|
void VideoEncoderResource::TryWriteVideoFrame() {
|
|
DCHECK(TrackedCallback::IsPending(get_video_frame_callback_));
|
|
|
|
int32_t frame_id = buffer_manager_.DequeueBuffer();
|
|
if (frame_id < 0)
|
|
return;
|
|
|
|
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));
|
|
|
|
*get_video_frame_data_ = resource->GetReference();
|
|
get_video_frame_data_ = nullptr;
|
|
RunCallback(&get_video_frame_callback_, PP_OK);
|
|
}
|
|
|
|
void VideoEncoderResource::WriteBitstreamBuffer(const BitstreamBuffer& buffer) {
|
|
DCHECK_LT(buffer.id, shm_buffers_.size());
|
|
|
|
get_bitstream_buffer_data_->size = buffer.size;
|
|
get_bitstream_buffer_data_->buffer = shm_buffers_[buffer.id]->shm->memory();
|
|
get_bitstream_buffer_data_->key_frame = PP_FromBool(buffer.key_frame);
|
|
get_bitstream_buffer_data_ = nullptr;
|
|
RunCallback(&get_bitstream_buffer_callback_, PP_OK);
|
|
}
|
|
|
|
void VideoEncoderResource::ReleaseFrames() {
|
|
for (VideoFrameMap::iterator it = video_frames_.begin();
|
|
it != video_frames_.end(); ++it) {
|
|
it->second->Invalidate();
|
|
it->second = nullptr;
|
|
}
|
|
video_frames_.clear();
|
|
}
|
|
|
|
} // namespace proxy
|
|
} // namespace ppapi
|