|  | // 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 <stdint.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/memory/unsafe_shared_memory_region.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/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 { | 
|  |  | 
|  | class MockCompletionCallback { | 
|  | public: | 
|  | MockCompletionCallback() | 
|  | : called_(false), | 
|  | call_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
|  | base::WaitableEvent::InitialState::NOT_SIGNALED) {} | 
|  |  | 
|  | 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 MediaStreamBufferManager::Delegate { | 
|  | public: | 
|  | VideoEncoderResourceTest() | 
|  | : encoder_iface_(thunk::GetPPB_VideoEncoder_0_2_Thunk()), | 
|  | encoder_iface_0_1_(thunk::GetPPB_VideoEncoder_0_1_Thunk()), | 
|  | video_frames_manager_(this) {} | 
|  | ~VideoEncoderResourceTest() override {} | 
|  |  | 
|  | const PPB_VideoEncoder_0_2* encoder_iface() const { return encoder_iface_; } | 
|  | const PPB_VideoEncoder_0_1* encoder_iface_0_1() const { | 
|  | return encoder_iface_0_1_; | 
|  | } | 
|  |  | 
|  | const uint32_t kBitstreamBufferSize = 4000; | 
|  | const uint32_t kBitstreamBufferCount = 5; | 
|  | const uint32_t kVideoFrameCount = 3; | 
|  | const uint32_t kBitrate = 200000; | 
|  |  | 
|  | const PP_Size kFrameSize = PP_MakeSize(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, | 
|  | SerializedHandle handle) { | 
|  | ResourceMessageReplyParams reply_params(params.pp_resource(), | 
|  | params.sequence()); | 
|  | reply_params.set_result(result); | 
|  | reply_params.AppendHandle(std::move(handle)); | 
|  | PluginMessageFilter::DispatchResourceReplyForTest(reply_params, | 
|  | nested_message); | 
|  | } | 
|  |  | 
|  | void SendReplyWithHandles(const ResourceMessageCallParams& params, | 
|  | int32_t result, | 
|  | const IPC::Message& nested_message, | 
|  | std::vector<SerializedHandle> handles) { | 
|  | ResourceMessageReplyParams reply_params(params.pp_resource(), | 
|  | params.sequence()); | 
|  | reply_params.set_result(result); | 
|  | for (auto& handle : handles) | 
|  | reply_params.AppendHandle(std::move(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) { | 
|  | base::UnsafeSharedMemoryRegion region = | 
|  | base::UnsafeSharedMemoryRegion::Create(buffer_size); | 
|  | ASSERT_TRUE(region.IsValid()); | 
|  | shared_memory_bitstreams_.push_back(std::move(region)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CreateVideoFramesSharedMemory(uint32_t frame_length, | 
|  | uint32_t frame_count) { | 
|  | uint32_t buffer_length = | 
|  | frame_length + sizeof(ppapi::MediaStreamBuffer::Video); | 
|  | base::UnsafeSharedMemoryRegion shared_memory_frames = | 
|  | base::UnsafeSharedMemoryRegion::Create(buffer_length * frame_count); | 
|  | ASSERT_TRUE(shared_memory_frames.IsValid()); | 
|  | ASSERT_TRUE(video_frames_manager_.SetBuffers( | 
|  | frame_count, buffer_length, std::move(shared_memory_frames), true)); | 
|  | for (int32_t i = 0; i < video_frames_manager_.number_of_buffers(); ++i) { | 
|  | ppapi::MediaStreamBuffer::Video* buffer = | 
|  | &(video_frames_manager_.GetBufferPointer(i)->video); | 
|  | buffer->header.size = buffer_length; | 
|  | buffer->header.type = ppapi::MediaStreamBuffer::TYPE_VIDEO; | 
|  | buffer->format = PP_VIDEOFRAME_FORMAT_I420; | 
|  | buffer->size = kFrameSize; | 
|  | buffer->data_size = frame_length; | 
|  | } | 
|  | } | 
|  |  | 
|  | PP_Resource CreateAndInitializeEncoder() { | 
|  | PP_Resource encoder = CreateEncoder(); | 
|  | PP_Size size = kFrameSize; | 
|  | 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(); | 
|  |  | 
|  | SendInitializeReply(params, PP_OK, kVideoFrameCount, kFrameSize); | 
|  | CreateBitstreamSharedMemory(kBitstreamBufferSize, kBitstreamBufferCount); | 
|  | SendBitstreamBuffers(params, kBitstreamBufferSize); | 
|  |  | 
|  | 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 = kFrameSize.width * kFrameSize.height * 2; | 
|  | CreateVideoFramesSharedMemory(frame_length, kVideoFrameCount); | 
|  | SendGetVideoFramesReply(params, kVideoFrameCount, frame_length, kFrameSize); | 
|  |  | 
|  | 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 CallClose(PP_Resource pp_encoder) { | 
|  | encoder_iface()->Close(pp_encoder); | 
|  | } | 
|  |  | 
|  | void SendGetSupportedProfilesReply( | 
|  | const ResourceMessageCallParams& params, | 
|  | const std::vector<PP_VideoProfileDescription>& profiles) { | 
|  | SendReply(params, PP_OK, | 
|  | PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply(profiles)); | 
|  | } | 
|  |  | 
|  | void SendInitializeReply(const ResourceMessageCallParams& params, | 
|  | int32_t success, | 
|  | uint32_t input_frame_count, | 
|  | const PP_Size& input_coded_size) { | 
|  | SendReply(params, success, PpapiPluginMsg_VideoEncoder_InitializeReply( | 
|  | input_frame_count, input_coded_size)); | 
|  | } | 
|  |  | 
|  | void SendBitstreamBuffers(const ResourceMessageCallParams& params, | 
|  | uint32_t buffer_length) { | 
|  | std::vector<SerializedHandle> handles; | 
|  | for (const auto& mem : shared_memory_bitstreams_) { | 
|  | ASSERT_EQ(mem.GetSize(), buffer_length); | 
|  | base::UnsafeSharedMemoryRegion region = mem.Duplicate(); | 
|  | ASSERT_TRUE(region.IsValid()); | 
|  | handles.push_back(SerializedHandle(std::move(region))); | 
|  | } | 
|  | SendReplyWithHandles( | 
|  | params, PP_OK, | 
|  | PpapiPluginMsg_VideoEncoder_BitstreamBuffers(buffer_length), | 
|  | std::move(handles)); | 
|  | } | 
|  |  | 
|  | void SendGetVideoFramesReply(const ResourceMessageCallParams& params, | 
|  | uint32_t frame_count, | 
|  | uint32_t frame_length, | 
|  | const PP_Size& size) { | 
|  | base::UnsafeSharedMemoryRegion region = | 
|  | video_frames_manager_.region().Duplicate(); | 
|  | ASSERT_TRUE(region.IsValid()); | 
|  | SendReplyWithHandle( | 
|  | params, PP_OK, | 
|  | PpapiPluginMsg_VideoEncoder_GetVideoFramesReply( | 
|  | frame_count, frame_length + sizeof(MediaStreamBuffer::Video), size), | 
|  | SerializedHandle(std::move(region))); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | return sink().GetFirstResourceCallMatching( | 
|  | PpapiHostMsg_VideoEncoder_GetSupportedProfiles::ID, params, &msg); | 
|  | } | 
|  |  | 
|  | 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 CheckRequestEncodingParametersChangeMsg( | 
|  | 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); | 
|  | } | 
|  |  | 
|  | bool CheckIsVideoFrameValid(PP_Resource video_frame) { | 
|  | PP_Size frame_size; | 
|  | return thunk::GetPPB_VideoFrame_0_1_Thunk()->GetSize( | 
|  | video_frame, &frame_size) == PP_TRUE; | 
|  | } | 
|  |  | 
|  | private: | 
|  | // MediaStreamBufferManager::Delegate: | 
|  | void OnNewBufferEnqueued() override {} | 
|  |  | 
|  | const PPB_VideoEncoder_0_2* encoder_iface_; | 
|  | const PPB_VideoEncoder_0_1* encoder_iface_0_1_; | 
|  |  | 
|  | std::vector<base::UnsafeSharedMemoryRegion> shared_memory_bitstreams_; | 
|  |  | 
|  | MediaStreamBufferManager video_frames_manager_; | 
|  | }; | 
|  |  | 
|  | 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()); | 
|  | PP_VideoProfileDescription profiles[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; | 
|  | profile.hardware_accelerated = PP_TRUE; | 
|  | 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; | 
|  | profile.hardware_accelerated = PP_FALSE; | 
|  | profiles_response.push_back(profile); | 
|  |  | 
|  | SendGetSupportedProfilesReply(params, profiles_response); | 
|  | ASSERT_EQ(profiles_response.size(), static_cast<uint32_t>(cb.result())); | 
|  | ASSERT_EQ(0, memcmp(&profiles[0], &profiles_response[0], sizeof(profiles))); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(VideoEncoderResourceTest, GetSupportedProfiles0_1) { | 
|  | // Verifies that GetSupportedProfiles calls into the renderer and | 
|  | // the we get the right results back. | 
|  | { | 
|  | LockingResourceReleaser encoder(CreateEncoder()); | 
|  | PP_VideoProfileDescription_0_1 profiles[2]; | 
|  | PP_ArrayOutput output; | 
|  | output.user_data = &profiles[0]; | 
|  | output.GetDataBuffer = ForwardUserData; | 
|  | ResourceMessageCallParams params; | 
|  | MockCompletionCallback cb; | 
|  | int32_t result = encoder_iface_0_1()->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; | 
|  | profile.hardware_accelerated = PP_TRUE; | 
|  | 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; | 
|  | profile.hardware_accelerated = PP_FALSE; | 
|  | profiles_response.push_back(profile); | 
|  |  | 
|  | SendGetSupportedProfilesReply(params, profiles_response); | 
|  |  | 
|  | ASSERT_EQ(profiles_response.size(), static_cast<uint32_t>(cb.result())); | 
|  |  | 
|  | for (uint32_t i = 0; i < profiles_response.size(); i++) { | 
|  | ASSERT_EQ(profiles_response[i].profile, profiles[i].profile); | 
|  | ASSERT_EQ(profiles_response[i].max_resolution.width, | 
|  | profiles[i].max_resolution.width); | 
|  | ASSERT_EQ(profiles_response[i].max_resolution.height, | 
|  | profiles[i].max_resolution.height); | 
|  | ASSERT_EQ(profiles_response[i].max_framerate_numerator, | 
|  | profiles[i].max_framerate_numerator); | 
|  | ASSERT_EQ(profiles_response[i].max_framerate_denominator, | 
|  | profiles[i].max_framerate_denominator); | 
|  | if (profiles_response[i].hardware_accelerated) | 
|  | ASSERT_EQ(PP_HARDWAREACCELERATION_ONLY, profiles[i].acceleration); | 
|  | else | 
|  | ASSERT_EQ(PP_HARDWAREACCELERATION_NONE, profiles[i].acceleration); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(VideoEncoderResourceTest, InitializeFailure) { | 
|  | { | 
|  | // Verify the initialize callback is called in case of failure. | 
|  | LockingResourceReleaser encoder(CreateEncoder()); | 
|  | ResourceMessageCallParams params; | 
|  | PP_Size size = kFrameSize; | 
|  | 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); | 
|  |  | 
|  | SendInitializeReply(params, PP_ERROR_NOTSUPPORTED, kVideoFrameCount, | 
|  | kFrameSize); | 
|  | 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 = kFrameSize; | 
|  | 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(kFrameSize.width, input_visible_size.width); | 
|  | ASSERT_EQ(kFrameSize.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()); | 
|  | } | 
|  | { | 
|  | // Verify that calling initialize twice fails the second time if | 
|  | // we haven't received a response yet. | 
|  | LockingResourceReleaser encoder(CreateEncoder()); | 
|  | ResourceMessageCallParams params; | 
|  | PP_Size size = kFrameSize; | 
|  | 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); | 
|  |  | 
|  | 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_ERROR_INPROGRESS, result); | 
|  |  | 
|  | 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 | 
|  | // successful. | 
|  | LockingResourceReleaser encoder(CreateEncoder()); | 
|  | ResourceMessageCallParams params; | 
|  | PP_Size size = kFrameSize; | 
|  | 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(kFrameSize.width, input_visible_size.width); | 
|  | ASSERT_EQ(kFrameSize.height, input_visible_size.height); | 
|  | ASSERT_EQ(kBitrate, bitrate); | 
|  | ASSERT_EQ(PP_VIDEOPROFILE_H264MAIN, output_profile); | 
|  | ASSERT_EQ(PP_HARDWAREACCELERATION_WITHFALLBACK, acceleration); | 
|  |  | 
|  | SendInitializeReply(params, PP_OK, kVideoFrameCount, kFrameSize); | 
|  |  | 
|  | ASSERT_TRUE(cb.called()); | 
|  | ASSERT_EQ(PP_OK, cb.result()); | 
|  | } | 
|  | { | 
|  | // Verify that calling initialize a second time, after it already | 
|  | // succeeded, fails. | 
|  | LockingResourceReleaser encoder(CreateEncoder()); | 
|  | ResourceMessageCallParams params; | 
|  | PP_Size size = kFrameSize; | 
|  | 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(kFrameSize.width, input_visible_size.width); | 
|  | ASSERT_EQ(kFrameSize.height, input_visible_size.height); | 
|  | ASSERT_EQ(kBitrate, bitrate); | 
|  | ASSERT_EQ(PP_VIDEOPROFILE_H264MAIN, output_profile); | 
|  | ASSERT_EQ(PP_HARDWAREACCELERATION_WITHFALLBACK, acceleration); | 
|  |  | 
|  | SendInitializeReply(params, PP_OK, kVideoFrameCount, kFrameSize); | 
|  |  | 
|  | ASSERT_TRUE(cb.called()); | 
|  | ASSERT_EQ(PP_OK, cb.result()); | 
|  |  | 
|  | 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_ERROR_FAILED, result); | 
|  | } | 
|  | { | 
|  | // Verify the sending the bitstream buffers details makes them | 
|  | // available through the API. the right values. | 
|  | LockingResourceReleaser encoder(CreateEncoder()); | 
|  | ResourceMessageCallParams params; | 
|  | PP_Size size = kFrameSize; | 
|  | 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(kFrameSize.width, input_visible_size.width); | 
|  | ASSERT_EQ(kFrameSize.height, input_visible_size.height); | 
|  | ASSERT_EQ(kBitrate, bitrate); | 
|  | ASSERT_EQ(PP_VIDEOPROFILE_H264MAIN, output_profile); | 
|  | ASSERT_EQ(PP_HARDWAREACCELERATION_WITHFALLBACK, acceleration); | 
|  |  | 
|  | SendInitializeReply(params, PP_OK, kVideoFrameCount, kFrameSize); | 
|  |  | 
|  | ASSERT_TRUE(cb.called()); | 
|  | ASSERT_EQ(PP_OK, cb.result()); | 
|  |  | 
|  | PP_Size coded_size; | 
|  | ASSERT_EQ(PP_OK, CallGetFrameCodedSize(encoder.get(), &coded_size)); | 
|  | ASSERT_EQ(kFrameSize.width, coded_size.width); | 
|  | ASSERT_EQ(kFrameSize.height, coded_size.height); | 
|  | ASSERT_EQ(static_cast<int32_t>(kVideoFrameCount), | 
|  | 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( | 
|  | CheckRequestEncodingParametersChangeMsg(¶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; | 
|  | std::vector<PP_Resource> video_frames; | 
|  | MockCompletionCallback get_frame_cb; | 
|  |  | 
|  | video_frames.resize(kVideoFrameCount + 1); | 
|  |  | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallGetVideoFrame(encoder.get(), &video_frames[0], &get_frame_cb)); | 
|  | ASSERT_FALSE(get_frame_cb.called()); | 
|  | ASSERT_TRUE(CheckGetVideoFramesMsg(¶ms)); | 
|  |  | 
|  | uint32_t frame_length = kFrameSize.width * kFrameSize.height * 2; | 
|  | CreateVideoFramesSharedMemory(frame_length, kVideoFrameCount); | 
|  | SendGetVideoFramesReply(params, kVideoFrameCount, frame_length, kFrameSize); | 
|  |  | 
|  | for (uint32_t i = 1; i < kVideoFrameCount; ++i) { | 
|  | get_frame_cb.Reset(); | 
|  | ASSERT_EQ( | 
|  | PP_OK_COMPLETIONPENDING, | 
|  | CallGetVideoFrame(encoder.get(), &video_frames[i], &get_frame_cb)); | 
|  | ASSERT_TRUE(get_frame_cb.called()); | 
|  | ASSERT_EQ(PP_OK, get_frame_cb.result()); | 
|  | ASSERT_TRUE(CheckIsVideoFrame(video_frames[i])); | 
|  | } | 
|  |  | 
|  | get_frame_cb.Reset(); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallGetVideoFrame(encoder.get(), &video_frames[kVideoFrameCount], | 
|  | &get_frame_cb)); | 
|  | ASSERT_FALSE(get_frame_cb.called()); | 
|  |  | 
|  | MockCompletionCallback get_frame_fail_cb; | 
|  | ASSERT_EQ(PP_ERROR_INPROGRESS, | 
|  | CallGetVideoFrame(encoder.get(), &video_frames[kVideoFrameCount], | 
|  | &get_frame_fail_cb)); | 
|  | ASSERT_FALSE(get_frame_fail_cb.called()); | 
|  |  | 
|  | // Unblock the GetVideoFrame callback by freeing up a frame. | 
|  | 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(get_frame_cb.called()); | 
|  |  | 
|  | CallClose(encoder.get()); | 
|  | } | 
|  |  | 
|  | 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()); | 
|  | ASSERT_FALSE(CheckIsVideoFrameValid(video_frame)); | 
|  |  | 
|  | 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 * kVideoFrameCount; ++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(10U, bitstream_buffer.size); | 
|  | ASSERT_EQ(PP_TRUE, bitstream_buffer.key_frame); | 
|  | } | 
|  | { | 
|  | // Verify that calling GetBitstreamBuffer a second time, while the | 
|  | // first callback hasn't been fired fails. | 
|  | 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()); | 
|  |  | 
|  | ASSERT_EQ(PP_ERROR_INPROGRESS, | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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, kBitstreamBufferCount - 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(kBitstreamBufferCount - 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( | 
|  | CheckRequestEncodingParametersChangeMsg(¶ms, &bitrate, &framerate)); | 
|  | ASSERT_EQ(1U, bitrate); | 
|  | ASSERT_EQ(2U, framerate); | 
|  | } | 
|  |  | 
|  | TEST_F(VideoEncoderResourceTest, NotifyError) { | 
|  | { | 
|  | // Check an error from the encoder aborts GetVideoFrame and | 
|  | // GetBitstreamBuffer callbacks. | 
|  | LockingResourceReleaser encoder(CreateAndInitializeEncoder()); | 
|  |  | 
|  | MockCompletionCallback get_frame_cb; | 
|  | PP_Resource video_frame; | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallGetVideoFrame(encoder.get(), &video_frame, &get_frame_cb)); | 
|  | ASSERT_FALSE(get_frame_cb.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_cb.called()); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, get_frame_cb.result()); | 
|  | ASSERT_TRUE(get_bitstream_buffer_cb.called()); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, get_bitstream_buffer_cb.result()); | 
|  | } | 
|  | { | 
|  | // Check an error from the encoder aborts Encode and GetBitstreamBuffer | 
|  | // callbacks. | 
|  | LockingResourceReleaser encoder(CreateAndInitializeEncoder()); | 
|  |  | 
|  | MockCompletionCallback get_frame_cb, encode_cb1, encode_cb2; | 
|  | PP_Resource video_frame1, video_frame2; | 
|  | ASSERT_EQ( | 
|  | PP_OK_COMPLETIONPENDING, | 
|  | CallFirstGetVideoFrame(encoder.get(), &video_frame1, &get_frame_cb)); | 
|  | ASSERT_TRUE(get_frame_cb.called()); | 
|  | ASSERT_EQ(PP_OK, get_frame_cb.result()); | 
|  |  | 
|  | get_frame_cb.Reset(); | 
|  | ASSERT_EQ( | 
|  | PP_OK_COMPLETIONPENDING, | 
|  | CallFirstGetVideoFrame(encoder.get(), &video_frame2, &get_frame_cb)); | 
|  | ASSERT_TRUE(get_frame_cb.called()); | 
|  | ASSERT_EQ(PP_OK, get_frame_cb.result()); | 
|  |  | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallEncode(encoder.get(), video_frame1, PP_FALSE, &encode_cb1)); | 
|  | ASSERT_FALSE(encode_cb1.called()); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallEncode(encoder.get(), video_frame2, PP_FALSE, &encode_cb2)); | 
|  | ASSERT_FALSE(encode_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(encode_cb1.called()); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, encode_cb1.result()); | 
|  | ASSERT_TRUE(encode_cb2.called()); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, encode_cb2.result()); | 
|  | ASSERT_TRUE(get_bitstream_buffer_cb.called()); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, get_bitstream_buffer_cb.result()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(VideoEncoderResourceTest, Close) { | 
|  | { | 
|  | // Check closing the encoder aborts GetVideoFrame and | 
|  | // GetBitstreamBuffer callbacks. | 
|  | LockingResourceReleaser encoder(CreateAndInitializeEncoder()); | 
|  |  | 
|  | MockCompletionCallback get_frame_cb; | 
|  | PP_Resource video_frame; | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallGetVideoFrame(encoder.get(), &video_frame, &get_frame_cb)); | 
|  | ASSERT_FALSE(get_frame_cb.called()); | 
|  |  | 
|  | MockCompletionCallback get_bitstream_buffer_cb; | 
|  | PP_BitstreamBuffer bitstream_buffer; | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallGetBitstreamBuffer(encoder.get(), &bitstream_buffer, | 
|  | &get_bitstream_buffer_cb)); | 
|  |  | 
|  | CallClose(encoder.get()); | 
|  |  | 
|  | ASSERT_TRUE(get_frame_cb.called()); | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, get_frame_cb.result()); | 
|  | ASSERT_TRUE(get_bitstream_buffer_cb.called()); | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, get_bitstream_buffer_cb.result()); | 
|  | } | 
|  | { | 
|  | // Check closing the encoder aborts Encode and GetBitstreamBuffer | 
|  | // callbacks. | 
|  | LockingResourceReleaser encoder(CreateAndInitializeEncoder()); | 
|  |  | 
|  | MockCompletionCallback get_frame_cb, encode_cb1, encode_cb2; | 
|  | PP_Resource video_frame1, video_frame2; | 
|  | ASSERT_EQ( | 
|  | PP_OK_COMPLETIONPENDING, | 
|  | CallFirstGetVideoFrame(encoder.get(), &video_frame1, &get_frame_cb)); | 
|  | ASSERT_TRUE(get_frame_cb.called()); | 
|  | ASSERT_EQ(PP_OK, get_frame_cb.result()); | 
|  |  | 
|  | get_frame_cb.Reset(); | 
|  | ASSERT_EQ( | 
|  | PP_OK_COMPLETIONPENDING, | 
|  | CallFirstGetVideoFrame(encoder.get(), &video_frame2, &get_frame_cb)); | 
|  | ASSERT_TRUE(get_frame_cb.called()); | 
|  | ASSERT_EQ(PP_OK, get_frame_cb.result()); | 
|  |  | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallEncode(encoder.get(), video_frame1, PP_FALSE, &encode_cb1)); | 
|  | ASSERT_FALSE(encode_cb1.called()); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, | 
|  | CallEncode(encoder.get(), video_frame2, PP_FALSE, &encode_cb2)); | 
|  | ASSERT_FALSE(encode_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)); | 
|  |  | 
|  | CallClose(encoder.get()); | 
|  |  | 
|  | ASSERT_TRUE(encode_cb1.called()); | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, encode_cb1.result()); | 
|  | ASSERT_TRUE(encode_cb2.called()); | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, encode_cb2.result()); | 
|  | ASSERT_TRUE(get_bitstream_buffer_cb.called()); | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, get_bitstream_buffer_cb.result()); | 
|  |  | 
|  | // Verify that a remaining encode response from the renderer is | 
|  | // discarded. | 
|  | ResourceMessageCallParams params; | 
|  | uint32_t frame_id; | 
|  | bool force_frame; | 
|  | ASSERT_TRUE(CheckEncodeMsg(¶ms, &frame_id, &force_frame)); | 
|  | SendEncodeReply(params, frame_id); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace proxy | 
|  | }  // namespace ppapi |