| Index: ppapi/proxy/video_encoder_resource_unittest.cc
|
| diff --git a/ppapi/proxy/video_encoder_resource_unittest.cc b/ppapi/proxy/video_encoder_resource_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..02f1ae7a9fdc1126eac2d18e707019817218d760
|
| --- /dev/null
|
| +++ b/ppapi/proxy/video_encoder_resource_unittest.cc
|
| @@ -0,0 +1,813 @@
|
| +// 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 "base/memory/shared_memory.h"
|
| +#include "base/process/process.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "ppapi/c/pp_codecs.h"
|
| +#include "ppapi/c/pp_errors.h"
|
| +#include "ppapi/c/ppb_video_encoder.h"
|
| +#include "ppapi/c/ppb_video_frame.h"
|
| +#include "ppapi/proxy/locking_resource_releaser.h"
|
| +#include "ppapi/proxy/plugin_message_filter.h"
|
| +#include "ppapi/proxy/ppapi_message_utils.h"
|
| +#include "ppapi/proxy/ppapi_messages.h"
|
| +#include "ppapi/proxy/ppapi_proxy_test.h"
|
| +#include "ppapi/proxy/video_encoder_resource.h"
|
| +#include "ppapi/shared_impl/media_stream_buffer.h"
|
| +#include "ppapi/shared_impl/proxy_lock.h"
|
| +#include "ppapi/thunk/thunk.h"
|
| +
|
| +using ppapi::proxy::ResourceMessageTestSink;
|
| +
|
| +namespace ppapi {
|
| +namespace proxy {
|
| +
|
| +namespace {
|
| +
|
| +const uint32_t kShmSize = 256;
|
| +const size_t kDecodeBufferSize = 16;
|
| +const uint32_t kDecodeId = 5;
|
| +
|
| +class MockCompletionCallback {
|
| + public:
|
| + MockCompletionCallback() : called_(false), call_event_(false, false) {}
|
| +
|
| + bool called() { return called_; }
|
| + int32_t result() { return result_; }
|
| +
|
| + void WaitUntilCalled() { call_event_.Wait(); }
|
| +
|
| + void Reset() {
|
| + called_ = false;
|
| + call_event_.Reset();
|
| + }
|
| +
|
| + static void Callback(void* user_data, int32_t result) {
|
| + MockCompletionCallback* that =
|
| + reinterpret_cast<MockCompletionCallback*>(user_data);
|
| + that->call_event_.Signal();
|
| + that->called_ = true;
|
| + that->result_ = result;
|
| + }
|
| +
|
| + private:
|
| + bool called_;
|
| + int32_t result_;
|
| + base::WaitableEvent call_event_;
|
| +};
|
| +
|
| +class VideoEncoderResourceTest : public PluginProxyTest {
|
| + public:
|
| + VideoEncoderResourceTest()
|
| + : encoder_iface_(thunk::GetPPB_VideoEncoder_0_1_Thunk()) {}
|
| +
|
| + const PPB_VideoEncoder_0_1* encoder_iface() const { return encoder_iface_; }
|
| +
|
| + const uint32_t kBitstreamBufferSize = 4000;
|
| + const uint32_t kBitstreamBufferNb = 5;
|
| + const uint32_t kVideoFrameNb = 3;
|
| + const uint32_t kBitrate = 200000;
|
| +
|
| + const PP_Size kSize = {640, 480};
|
| +
|
| + void SendReply(const ResourceMessageCallParams& params,
|
| + int32_t result,
|
| + const IPC::Message& nested_message) {
|
| + ResourceMessageReplyParams reply_params(params.pp_resource(),
|
| + params.sequence());
|
| + reply_params.set_result(result);
|
| + PluginMessageFilter::DispatchResourceReplyForTest(reply_params,
|
| + nested_message);
|
| + }
|
| +
|
| + void SendReplyWithHandle(const ResourceMessageCallParams& params,
|
| + int32_t result,
|
| + const IPC::Message& nested_message,
|
| + const SerializedHandle& handle) {
|
| + ResourceMessageReplyParams reply_params(params.pp_resource(),
|
| + params.sequence());
|
| + reply_params.set_result(result);
|
| + reply_params.AppendHandle(handle);
|
| + PluginMessageFilter::DispatchResourceReplyForTest(reply_params,
|
| + nested_message);
|
| + }
|
| +
|
| + void SendReplyWithHandles(const ResourceMessageCallParams& params,
|
| + int32_t result,
|
| + const IPC::Message& nested_message,
|
| + const std::vector<SerializedHandle>& handles) {
|
| + ResourceMessageReplyParams reply_params(params.pp_resource(),
|
| + params.sequence());
|
| + reply_params.set_result(result);
|
| + for (SerializedHandle handle : handles)
|
| + reply_params.AppendHandle(handle);
|
| + PluginMessageFilter::DispatchResourceReplyForTest(reply_params,
|
| + nested_message);
|
| + }
|
| +
|
| + PP_Resource CreateEncoder() {
|
| + PP_Resource result = encoder_iface()->Create(pp_instance());
|
| + return result;
|
| + }
|
| +
|
| + void CreateBitstreamSharedMemory(uint32_t buffer_size, uint32_t nb_buffers) {
|
| + shared_memory_bitstreams_.clear();
|
| + for (uint32_t i = 0; i < nb_buffers; ++i) {
|
| + scoped_ptr<base::SharedMemory> mem(new base::SharedMemory());
|
| + ASSERT_TRUE(mem->CreateAnonymous(buffer_size));
|
| + shared_memory_bitstreams_.push_back(mem.Pass());
|
| + }
|
| + }
|
| +
|
| + void CreateVideoFramesSharedMemory(uint32_t frame_length,
|
| + uint32_t frame_count) {
|
| + shared_memory_frames_.reset(new base::SharedMemory());
|
| + uint32_t frame_buffer_length =
|
| + frame_length + sizeof(ppapi::MediaStreamBuffer::Video) -
|
| + sizeof(ppapi::MediaStreamBuffer::Video::data);
|
| + ASSERT_TRUE(shared_memory_frames_->CreateAnonymous(frame_buffer_length *
|
| + frame_count));
|
| + }
|
| +
|
| + PP_Resource CreateAndInitializeEncoder() {
|
| + PP_Resource encoder = CreateEncoder();
|
| + PP_Size size = kSize;
|
| + MockCompletionCallback cb;
|
| + int32_t result = encoder_iface()->Initialize(
|
| + encoder, PP_VIDEOFRAME_FORMAT_I420, &size, PP_VIDEOPROFILE_H264MAIN,
|
| + kBitrate, PP_HARDWAREACCELERATION_WITHFALLBACK,
|
| + PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
|
| + &cb));
|
| + if (result != PP_OK_COMPLETIONPENDING)
|
| + return 0;
|
| + ResourceMessageCallParams params;
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_Initialize::ID, ¶ms, &msg))
|
| + return 0;
|
| + sink().ClearMessages();
|
| +
|
| + CreateBitstreamSharedMemory(kBitstreamBufferSize, kBitstreamBufferNb);
|
| + SendInitializeReply(params, kBitstreamBufferNb, kBitstreamBufferSize,
|
| + kVideoFrameNb, size);
|
| +
|
| + if (!cb.called() || cb.result() != PP_OK)
|
| + return 0;
|
| +
|
| + return encoder;
|
| + }
|
| +
|
| + int32_t CallGetFramesRequired(PP_Resource pp_encoder) {
|
| + return encoder_iface()->GetFramesRequired(pp_encoder);
|
| + }
|
| +
|
| + int32_t CallGetFrameCodedSize(PP_Resource pp_encoder, PP_Size* coded_size) {
|
| + return encoder_iface()->GetFrameCodedSize(pp_encoder, coded_size);
|
| + }
|
| +
|
| + int32_t CallGetVideoFrame(PP_Resource pp_encoder,
|
| + PP_Resource* video_frame,
|
| + MockCompletionCallback* cb) {
|
| + return encoder_iface()->GetVideoFrame(
|
| + pp_encoder, video_frame, PP_MakeOptionalCompletionCallback(
|
| + &MockCompletionCallback::Callback, cb));
|
| + }
|
| +
|
| + int32_t CallFirstGetVideoFrame(PP_Resource pp_encoder,
|
| + PP_Resource* video_frame,
|
| + MockCompletionCallback* cb) {
|
| + int32_t result = encoder_iface()->GetVideoFrame(
|
| + pp_encoder, video_frame, PP_MakeOptionalCompletionCallback(
|
| + &MockCompletionCallback::Callback, cb));
|
| + if (result != PP_OK_COMPLETIONPENDING)
|
| + return result;
|
| +
|
| + ResourceMessageCallParams params;
|
| + CheckGetVideoFramesMsg(¶ms);
|
| +
|
| + uint32_t frame_length = kSize.width * kSize.height * 2;
|
| + CreateVideoFramesSharedMemory(frame_length, kVideoFrameNb);
|
| + SendGetVideoFramesReply(params, kVideoFrameNb, frame_length, kSize);
|
| +
|
| + return result;
|
| + }
|
| +
|
| + int32_t CallEncode(PP_Resource pp_encoder,
|
| + PP_Resource video_frame,
|
| + PP_Bool force_keyframe,
|
| + MockCompletionCallback* cb) {
|
| + return encoder_iface()->Encode(pp_encoder, video_frame, force_keyframe,
|
| + PP_MakeOptionalCompletionCallback(
|
| + &MockCompletionCallback::Callback, cb));
|
| + }
|
| +
|
| + int32_t CallCompleteEncode(PP_Resource pp_encoder,
|
| + PP_Resource video_frame,
|
| + PP_Bool force_keyframe,
|
| + MockCompletionCallback* cb) {
|
| + int32_t result =
|
| + encoder_iface()->Encode(pp_encoder, video_frame, force_keyframe,
|
| + PP_MakeOptionalCompletionCallback(
|
| + &MockCompletionCallback::Callback, cb));
|
| + if (result != PP_OK_COMPLETIONPENDING)
|
| + return result;
|
| +
|
| + ResourceMessageCallParams params;
|
| + uint32_t frame_id;
|
| + bool forced_keyframe;
|
| + if (!CheckEncodeMsg(¶ms, &frame_id, &forced_keyframe))
|
| + return PP_ERROR_FAILED;
|
| +
|
| + SendEncodeReply(params, frame_id);
|
| +
|
| + return result;
|
| + }
|
| +
|
| + int32_t CallGetBitstreamBuffer(PP_Resource pp_encoder,
|
| + PP_BitstreamBuffer* bitstream_buffer,
|
| + MockCompletionCallback* cb) {
|
| + return encoder_iface()->GetBitstreamBuffer(
|
| + pp_encoder, bitstream_buffer,
|
| + PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
|
| + cb));
|
| + }
|
| +
|
| + void CallRecycleBitstreamBuffer(PP_Resource pp_encoder,
|
| + const PP_BitstreamBuffer& buffer) {
|
| + encoder_iface()->RecycleBitstreamBuffer(pp_encoder, &buffer);
|
| + }
|
| +
|
| + void CallRequestEncodingParametersChange(PP_Resource pp_encoder,
|
| + uint32_t bitrate,
|
| + uint32_t framerate) {
|
| + encoder_iface()->RequestEncodingParametersChange(pp_encoder, bitrate,
|
| + framerate);
|
| + }
|
| +
|
| + void SendGetSupportedProfilesReply(
|
| + const ResourceMessageCallParams& params,
|
| + const std::vector<PP_VideoProfileDescription>& profiles) {
|
| + SendReply(params, PP_OK,
|
| + PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply(profiles));
|
| + }
|
| +
|
| + void SendInitializeReply(const ResourceMessageCallParams& params,
|
| + uint32_t buffer_count,
|
| + uint32_t buffer_length,
|
| + uint32_t input_frame_count,
|
| + const PP_Size& coded_size) {
|
| + std::vector<SerializedHandle> handles;
|
| + for (base::SharedMemory* mem : shared_memory_bitstreams_) {
|
| + ASSERT_EQ(mem->requested_size(), buffer_length);
|
| + base::SharedMemoryHandle handle;
|
| +
|
| + ASSERT_TRUE(
|
| + mem->ShareToProcess(base::Process::Current().Handle(), &handle));
|
| + handles.push_back(SerializedHandle(handle, buffer_length));
|
| + }
|
| + SendReplyWithHandles(
|
| + params, PP_OK,
|
| + PpapiPluginMsg_VideoEncoder_InitializeReply(
|
| + buffer_count, buffer_length, input_frame_count, coded_size),
|
| + handles);
|
| + }
|
| +
|
| + void SendGetVideoFramesReply(const ResourceMessageCallParams& params,
|
| + uint32_t frame_count,
|
| + uint32_t frame_length,
|
| + const PP_Size& size) {
|
| + base::SharedMemoryHandle handle;
|
| + ASSERT_TRUE(shared_memory_frames_->ShareToProcess(
|
| + base::Process::Current().Handle(), &handle));
|
| + SendReplyWithHandle(
|
| + params, PP_OK, PpapiPluginMsg_VideoEncoder_GetVideoFramesReply(
|
| + frame_count, frame_length, size),
|
| + SerializedHandle(handle, shared_memory_frames_->requested_size()));
|
| + }
|
| +
|
| + void SendEncodeReply(const ResourceMessageCallParams& params,
|
| + uint32_t frame_id) {
|
| + SendReply(params, PP_OK, PpapiPluginMsg_VideoEncoder_EncodeReply(frame_id));
|
| + }
|
| +
|
| + void SendBitstreamBufferReady(const ResourceMessageCallParams& params,
|
| + uint32_t buffer_id,
|
| + uint32_t buffer_size,
|
| + bool keyframe) {
|
| + SendReply(params, PP_OK,
|
| + PpapiPluginMsg_VideoEncoder_BitstreamBufferReady(
|
| + buffer_id, buffer_size, PP_FromBool(keyframe)));
|
| + }
|
| +
|
| + void SendNotifyError(const ResourceMessageCallParams& params, int32_t error) {
|
| + SendReply(params, PP_OK, PpapiPluginMsg_VideoEncoder_NotifyError(error));
|
| + }
|
| +
|
| + bool CheckGetSupportedProfilesMsg(ResourceMessageCallParams* params) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_GetSupportedProfiles::ID, params, &msg))
|
| + return false;
|
| + return true;
|
| + }
|
| +
|
| + bool CheckInitializeMsg(ResourceMessageCallParams* params,
|
| + PP_VideoFrame_Format* input_format,
|
| + struct PP_Size* input_visible_size,
|
| + PP_VideoProfile* output_profile,
|
| + uint32_t* bitrate,
|
| + PP_HardwareAcceleration* acceleration) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_Initialize::ID, params, &msg))
|
| + return false;
|
| + sink().ClearMessages();
|
| + return UnpackMessage<PpapiHostMsg_VideoEncoder_Initialize>(
|
| + msg, input_format, input_visible_size, output_profile, bitrate,
|
| + acceleration);
|
| + }
|
| +
|
| + bool CheckGetVideoFramesMsg(ResourceMessageCallParams* params) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_GetVideoFrames::ID, params, &msg))
|
| + return false;
|
| + sink().ClearMessages();
|
| + return true;
|
| + }
|
| +
|
| + bool CheckEncodeMsg(ResourceMessageCallParams* params,
|
| + uint32_t* frame_id,
|
| + bool* keyframe) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_Encode::ID, params, &msg))
|
| + return false;
|
| + sink().ClearMessages();
|
| + return UnpackMessage<PpapiHostMsg_VideoEncoder_Encode>(msg, frame_id,
|
| + keyframe);
|
| + }
|
| +
|
| + bool CheckRecycleBitstreamBufferMsg(ResourceMessageCallParams* params,
|
| + uint32_t* buffer_id) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer::ID, params, &msg))
|
| + return false;
|
| + sink().ClearMessages();
|
| + return UnpackMessage<PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer>(
|
| + msg, buffer_id);
|
| + }
|
| +
|
| + bool CheckRequestEncodeingParametersChangeMsg(
|
| + ResourceMessageCallParams* params,
|
| + uint32_t* bitrate,
|
| + uint32_t* framerate) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(
|
| + PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange::ID,
|
| + params, &msg))
|
| + return false;
|
| + sink().ClearMessages();
|
| + return UnpackMessage<
|
| + PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange>(msg, bitrate,
|
| + framerate);
|
| + }
|
| +
|
| + bool CheckIsVideoFrame(PP_Resource video_frame) {
|
| + return thunk::GetPPB_VideoFrame_0_1_Thunk()->IsVideoFrame(video_frame);
|
| + }
|
| +
|
| + private:
|
| + bool CheckMsg(ResourceMessageCallParams* params, int id) {
|
| + IPC::Message msg;
|
| + if (!sink().GetFirstResourceCallMatching(id, params, &msg))
|
| + return false;
|
| + sink().ClearMessages();
|
| + return true;
|
| + }
|
| +
|
| + const PPB_VideoEncoder_0_1* encoder_iface_;
|
| +
|
| + ScopedVector<base::SharedMemory> shared_memory_bitstreams_;
|
| + scoped_ptr<base::SharedMemory> shared_memory_frames_;
|
| +
|
| + char decode_buffer_[kDecodeBufferSize];
|
| +};
|
| +
|
| +void* ForwardUserData(void* user_data,
|
| + uint32_t element_count,
|
| + uint32_t element_size) {
|
| + return user_data;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +TEST_F(VideoEncoderResourceTest, GetSupportedProfiles) {
|
| + // Verifies that GetSupportedProfiles calls into the renderer and
|
| + // the we get the right results back.
|
| + {
|
| + LockingResourceReleaser encoder(CreateEncoder());
|
| + std::vector<PP_VideoProfileDescription> profiles;
|
| + profiles.resize(2);
|
| + PP_ArrayOutput output;
|
| + output.user_data = &profiles[0];
|
| + output.GetDataBuffer = ForwardUserData;
|
| + ResourceMessageCallParams params;
|
| + MockCompletionCallback cb;
|
| + int32_t result = encoder_iface()->GetSupportedProfiles(
|
| + encoder.get(), output, PP_MakeOptionalCompletionCallback(
|
| + &MockCompletionCallback::Callback, &cb));
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
|
| + ASSERT_TRUE(CheckGetSupportedProfilesMsg(¶ms));
|
| +
|
| + std::vector<PP_VideoProfileDescription> profiles_response;
|
| + PP_VideoProfileDescription profile;
|
| + profile.profile = PP_VIDEOPROFILE_H264MAIN;
|
| + profile.max_resolution.width = 1920;
|
| + profile.max_resolution.height = 1080;
|
| + profile.max_framerate_numerator = 30;
|
| + profile.max_framerate_denominator = 1;
|
| + profiles_response.push_back(profile);
|
| + profile.profile = PP_VIDEOPROFILE_VP8_ANY;
|
| + profile.max_resolution.width = 1920;
|
| + profile.max_resolution.height = 1080;
|
| + profile.max_framerate_numerator = 30;
|
| + profile.max_framerate_denominator = 1;
|
| + profiles_response.push_back(profile);
|
| +
|
| + SendGetSupportedProfilesReply(params, profiles_response);
|
| + ASSERT_EQ(PP_OK, cb.result());
|
| +
|
| + ASSERT_EQ(profiles_response.size(), profiles.size());
|
| + ASSERT_EQ(0, memcmp(&profiles[0], &profiles_response[0],
|
| + sizeof(PP_VideoProfileDescription) * 2));
|
| + }
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, InitializeFailure) {
|
| + {
|
| + // Verify the initialize callback is called in case of failure.
|
| + LockingResourceReleaser encoder(CreateEncoder());
|
| + ResourceMessageCallParams params;
|
| + PP_Size size = kSize;
|
| + MockCompletionCallback cb;
|
| + int32_t result = encoder_iface()->Initialize(
|
| + encoder.get(), PP_VIDEOFRAME_FORMAT_BGRA, &size,
|
| + PP_VIDEOPROFILE_H264MAIN, kBitrate,
|
| + PP_HARDWAREACCELERATION_WITHFALLBACK,
|
| + PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
|
| + &cb));
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
|
| +
|
| + PP_VideoFrame_Format input_format;
|
| + PP_Size input_visible_size;
|
| + PP_VideoProfile output_profile;
|
| + uint32_t bitrate;
|
| + PP_HardwareAcceleration acceleration;
|
| + ASSERT_TRUE(CheckInitializeMsg(¶ms, &input_format, &input_visible_size,
|
| + &output_profile, &bitrate, &acceleration));
|
| + ASSERT_EQ(PP_VIDEOFRAME_FORMAT_BGRA, input_format);
|
| + ASSERT_EQ(size.width, input_visible_size.width);
|
| + ASSERT_EQ(size.height, input_visible_size.height);
|
| + ASSERT_EQ(kBitrate, bitrate);
|
| + ASSERT_EQ(PP_VIDEOPROFILE_H264MAIN, output_profile);
|
| + ASSERT_EQ(PP_HARDWAREACCELERATION_WITHFALLBACK, acceleration);
|
| +
|
| + SendReply(params, PP_ERROR_NOTSUPPORTED,
|
| + PpapiPluginMsg_VideoEncoder_InitializeReply(0, 0, 0, size));
|
| + ASSERT_TRUE(cb.called());
|
| + ASSERT_EQ(PP_ERROR_NOTSUPPORTED, cb.result());
|
| + }
|
| + {
|
| + // Verify the initialize callback is called in case of error
|
| + // notification.
|
| + LockingResourceReleaser encoder(CreateEncoder());
|
| + ResourceMessageCallParams params;
|
| + PP_Size size = kSize;
|
| + MockCompletionCallback cb;
|
| + int32_t result = encoder_iface()->Initialize(
|
| + encoder.get(), PP_VIDEOFRAME_FORMAT_BGRA, &size,
|
| + PP_VIDEOPROFILE_H264MAIN, kBitrate,
|
| + PP_HARDWAREACCELERATION_WITHFALLBACK,
|
| + PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
|
| + &cb));
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
|
| +
|
| + PP_VideoFrame_Format input_format;
|
| + PP_Size input_visible_size;
|
| + PP_VideoProfile output_profile;
|
| + uint32_t bitrate;
|
| + PP_HardwareAcceleration acceleration;
|
| + ASSERT_TRUE(CheckInitializeMsg(¶ms, &input_format, &input_visible_size,
|
| + &output_profile, &bitrate, &acceleration));
|
| + ASSERT_EQ(PP_VIDEOFRAME_FORMAT_BGRA, input_format);
|
| + ASSERT_EQ(kSize.width, input_visible_size.width);
|
| + ASSERT_EQ(kSize.height, input_visible_size.height);
|
| + ASSERT_EQ(kBitrate, bitrate);
|
| + ASSERT_EQ(PP_VIDEOPROFILE_H264MAIN, output_profile);
|
| + ASSERT_EQ(PP_HARDWAREACCELERATION_WITHFALLBACK, acceleration);
|
| +
|
| + ResourceMessageCallParams error_params(encoder.get(), 0);
|
| + SendNotifyError(error_params, PP_ERROR_FAILED);
|
| + ASSERT_TRUE(cb.called());
|
| + ASSERT_EQ(PP_ERROR_FAILED, cb.result());
|
| + }
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, InitializeSuccess) {
|
| + {
|
| + // Verify the initialize callback is called when initialization is
|
| + // successfull.
|
| + LockingResourceReleaser encoder(CreateEncoder());
|
| + ResourceMessageCallParams params;
|
| + PP_Size size = kSize;
|
| + const uint32_t kBitrate = 420000;
|
| + MockCompletionCallback cb;
|
| + int32_t result = encoder_iface()->Initialize(
|
| + encoder.get(), PP_VIDEOFRAME_FORMAT_I420, &size,
|
| + PP_VIDEOPROFILE_H264MAIN, kBitrate,
|
| + PP_HARDWAREACCELERATION_WITHFALLBACK,
|
| + PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
|
| + &cb));
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
|
| +
|
| + PP_VideoFrame_Format input_format;
|
| + PP_Size input_visible_size;
|
| + PP_VideoProfile output_profile;
|
| + uint32_t bitrate;
|
| + PP_HardwareAcceleration acceleration;
|
| + ASSERT_TRUE(CheckInitializeMsg(¶ms, &input_format, &input_visible_size,
|
| + &output_profile, &bitrate, &acceleration));
|
| + ASSERT_EQ(PP_VIDEOFRAME_FORMAT_I420, input_format);
|
| + ASSERT_EQ(kSize.width, input_visible_size.width);
|
| + ASSERT_EQ(kSize.height, input_visible_size.height);
|
| + ASSERT_EQ(kBitrate, bitrate);
|
| + ASSERT_EQ(PP_VIDEOPROFILE_H264MAIN, output_profile);
|
| + ASSERT_EQ(PP_HARDWAREACCELERATION_WITHFALLBACK, acceleration);
|
| +
|
| + CreateBitstreamSharedMemory(kBitstreamBufferSize, kBitstreamBufferNb);
|
| + SendInitializeReply(params, kBitstreamBufferNb, kBitstreamBufferSize,
|
| + kVideoFrameNb, size);
|
| +
|
| + ASSERT_TRUE(cb.called());
|
| + ASSERT_EQ(PP_OK, cb.result());
|
| +
|
| + PP_Size coded_size;
|
| + CallGetFrameCodedSize(encoder.get(), &coded_size);
|
| + ASSERT_EQ(kSize.width, coded_size.width);
|
| + ASSERT_EQ(kSize.height, coded_size.height);
|
| + ASSERT_EQ(static_cast<int32_t>(kVideoFrameNb),
|
| + CallGetFramesRequired(encoder.get()));
|
| + }
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, Uninitialized) {
|
| + // Operations on uninitialized encoders should fail.
|
| + LockingResourceReleaser encoder(CreateEncoder());
|
| +
|
| + ASSERT_EQ(PP_ERROR_FAILED, CallGetFramesRequired(encoder.get()));
|
| +
|
| + PP_Size size;
|
| + ASSERT_EQ(PP_ERROR_FAILED, CallGetFrameCodedSize(encoder.get(), &size));
|
| +
|
| + MockCompletionCallback uncalled_cb;
|
| + PP_Resource video_frame = 0;
|
| + ASSERT_EQ(PP_ERROR_FAILED,
|
| + CallGetVideoFrame(encoder.get(), &video_frame, &uncalled_cb));
|
| + ASSERT_FALSE(uncalled_cb.called());
|
| + ASSERT_EQ(0, video_frame);
|
| +
|
| + ASSERT_EQ(PP_ERROR_FAILED,
|
| + CallEncode(encoder.get(), video_frame, PP_FALSE, &uncalled_cb));
|
| + ASSERT_FALSE(uncalled_cb.called());
|
| +
|
| + PP_BitstreamBuffer bitstream_buffer;
|
| + ASSERT_EQ(
|
| + PP_ERROR_FAILED,
|
| + CallGetBitstreamBuffer(encoder.get(), &bitstream_buffer, &uncalled_cb));
|
| + ASSERT_FALSE(uncalled_cb.called());
|
| +
|
| + ResourceMessageCallParams params;
|
| + uint32_t buffer_id;
|
| + CallRecycleBitstreamBuffer(encoder.get(), bitstream_buffer);
|
| + ASSERT_FALSE(CheckRecycleBitstreamBufferMsg(¶ms, &buffer_id));
|
| +
|
| + uint32_t bitrate, framerate;
|
| + CallRequestEncodingParametersChange(encoder.get(), 0, 0);
|
| + ASSERT_FALSE(
|
| + CheckRequestEncodeingParametersChangeMsg(¶ms, &bitrate, &framerate));
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, InitializeAndGetVideoFrame) {
|
| + // Verify that we can pull the right number of video frames before
|
| + // the proxy makes us wait.
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| + ResourceMessageCallParams params;
|
| + PP_Resource video_frames[kVideoFrameNb + 1];
|
| + MockCompletionCallback cb;
|
| +
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetVideoFrame(encoder.get(), &video_frames[0], &cb));
|
| + ASSERT_FALSE(cb.called());
|
| + ASSERT_TRUE(CheckGetVideoFramesMsg(¶ms));
|
| +
|
| + uint32_t frame_length = kSize.width * kSize.height * 2;
|
| + CreateVideoFramesSharedMemory(frame_length, kVideoFrameNb);
|
| + SendGetVideoFramesReply(params, kVideoFrameNb, frame_length, kSize);
|
| +
|
| + for (uint32_t i = 1; i < kVideoFrameNb; ++i) {
|
| + cb.Reset();
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetVideoFrame(encoder.get(), &video_frames[i], &cb));
|
| + ASSERT_TRUE(cb.called());
|
| + ASSERT_EQ(PP_OK, cb.result());
|
| + ASSERT_TRUE(CheckIsVideoFrame(video_frames[i]));
|
| + }
|
| +
|
| + cb.Reset();
|
| + ASSERT_EQ(
|
| + PP_OK_COMPLETIONPENDING,
|
| + CallGetVideoFrame(encoder.get(), &video_frames[kVideoFrameNb], &cb));
|
| + ASSERT_FALSE(cb.called());
|
| +
|
| + // Unlock the GetVideoFrame callback.
|
| + MockCompletionCallback encode_cb;
|
| + ASSERT_EQ(
|
| + PP_OK_COMPLETIONPENDING,
|
| + CallCompleteEncode(encoder.get(), video_frames[0], PP_FALSE, &encode_cb));
|
| + ASSERT_TRUE(encode_cb.called());
|
| + ASSERT_EQ(PP_OK, encode_cb.result());
|
| + ASSERT_TRUE(cb.called());
|
| +
|
| + {
|
| + ProxyAutoLock lock;
|
| + for (uint32_t i = 0; i < (kVideoFrameNb + 1); ++i)
|
| + PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(
|
| + video_frames[i]);
|
| + }
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, Encode) {
|
| + // Check Encode() calls into the renderer.
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| +
|
| + PP_Resource video_frame;
|
| + MockCompletionCallback get_frame_cb;
|
| + ;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallFirstGetVideoFrame(encoder.get(), &video_frame, &get_frame_cb));
|
| + ASSERT_TRUE(get_frame_cb.called());
|
| + ASSERT_EQ(PP_OK, get_frame_cb.result());
|
| +
|
| + MockCompletionCallback encode_cb;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallEncode(encoder.get(), video_frame, PP_TRUE, &encode_cb));
|
| + ASSERT_FALSE(encode_cb.called());
|
| +
|
| + ResourceMessageCallParams params;
|
| + uint32_t frame_id;
|
| + bool force_frame;
|
| + ASSERT_TRUE(CheckEncodeMsg(¶ms, &frame_id, &force_frame));
|
| +
|
| + SendEncodeReply(params, frame_id);
|
| +
|
| + ASSERT_TRUE(encode_cb.called());
|
| + ASSERT_EQ(PP_OK, encode_cb.result());
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, EncodeAndGetVideoFrame) {
|
| + // Check the encoding loop works well.
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| + ResourceMessageCallParams params;
|
| + PP_Resource video_frame;
|
| + MockCompletionCallback get_frame_cb, encode_cb;
|
| +
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallFirstGetVideoFrame(encoder.get(), &video_frame, &get_frame_cb));
|
| + ASSERT_TRUE(get_frame_cb.called());
|
| + ASSERT_EQ(PP_OK, get_frame_cb.result());
|
| +
|
| + for (uint32_t i = 1; i < 20 * kVideoFrameNb; ++i) {
|
| + encode_cb.Reset();
|
| + ASSERT_EQ(
|
| + PP_OK_COMPLETIONPENDING,
|
| + CallCompleteEncode(encoder.get(), video_frame, PP_FALSE, &encode_cb));
|
| + ASSERT_TRUE(encode_cb.called());
|
| + ASSERT_EQ(PP_OK, encode_cb.result());
|
| +
|
| + get_frame_cb.Reset();
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetVideoFrame(encoder.get(), &video_frame, &get_frame_cb));
|
| + ASSERT_TRUE(get_frame_cb.called());
|
| + ASSERT_EQ(PP_OK, get_frame_cb.result());
|
| + ASSERT_TRUE(CheckIsVideoFrame(video_frame));
|
| + }
|
| +
|
| + ASSERT_EQ(
|
| + PP_OK_COMPLETIONPENDING,
|
| + CallCompleteEncode(encoder.get(), video_frame, PP_FALSE, &encode_cb));
|
| + ASSERT_TRUE(encode_cb.called());
|
| + ASSERT_EQ(PP_OK, encode_cb.result());
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, GetBitstreamBuffer) {
|
| + // Verify that the GetBitstreamBuffer callback is fired whenever the
|
| + // renderer signals a buffer is available.
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| +
|
| + MockCompletionCallback get_bitstream_buffer_cb;
|
| + PP_BitstreamBuffer bitstream_buffer;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetBitstreamBuffer(encoder.get(), &bitstream_buffer,
|
| + &get_bitstream_buffer_cb));
|
| + ASSERT_FALSE(get_bitstream_buffer_cb.called());
|
| +
|
| + ResourceMessageCallParams buffer_params(encoder.get(), 0);
|
| + SendBitstreamBufferReady(buffer_params, 0, 10, true);
|
| +
|
| + ASSERT_TRUE(get_bitstream_buffer_cb.called());
|
| + ASSERT_EQ(PP_OK, get_bitstream_buffer_cb.result());
|
| + ASSERT_EQ(static_cast<uint32_t>(10), bitstream_buffer.size);
|
| + ASSERT_EQ(PP_TRUE, bitstream_buffer.key_frame);
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, RecycleBitstreamBuffer) {
|
| + // Verify that we signal the renderer that a bitstream buffer has been
|
| + // recycled.
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| +
|
| + MockCompletionCallback get_bitstream_buffer_cb;
|
| + PP_BitstreamBuffer bitstream_buffer;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetBitstreamBuffer(encoder.get(), &bitstream_buffer,
|
| + &get_bitstream_buffer_cb));
|
| + ASSERT_FALSE(get_bitstream_buffer_cb.called());
|
| +
|
| + ResourceMessageCallParams buffer_params(encoder.get(), 0);
|
| + SendBitstreamBufferReady(buffer_params, kBitstreamBufferNb - 1, 10, true);
|
| +
|
| + ASSERT_TRUE(get_bitstream_buffer_cb.called());
|
| + ASSERT_EQ(PP_OK, get_bitstream_buffer_cb.result());
|
| +
|
| + CallRecycleBitstreamBuffer(encoder.get(), bitstream_buffer);
|
| +
|
| + ResourceMessageCallParams recycle_params;
|
| + uint32_t buffer_id;
|
| + ASSERT_TRUE(CheckRecycleBitstreamBufferMsg(&recycle_params, &buffer_id));
|
| + ASSERT_EQ(kBitstreamBufferNb - 1, buffer_id);
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, RequestEncodingParametersChange) {
|
| + // Check encoding parameter changes are correctly sent to the
|
| + // renderer.
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| +
|
| + CallRequestEncodingParametersChange(encoder.get(), 1, 2);
|
| + ResourceMessageCallParams params;
|
| + uint32_t bitrate, framerate;
|
| + ASSERT_TRUE(
|
| + CheckRequestEncodeingParametersChangeMsg(¶ms, &bitrate, &framerate));
|
| + ASSERT_EQ(static_cast<uint32_t>(1), bitrate);
|
| + ASSERT_EQ(static_cast<uint32_t>(2), framerate);
|
| +}
|
| +
|
| +TEST_F(VideoEncoderResourceTest, NotifyError) {
|
| + // Check an error from the encoder calls back GetVideoFrame and
|
| + // GetBitstreamBuffer callbacks.
|
| + {
|
| + LockingResourceReleaser encoder(CreateAndInitializeEncoder());
|
| +
|
| + MockCompletionCallback get_frame_cb1;
|
| + PP_Resource video_frame1;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetVideoFrame(encoder.get(), &video_frame1, &get_frame_cb1));
|
| + ASSERT_FALSE(get_frame_cb1.called());
|
| + MockCompletionCallback get_frame_cb2;
|
| + PP_Resource video_frame2;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetVideoFrame(encoder.get(), &video_frame2, &get_frame_cb2));
|
| + ASSERT_FALSE(get_frame_cb2.called());
|
| +
|
| + MockCompletionCallback get_bitstream_buffer_cb;
|
| + PP_BitstreamBuffer bitstream_buffer;
|
| + ASSERT_EQ(PP_OK_COMPLETIONPENDING,
|
| + CallGetBitstreamBuffer(encoder.get(), &bitstream_buffer,
|
| + &get_bitstream_buffer_cb));
|
| +
|
| + ResourceMessageCallParams error_params(encoder.get(), 0);
|
| + SendNotifyError(error_params, PP_ERROR_FAILED);
|
| +
|
| + ASSERT_TRUE(get_frame_cb1.called());
|
| + ASSERT_EQ(PP_ERROR_FAILED, get_frame_cb1.result());
|
| + ASSERT_TRUE(get_frame_cb2.called());
|
| + ASSERT_EQ(PP_ERROR_FAILED, get_frame_cb2.result());
|
| + ASSERT_TRUE(get_bitstream_buffer_cb.called());
|
| + ASSERT_EQ(PP_ERROR_FAILED, get_bitstream_buffer_cb.result());
|
| + }
|
| +}
|
| +
|
| +} // namespace proxy
|
| +} // namespace ppapi
|
|
|