| // 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/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/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) { |
| std::unique_ptr<base::SharedMemory> mem(new base::SharedMemory()); |
| ASSERT_TRUE(mem->CreateAnonymous(buffer_size)); |
| shared_memory_bitstreams_.push_back(std::move(mem)); |
| } |
| } |
| |
| void CreateVideoFramesSharedMemory(uint32_t frame_length, |
| uint32_t frame_count) { |
| std::unique_ptr<base::SharedMemory> shared_memory_frames( |
| new base::SharedMemory()); |
| uint32_t buffer_length = |
| frame_length + sizeof(ppapi::MediaStreamBuffer::Video); |
| ASSERT_TRUE(shared_memory_frames->CreateAnonymous(buffer_length * |
| frame_count)); |
| 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->requested_size(), buffer_length); |
| base::SharedMemoryHandle handle = mem->handle().Duplicate(); |
| ASSERT_TRUE(handle.IsValid()); |
| handles.push_back(SerializedHandle(handle, buffer_length)); |
| } |
| 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::SharedMemoryHandle handle = |
| video_frames_manager_.shm()->handle().Duplicate(); |
| ASSERT_TRUE(handle.IsValid()); |
| SendReplyWithHandle( |
| params, PP_OK, PpapiPluginMsg_VideoEncoder_GetVideoFramesReply( |
| frame_count, |
| frame_length + sizeof(MediaStreamBuffer::Video), |
| size), |
| SerializedHandle( |
| handle, |
| static_cast<uint32_t>( |
| video_frames_manager_.shm()->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; |
| 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<std::unique_ptr<base::SharedMemory>> 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 |