Commit 7918cb6a authored by lionel.g.landwerlin's avatar lionel.g.landwerlin Committed by Commit bot

content: pepper: VideoEncoder: add software encoder support

BUG=455409
TEST=run content_browsertests --gtest_filter=OutOfProcessPPAPITest.VideoEncoder

Review URL: https://codereview.chromium.org/956893002

Cr-Commit-Position: refs/heads/master@{#321790}
parent f7f718b5
...@@ -594,6 +594,8 @@ ...@@ -594,6 +594,8 @@
'renderer/pepper/v8object_var.h', 'renderer/pepper/v8object_var.h',
'renderer/pepper/video_decoder_shim.cc', 'renderer/pepper/video_decoder_shim.cc',
'renderer/pepper/video_decoder_shim.h', 'renderer/pepper/video_decoder_shim.h',
'renderer/pepper/video_encoder_shim.cc',
'renderer/pepper/video_encoder_shim.h',
'renderer/render_widget_fullscreen_pepper.cc', 'renderer/render_widget_fullscreen_pepper.cc',
'renderer/render_widget_fullscreen_pepper.h', 'renderer/render_widget_fullscreen_pepper.h',
], ],
...@@ -788,6 +790,9 @@ ...@@ -788,6 +790,9 @@
'renderer/java/gin_java_function_invocation_helper.cc', 'renderer/java/gin_java_function_invocation_helper.cc',
'renderer/java/gin_java_function_invocation_helper.h', 'renderer/java/gin_java_function_invocation_helper.h',
], ],
'dependencies': [
'../media/cast/cast.gyp:cast_sender',
]
}], }],
# TODO(jrg): remove the OS=="android" section? # TODO(jrg): remove the OS=="android" section?
# http://crbug.com/113172 # http://crbug.com/113172
...@@ -827,6 +832,8 @@ ...@@ -827,6 +832,8 @@
# Android does not build FFmpeg, which these depend on. # Android does not build FFmpeg, which these depend on.
'renderer/pepper/video_decoder_shim.cc', 'renderer/pepper/video_decoder_shim.cc',
'renderer/pepper/video_decoder_shim.h', 'renderer/pepper/video_decoder_shim.h',
'renderer/pepper/video_encoder_shim.cc',
'renderer/pepper/video_encoder_shim.h',
], ],
}], }],
], ],
......
...@@ -148,6 +148,7 @@ source_set("renderer") { ...@@ -148,6 +148,7 @@ source_set("renderer") {
".", ".",
"//content") "//content")
deps += [ deps += [
"//media/cast:sender",
"//ppapi/host", "//ppapi/host",
"//ppapi/proxy", "//ppapi/proxy",
"//ppapi/shared_impl", "//ppapi/shared_impl",
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "content/renderer/pepper/gfx_conversion.h" #include "content/renderer/pepper/gfx_conversion.h"
#include "content/renderer/pepper/host_globals.h" #include "content/renderer/pepper/host_globals.h"
#include "content/renderer/pepper/pepper_video_encoder_host.h" #include "content/renderer/pepper/pepper_video_encoder_host.h"
#include "content/renderer/pepper/video_encoder_shim.h"
#include "content/renderer/render_thread_impl.h" #include "content/renderer/render_thread_impl.h"
#include "media/base/bind_to_current_loop.h" #include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
...@@ -156,6 +157,8 @@ PP_VideoProfileDescription PP_FromVideoEncodeAcceleratorSupportedProfile( ...@@ -156,6 +157,8 @@ PP_VideoProfileDescription PP_FromVideoEncodeAcceleratorSupportedProfile(
bool PP_HardwareAccelerationCompatible(PP_HardwareAcceleration supply, bool PP_HardwareAccelerationCompatible(PP_HardwareAcceleration supply,
PP_HardwareAcceleration demand) { PP_HardwareAcceleration demand) {
// TODO(llandwerlin): Change API to use bool instead of
// PP_HardwareAcceleration
switch (supply) { switch (supply) {
case PP_HARDWAREACCELERATION_ONLY: case PP_HARDWAREACCELERATION_ONLY:
return (demand == PP_HARDWAREACCELERATION_ONLY || return (demand == PP_HARDWAREACCELERATION_ONLY ||
...@@ -270,8 +273,7 @@ int32_t PepperVideoEncoderHost::OnHostMsgInitialize( ...@@ -270,8 +273,7 @@ int32_t PepperVideoEncoderHost::OnHostMsgInitialize(
int32_t error = PP_ERROR_NOTSUPPORTED; int32_t error = PP_ERROR_NOTSUPPORTED;
initialize_reply_context_ = context->MakeReplyMessageContext(); initialize_reply_context_ = context->MakeReplyMessageContext();
if (acceleration == PP_HARDWAREACCELERATION_ONLY || if (acceleration != PP_HARDWAREACCELERATION_NONE) {
acceleration == PP_HARDWAREACCELERATION_WITHFALLBACK) {
if (InitializeHardware(media_input_format_, input_size, media_profile, if (InitializeHardware(media_input_format_, input_size, media_profile,
initial_bitrate)) initial_bitrate))
return PP_OK_COMPLETIONPENDING; return PP_OK_COMPLETIONPENDING;
...@@ -280,10 +282,23 @@ int32_t PepperVideoEncoderHost::OnHostMsgInitialize( ...@@ -280,10 +282,23 @@ int32_t PepperVideoEncoderHost::OnHostMsgInitialize(
error = PP_ERROR_FAILED; error = PP_ERROR_FAILED;
} }
// TODO(llandwerlin): Software encoder. #if defined(OS_ANDROID)
initialize_reply_context_ = ppapi::host::ReplyMessageContext(); initialize_reply_context_ = ppapi::host::ReplyMessageContext();
Close(); Close();
return error; return error;
#else
if (acceleration != PP_HARDWAREACCELERATION_ONLY) {
encoder_.reset(new VideoEncoderShim(this));
if (encoder_->Initialize(media_input_format_, input_size, media_profile,
initial_bitrate, this))
return PP_OK_COMPLETIONPENDING;
error = PP_ERROR_FAILED;
}
initialize_reply_context_ = ppapi::host::ReplyMessageContext();
Close();
return error;
#endif
} }
int32_t PepperVideoEncoderHost::OnHostMsgGetVideoFrames( int32_t PepperVideoEncoderHost::OnHostMsgGetVideoFrames(
...@@ -435,17 +450,25 @@ void PepperVideoEncoderHost::GetSupportedProfiles( ...@@ -435,17 +450,25 @@ void PepperVideoEncoderHost::GetSupportedProfiles(
std::vector<PP_VideoProfileDescription>* pp_profiles) { std::vector<PP_VideoProfileDescription>* pp_profiles) {
DCHECK(RenderThreadImpl::current()); DCHECK(RenderThreadImpl::current());
if (!EnsureGpuChannel()) std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles;
return;
std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles = if (EnsureGpuChannel()) {
GpuVideoEncodeAcceleratorHost::ConvertGpuToMediaProfiles( profiles = GpuVideoEncodeAcceleratorHost::ConvertGpuToMediaProfiles(
channel_->gpu_info().video_encode_accelerator_supported_profiles); channel_->gpu_info().video_encode_accelerator_supported_profiles);
for (media::VideoEncodeAccelerator::SupportedProfile profile : profiles) for (media::VideoEncodeAccelerator::SupportedProfile profile : profiles) {
pp_profiles->push_back(PP_FromVideoEncodeAcceleratorSupportedProfile( pp_profiles->push_back(PP_FromVideoEncodeAcceleratorSupportedProfile(
profile, PP_HARDWAREACCELERATION_ONLY)); profile, PP_HARDWAREACCELERATION_ONLY));
}
}
// TODO(llandwerlin): add software supported profiles. #if !defined(OS_ANDROID)
VideoEncoderShim software_encoder(this);
profiles = software_encoder.GetSupportedProfiles();
for (media::VideoEncodeAccelerator::SupportedProfile profile : profiles) {
pp_profiles->push_back(PP_FromVideoEncodeAcceleratorSupportedProfile(
profile, PP_HARDWAREACCELERATION_NONE));
}
#endif
} }
bool PepperVideoEncoderHost::IsInitializationValid( bool PepperVideoEncoderHost::IsInitializationValid(
...@@ -641,4 +664,11 @@ void PepperVideoEncoderHost::NotifyPepperError(int32_t error) { ...@@ -641,4 +664,11 @@ void PepperVideoEncoderHost::NotifyPepperError(int32_t error) {
PpapiPluginMsg_VideoEncoder_NotifyError(encoder_last_error_)); PpapiPluginMsg_VideoEncoder_NotifyError(encoder_last_error_));
} }
uint8_t* PepperVideoEncoderHost::ShmHandleToAddress(int32 buffer_id) {
DCHECK(RenderThreadImpl::current());
DCHECK_GE(buffer_id, 0);
DCHECK_LT(buffer_id, static_cast<int32>(shm_buffers_.size()));
return static_cast<uint8_t*>(shm_buffers_[buffer_id]->shm->memory());
}
} // namespace content } // namespace content
...@@ -26,6 +26,7 @@ namespace content { ...@@ -26,6 +26,7 @@ namespace content {
class CommandBufferProxyImpl; class CommandBufferProxyImpl;
class GpuChannelHost; class GpuChannelHost;
class RendererPpapiHost; class RendererPpapiHost;
class VideoEncoderShim;
class CONTENT_EXPORT PepperVideoEncoderHost class CONTENT_EXPORT PepperVideoEncoderHost
: public ppapi::host::ResourceHost, : public ppapi::host::ResourceHost,
...@@ -38,6 +39,8 @@ class CONTENT_EXPORT PepperVideoEncoderHost ...@@ -38,6 +39,8 @@ class CONTENT_EXPORT PepperVideoEncoderHost
~PepperVideoEncoderHost() override; ~PepperVideoEncoderHost() override;
private: private:
friend class VideoEncoderShim;
// Shared memory buffers. // Shared memory buffers.
struct ShmBuffer { struct ShmBuffer {
ShmBuffer(uint32_t id, scoped_ptr<base::SharedMemory> shm); ShmBuffer(uint32_t id, scoped_ptr<base::SharedMemory> shm);
...@@ -108,6 +111,9 @@ class CONTENT_EXPORT PepperVideoEncoderHost ...@@ -108,6 +111,9 @@ class CONTENT_EXPORT PepperVideoEncoderHost
uint32_t frame_id); uint32_t frame_id);
void NotifyPepperError(int32_t error); void NotifyPepperError(int32_t error);
// Helper method for VideoEncoderShim.
uint8_t* ShmHandleToAddress(int32 buffer_id);
// Non-owning pointer. // Non-owning pointer.
RendererPpapiHost* renderer_ppapi_host_; RendererPpapiHost* renderer_ppapi_host_;
......
// 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 "content/renderer/pepper/video_encoder_shim.h"
#include <inttypes.h>
#include <deque>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "content/renderer/pepper/pepper_video_encoder_host.h"
#include "content/renderer/render_thread_impl.h"
#include "media/cast/cast_config.h"
#include "media/cast/sender/vp8_encoder.h"
#include "ui/gfx/geometry/size.h"
namespace content {
// TODO(llandwerlin): Libvpx doesn't seem to have a maximum frame size
// limitation. We currently limit the size of the frames to encode at
// 1080p (%64 pixels blocks), this seems like a reasonable limit for
// software encoding.
const int32_t kMaxWidth = 1920;
const int32_t kMaxHeight = 1088;
// Bitstream buffer size.
const uint32_t kBitstreamBufferSize = 2 * 1024 * 1024;
// Number of frames needs at any given time.
const uint32_t kInputFrameCount = 1;
class VideoEncoderShim::EncoderImpl {
public:
explicit EncoderImpl(const base::WeakPtr<VideoEncoderShim>& shim);
~EncoderImpl();
void Initialize(media::VideoFrame::Format input_format,
const gfx::Size& input_visible_size,
media::VideoCodecProfile output_profile,
uint32 initial_bitrate);
void Encode(const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe);
void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer,
uint8_t* mem);
void RequestEncodingParametersChange(uint32 bitrate, uint32 framerate);
void Stop();
private:
struct PendingEncode {
PendingEncode(const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe)
: frame(frame), force_keyframe(force_keyframe) {}
~PendingEncode() {}
scoped_refptr<media::VideoFrame> frame;
bool force_keyframe;
};
struct BitstreamBuffer {
BitstreamBuffer(const media::BitstreamBuffer buffer, uint8_t* mem)
: buffer(buffer), mem(mem) {}
~BitstreamBuffer() {}
media::BitstreamBuffer buffer;
uint8_t* mem;
};
void DoEncode();
base::WeakPtr<VideoEncoderShim> shim_;
scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
scoped_ptr<media::cast::SoftwareVideoEncoder> encoder_;
std::deque<PendingEncode> frames_;
std::deque<BitstreamBuffer> buffers_;
};
VideoEncoderShim::EncoderImpl::EncoderImpl(
const base::WeakPtr<VideoEncoderShim>& shim)
: shim_(shim), media_task_runner_(base::MessageLoopProxy::current()) {
}
VideoEncoderShim::EncoderImpl::~EncoderImpl() {
}
void VideoEncoderShim::EncoderImpl::Initialize(
media::VideoFrame::Format input_format,
const gfx::Size& input_visible_size,
media::VideoCodecProfile output_profile,
uint32 initial_bitrate) {
media::cast::VideoSenderConfig config;
config.max_number_of_video_buffers_used = kInputFrameCount;
config.number_of_encode_threads = 1;
encoder_.reset(new media::cast::Vp8Encoder(config));
encoder_->UpdateRates(initial_bitrate);
media_task_runner_->PostTask(
FROM_HERE, base::Bind(&VideoEncoderShim::OnRequireBitstreamBuffers, shim_,
kInputFrameCount, input_visible_size,
kBitstreamBufferSize));
}
void VideoEncoderShim::EncoderImpl::Encode(
const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe) {
frames_.push_back(PendingEncode(frame, force_keyframe));
DoEncode();
}
void VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer(
const media::BitstreamBuffer& buffer,
uint8_t* mem) {
buffers_.push_back(BitstreamBuffer(buffer, mem));
DoEncode();
}
void VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange(
uint32 bitrate,
uint32 framerate) {
encoder_->UpdateRates(bitrate);
}
void VideoEncoderShim::EncoderImpl::Stop() {
frames_.clear();
buffers_.clear();
encoder_.reset();
}
void VideoEncoderShim::EncoderImpl::DoEncode() {
while (!frames_.empty() && !buffers_.empty()) {
PendingEncode frame = frames_.front();
frames_.pop_front();
if (frame.force_keyframe)
encoder_->GenerateKeyFrame();
scoped_ptr<media::cast::EncodedFrame> encoded_frame(
new media::cast::EncodedFrame());
encoder_->Encode(frame.frame, base::TimeTicks::Now(), encoded_frame.get());
BitstreamBuffer buffer = buffers_.front();
buffers_.pop_front();
CHECK(buffer.buffer.size() >= encoded_frame->data.size());
memcpy(buffer.mem, encoded_frame->bytes(), encoded_frame->data.size());
// Pass the media::VideoFrame back to the renderer thread so it's
// freed on the right thread.
media_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&VideoEncoderShim::OnBitstreamBufferReady, shim_,
frame.frame, buffer.buffer.id(), encoded_frame->data.size(),
encoded_frame->dependency == media::cast::EncodedFrame::KEY));
}
}
VideoEncoderShim::VideoEncoderShim(PepperVideoEncoderHost* host)
: host_(host),
media_task_runner_(
RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
weak_ptr_factory_(this) {
encoder_impl_.reset(new EncoderImpl(weak_ptr_factory_.GetWeakPtr()));
}
VideoEncoderShim::~VideoEncoderShim() {
DCHECK(RenderThreadImpl::current());
media_task_runner_->PostTask(
FROM_HERE, base::Bind(&VideoEncoderShim::EncoderImpl::Stop,
base::Owned(encoder_impl_.release())));
}
std::vector<media::VideoEncodeAccelerator::SupportedProfile>
VideoEncoderShim::GetSupportedProfiles() {
media::VideoEncodeAccelerator::SupportedProfile profile = {
media::VP8PROFILE_ANY,
gfx::Size(kMaxWidth, kMaxHeight),
media::cast::kDefaultMaxFrameRate,
1};
std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles;
profiles.push_back(profile);
return profiles;
}
bool VideoEncoderShim::Initialize(
media::VideoFrame::Format input_format,
const gfx::Size& input_visible_size,
media::VideoCodecProfile output_profile,
uint32 initial_bitrate,
media::VideoEncodeAccelerator::Client* client) {
DCHECK(RenderThreadImpl::current());
DCHECK_EQ(client, host_);
if (input_format != media::VideoFrame::I420)
return false;
media_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoEncoderShim::EncoderImpl::Initialize,
base::Unretained(encoder_impl_.get()), input_format,
input_visible_size, output_profile, initial_bitrate));
return true;
}
void VideoEncoderShim::Encode(const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe) {
DCHECK(RenderThreadImpl::current());
media_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoEncoderShim::EncoderImpl::Encode,
base::Unretained(encoder_impl_.get()), frame, force_keyframe));
}
void VideoEncoderShim::UseOutputBitstreamBuffer(
const media::BitstreamBuffer& buffer) {
DCHECK(RenderThreadImpl::current());
media_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoEncoderShim::EncoderImpl::UseOutputBitstreamBuffer,
base::Unretained(encoder_impl_.get()), buffer,
host_->ShmHandleToAddress(buffer.id())));
}
void VideoEncoderShim::RequestEncodingParametersChange(uint32 bitrate,
uint32 framerate) {
DCHECK(RenderThreadImpl::current());
media_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&VideoEncoderShim::EncoderImpl::RequestEncodingParametersChange,
base::Unretained(encoder_impl_.get()), bitrate, framerate));
}
void VideoEncoderShim::Destroy() {
DCHECK(RenderThreadImpl::current());
delete this;
}
void VideoEncoderShim::OnRequireBitstreamBuffers(
unsigned int input_count,
const gfx::Size& input_coded_size,
size_t output_buffer_size) {
DCHECK(RenderThreadImpl::current());
host_->RequireBitstreamBuffers(input_count, input_coded_size,
output_buffer_size);
}
void VideoEncoderShim::OnBitstreamBufferReady(
scoped_refptr<media::VideoFrame> frame,
int32 bitstream_buffer_id,
size_t payload_size,
bool key_frame) {
DCHECK(RenderThreadImpl::current());
host_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame);
}
void VideoEncoderShim::OnNotifyError(
media::VideoEncodeAccelerator::Error error) {
DCHECK(RenderThreadImpl::current());
host_->NotifyError(error);
}
} // namespace content
// 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.
#ifndef CONTENT_RENDERER_PEPPER_VIDEO_ENCODER_SHIM_H_
#define CONTENT_RENDERER_PEPPER_VIDEO_ENCODER_SHIM_H_
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "media/video/video_encode_accelerator.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace gfx {
class Size;
}
namespace content {
class PepperVideoEncoderHost;
// This class is a shim to wrap a media::cast::SoftwareVideoEncoder so that it
// can be used by PepperVideoEncoderHost in place of a
// media::VideoEncodeAccelerator. This class should be constructed, used, and
// destructed on the main (render) thread.
class VideoEncoderShim : public media::VideoEncodeAccelerator {
public:
explicit VideoEncoderShim(PepperVideoEncoderHost* host);
~VideoEncoderShim() override;
// media::VideoEncodeAccelerator implementation.
std::vector<media::VideoEncodeAccelerator::SupportedProfile>
GetSupportedProfiles() override;
bool Initialize(media::VideoFrame::Format input_format,
const gfx::Size& input_visible_size,
media::VideoCodecProfile output_profile,
uint32 initial_bitrate,
media::VideoEncodeAccelerator::Client* client) override;
void Encode(const scoped_refptr<media::VideoFrame>& frame,
bool force_keyframe) override;
void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer) override;
void RequestEncodingParametersChange(uint32 bitrate,
uint32 framerate) override;
void Destroy() override;
private:
class EncoderImpl;
void OnRequireBitstreamBuffers(unsigned int input_count,
const gfx::Size& input_coded_size,
size_t output_buffer_size);
void OnBitstreamBufferReady(scoped_refptr<media::VideoFrame> frame,
int32 bitstream_buffer_id,
size_t payload_size,
bool key_frame);
void OnNotifyError(media::VideoEncodeAccelerator::Error error);
scoped_ptr<EncoderImpl> encoder_impl_;
PepperVideoEncoderHost* host_;
// Task doing the encoding.
scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
base::WeakPtrFactory<VideoEncoderShim> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(VideoEncoderShim);
};
} // namespace content
#endif // CONTENT_RENDERER_PEPPER_VIDEO_ENCODER_SHIM_H_
This diff is collapsed.
...@@ -7,18 +7,6 @@ ...@@ -7,18 +7,6 @@
--> -->
<head> <head>
<title>Video Encoder Example</title> <title>Video Encoder Example</title>
<style type="text/css">
#video {
position: fixed;
}
#video-playback {
position: fixed;
left: 640px;
}
#plugin {
position: fixed;
}
</style>
<script type="text/javascript"> <script type="text/javascript">
var plugin; var plugin;
var track; var track;
...@@ -33,9 +21,14 @@ ...@@ -33,9 +21,14 @@
video.src = URL.createObjectURL(stream); video.src = URL.createObjectURL(stream);
video.play(); video.play();
var list = $('profileList');
var profile = (list.length < 1) ? 'vp8'
: list.options[list.selectedIndex].value;
plugin.postMessage({ plugin.postMessage({
command: 'start', command: 'start',
track: track, track: track,
profile: profile,
width: 640, width: 640,
height: 480 height: 480
}); });
...@@ -46,7 +39,7 @@ ...@@ -46,7 +39,7 @@
} }
function startRecord() { function startRecord() {
console.log("starting record"); $('length').innerHTML = ' Size: ' + dataArray.byteLength + ' bytes';
navigator.webkitGetUserMedia({audio: false, video: true}, navigator.webkitGetUserMedia({audio: false, video: true},
success, failure); success, failure);
} }
...@@ -72,6 +65,14 @@ ...@@ -72,6 +65,14 @@
appendData(msg.data.data); appendData(msg.data.data);
} else if (msg.data.name == 'stopped') { } else if (msg.data.name == 'stopped') {
console.log('done recording! bytes: ' + dataArray.byteLength); console.log('done recording! bytes: ' + dataArray.byteLength);
} else if (msg.data.name == 'supportedProfiles') {
console.log('profiles: ', msg.data);
var profileList = $('profileList');
for (var i = 0; i < msg.data.profiles.length; i++) {
var item = document.createElement('option');
item.label = item.value = msg.data.profiles[i];
profileList.appendChild(item);
}
} }
} }
...@@ -114,6 +115,7 @@ ...@@ -114,6 +115,7 @@
This example demonstrates receiving frames from a video MediaStreamTrack and This example demonstrates receiving frames from a video MediaStreamTrack and
encoding them in a plugin. encoding them in a plugin.
<br> <br>
<select id="profileList"></select>
<input type="button" id="start" value="Start Recording"/> <input type="button" id="start" value="Start Recording"/>
<input type="button" id="stop" value="Stop Recording"/> <input type="button" id="stop" value="Stop Recording"/>
<input type="button" id="download" value="Download Recording"/> <input type="button" id="download" value="Download Recording"/>
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/video_encoder_resource.h" #include "ppapi/proxy/video_encoder_resource.h"
#include "ppapi/proxy/video_frame_resource.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.h"
#include "ppapi/shared_impl/media_stream_buffer_manager.h" #include "ppapi/shared_impl/media_stream_buffer_manager.h"
#include "ppapi/thunk/enter.h" #include "ppapi/thunk/enter.h"
...@@ -249,19 +250,23 @@ void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply( ...@@ -249,19 +250,23 @@ void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply(
const PP_ArrayOutput& output, const PP_ArrayOutput& output,
const ResourceMessageReplyParams& params, const ResourceMessageReplyParams& params,
const std::vector<PP_VideoProfileDescription>& profiles) { const std::vector<PP_VideoProfileDescription>& profiles) {
void* ptr = output.GetDataBuffer( int32_t error = params.result();
output.user_data, if (error) {
base::checked_cast<uint32_t>(profiles.size()), NotifyError(error);
base::checked_cast<uint32_t>(sizeof(PP_VideoProfileDescription))); return;
}
ArrayWriter writer(output);
if (!writer.is_valid()) {
RunCallback(&get_supported_profiles_callback_, PP_ERROR_BADARGUMENT);
return;
}
if (!ptr) { if (!writer.StoreVector(profiles)) {
RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED); RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED);
return; return;
} }
if (profiles.size() > 0)
memcpy(ptr, &profiles[0],
profiles.size() * sizeof(PP_VideoProfileDescription));
RunCallback(&get_supported_profiles_callback_, PP_OK); RunCallback(&get_supported_profiles_callback_, PP_OK);
} }
......
...@@ -32,10 +32,59 @@ std::string TestVideoEncoder::TestCreate() { ...@@ -32,10 +32,59 @@ std::string TestVideoEncoder::TestCreate() {
callback.WaitForResult( callback.WaitForResult(
video_encoder.GetSupportedProfiles(callback.GetCallback())); video_encoder.GetSupportedProfiles(callback.GetCallback()));
ASSERT_GE(callback.output().size(), 0U); ASSERT_EQ(PP_OK, callback.result());
const std::vector<PP_VideoProfileDescription> video_profiles =
callback.output();
ASSERT_GE(video_profiles.size(), 1U);
bool found_vp8 = false;
for (uint32_t i = 0; i < video_profiles.size(); ++i) {
const PP_VideoProfileDescription& description = video_profiles[i];
if (description.profile == PP_VIDEOPROFILE_VP8_ANY)
found_vp8 = true;
}
ASSERT_TRUE(found_vp8);
}
// Test that initializing the encoder with incorrect size fails.
{
pp::VideoEncoder video_encoder(instance_);
ASSERT_FALSE(video_encoder.is_null());
pp::Size video_size(0, 0);
TestCompletionCallback callback(instance_->pp_instance(), false);
callback.WaitForResult(
video_encoder.Initialize(PP_VIDEOFRAME_FORMAT_I420,
video_size,
PP_VIDEOPROFILE_VP8_ANY,
1000000,
PP_HARDWAREACCELERATION_WITHFALLBACK,
callback.GetCallback()));
ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
}
// Test that initializing the encoder with software VP8 succeeds.
{
pp::VideoEncoder video_encoder(instance_);
ASSERT_FALSE(video_encoder.is_null());
pp::Size video_size(640, 480);
TestCompletionCallback callback(instance_->pp_instance(), false);
callback.WaitForResult(
video_encoder.Initialize(PP_VIDEOFRAME_FORMAT_I420,
video_size,
PP_VIDEOPROFILE_VP8_ANY,
1000000,
PP_HARDWAREACCELERATION_WITHFALLBACK,
callback.GetCallback()));
ASSERT_EQ(PP_OK, callback.result());
pp::Size coded_size;
ASSERT_EQ(PP_OK, video_encoder.GetFrameCodedSize(&coded_size));
ASSERT_GE(coded_size.GetArea(), video_size.GetArea());
ASSERT_GE(video_encoder.GetFramesRequired(), 1);
} }
// TODO(llandwerlin): add more tests once software video encode is
// available.
PASS(); PASS();
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment