diff options
author | lionel.g.landwerlin <lionel.g.landwerlin@intel.com> | 2015-02-24 17:20:58 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-25 01:21:35 +0000 |
commit | c3f15126607423812eaefe48ecdcb0ffd39c5e05 (patch) | |
tree | 1341d9f852962301318019b407aefa3c807ce8f0 /ppapi | |
parent | 3c3372a6f65be1bf0d0d673cf3361798bacd607a (diff) | |
download | chromium_src-c3f15126607423812eaefe48ecdcb0ffd39c5e05.zip chromium_src-c3f15126607423812eaefe48ecdcb0ffd39c5e05.tar.gz chromium_src-c3f15126607423812eaefe48ecdcb0ffd39c5e05.tar.bz2 |
Pepper: PPB_VideoEncoder implementation
BUG=455409
TEST=run ./ppapi_unittests --gtest_filter=VideoEncoderResourceTest
Review URL: https://codereview.chromium.org/905023005
Cr-Commit-Position: refs/heads/master@{#317938}
Diffstat (limited to 'ppapi')
-rw-r--r-- | ppapi/ppapi_sources.gypi | 2 | ||||
-rw-r--r-- | ppapi/ppapi_tests.gypi | 1 | ||||
-rw-r--r-- | ppapi/proxy/ppapi_messages.h | 49 | ||||
-rw-r--r-- | ppapi/proxy/video_encoder_resource.cc | 378 | ||||
-rw-r--r-- | ppapi/proxy/video_encoder_resource.h | 101 | ||||
-rw-r--r-- | ppapi/proxy/video_encoder_resource_unittest.cc | 1085 | ||||
-rw-r--r-- | ppapi/tests/test_video_encoder.cc | 41 | ||||
-rw-r--r-- | ppapi/tests/test_video_encoder.h | 29 |
8 files changed, 1670 insertions, 16 deletions
diff --git a/ppapi/ppapi_sources.gypi b/ppapi/ppapi_sources.gypi index e182b05..3536453 100644 --- a/ppapi/ppapi_sources.gypi +++ b/ppapi/ppapi_sources.gypi @@ -513,6 +513,8 @@ 'tests/test_video_decoder.h', 'tests/test_video_destination.cc', 'tests/test_video_destination.h', + 'tests/test_video_encoder.cc', + 'tests/test_video_encoder.h', 'tests/test_video_source.cc', 'tests/test_video_source.h', 'tests/test_view.cc', diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi index 0e5de16..562903d 100644 --- a/ppapi/ppapi_tests.gypi +++ b/ppapi/ppapi_tests.gypi @@ -175,6 +175,7 @@ 'proxy/serialized_var_unittest.cc', 'proxy/talk_resource_unittest.cc', 'proxy/video_decoder_resource_unittest.cc', + 'proxy/video_encoder_resource_unittest.cc', 'proxy/websocket_resource_unittest.cc', 'shared_impl/media_stream_audio_track_shared_unittest.cc', 'shared_impl/media_stream_buffer_manager_unittest.cc', diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 243e783..643d8f9 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -41,6 +41,7 @@ #include "ppapi/c/ppb_tcp_socket.h" #include "ppapi/c/ppb_text_input_controller.h" #include "ppapi/c/ppb_udp_socket.h" +#include "ppapi/c/ppb_video_encoder.h" #include "ppapi/c/private/pp_content_decryptor.h" #include "ppapi/c/private/pp_private_font_charset.h" #include "ppapi/c/private/pp_video_capture_format.h" @@ -443,6 +444,14 @@ IPC_STRUCT_TRAITS_BEGIN(ppapi::PpapiNaClPluginArgs) IPC_STRUCT_TRAITS_MEMBER(switch_values) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(PP_VideoProfileDescription) +IPC_STRUCT_TRAITS_MEMBER(profile) +IPC_STRUCT_TRAITS_MEMBER(max_resolution) +IPC_STRUCT_TRAITS_MEMBER(max_framerate_numerator) +IPC_STRUCT_TRAITS_MEMBER(max_framerate_denominator) +IPC_STRUCT_TRAITS_MEMBER(acceleration) +IPC_STRUCT_TRAITS_END() + #if !defined(OS_NACL) && !defined(NACL_WIN64) IPC_STRUCT_TRAITS_BEGIN(ppapi::proxy::PPPDecryptor_Buffer) @@ -1992,6 +2001,46 @@ IPC_MESSAGE_CONTROL0(PpapiPluginMsg_VideoDecoder_ResetReply) IPC_MESSAGE_CONTROL1(PpapiPluginMsg_VideoDecoder_NotifyError, int32_t /* error */) +// VideoEncoder ------------------------------------------------------ + +IPC_MESSAGE_CONTROL0(PpapiHostMsg_VideoEncoder_Create) +IPC_MESSAGE_CONTROL0(PpapiHostMsg_VideoEncoder_GetSupportedProfiles) +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply, + std::vector<PP_VideoProfileDescription> /* results */) +IPC_MESSAGE_CONTROL5(PpapiHostMsg_VideoEncoder_Initialize, + PP_VideoFrame_Format /* input_format */, + PP_Size /* input_visible_size */, + PP_VideoProfile /* output_profile */, + uint32_t /* initial_bitrate */, + PP_HardwareAcceleration /* acceleration */) +IPC_MESSAGE_CONTROL2(PpapiPluginMsg_VideoEncoder_InitializeReply, + uint32_t /* input_frame_count */, + PP_Size /* input_coded_size */) +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_VideoEncoder_BitstreamBuffers, + uint32_t /* buffer_length */) +IPC_MESSAGE_CONTROL0(PpapiHostMsg_VideoEncoder_GetVideoFrames) +IPC_MESSAGE_CONTROL3(PpapiPluginMsg_VideoEncoder_GetVideoFramesReply, + uint32_t /* frame_count */, + uint32_t /* frame_length */, + PP_Size /* frame_size */) +IPC_MESSAGE_CONTROL2(PpapiHostMsg_VideoEncoder_Encode, + uint32_t /* frame_id */, + bool /* force_keyframe */) +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_VideoEncoder_EncodeReply, + uint32_t /* frame_id */) +IPC_MESSAGE_CONTROL3(PpapiPluginMsg_VideoEncoder_BitstreamBufferReady, + uint32_t /* buffer_id */, + uint32_t /* buffer_size */, + bool /* key_frame */) +IPC_MESSAGE_CONTROL1(PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer, + uint32_t /* buffer_id */) +IPC_MESSAGE_CONTROL2(PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange, + uint32_t /* bitrate */, + uint32_t /* framerate */) +IPC_MESSAGE_CONTROL1(PpapiPluginMsg_VideoEncoder_NotifyError, + int32_t /* error */) +IPC_MESSAGE_CONTROL0(PpapiHostMsg_VideoEncoder_Close) + #if !defined(OS_NACL) && !defined(NACL_WIN64) // Audio input. diff --git a/ppapi/proxy/video_encoder_resource.cc b/ppapi/proxy/video_encoder_resource.cc index 5d7913d..3c6436b 100644 --- a/ppapi/proxy/video_encoder_resource.cc +++ b/ppapi/proxy/video_encoder_resource.cc @@ -2,19 +2,73 @@ // 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/numerics/safe_conversions.h" +#include "ppapi/c/pp_array_output.h" +#include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/video_encoder_resource.h" +#include "ppapi/proxy/video_frame_resource.h" +#include "ppapi/shared_impl/media_stream_buffer.h" +#include "ppapi/shared_impl/media_stream_buffer_manager.h" +#include "ppapi/thunk/enter.h" +using ppapi::proxy::SerializedHandle; +using ppapi::thunk::EnterResourceNoLock; using ppapi::thunk::PPB_VideoEncoder_API; namespace ppapi { namespace proxy { +namespace { + +void RunCallback(scoped_refptr<TrackedCallback>* callback, int32_t error) { + if (!TrackedCallback::IsPending(*callback)) + return; + + scoped_refptr<TrackedCallback> temp; + callback->swap(temp); + temp->Run(error); +} + +} // namespace + +VideoEncoderResource::ShmBuffer::ShmBuffer(uint32_t id, + scoped_ptr<base::SharedMemory> shm) + : id(id), shm(shm.Pass()) { +} + +VideoEncoderResource::ShmBuffer::~ShmBuffer() { +} + +VideoEncoderResource::BitstreamBuffer::BitstreamBuffer(uint32_t id, + uint32_t size, + bool key_frame) + : id(id), size(size), key_frame(key_frame) { +} + +VideoEncoderResource::BitstreamBuffer::~BitstreamBuffer() { +} + VideoEncoderResource::VideoEncoderResource(Connection connection, PP_Instance instance) - : PluginResource(connection, instance) { + : PluginResource(connection, instance), + initialized_(false), + closed_(false), + // Set |encoder_last_error_| to PP_OK after successful initialization. + // This makes error checking a little more concise, since we can check + // that the encoder has been initialized and hasn't returned an error by + // just testing |encoder_last_error_|. + encoder_last_error_(PP_ERROR_FAILED), + input_frame_count_(0), + input_coded_size_(PP_MakeSize(0, 0)), + buffer_manager_(this), + get_video_frame_data_(nullptr), + get_bitstream_buffer_data_(nullptr) { + SendCreate(RENDERER, PpapiHostMsg_VideoEncoder_Create()); } VideoEncoderResource::~VideoEncoderResource() { + Close(); } PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() { @@ -24,7 +78,28 @@ PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() { int32_t VideoEncoderResource::GetSupportedProfiles( const PP_ArrayOutput& output, const scoped_refptr<TrackedCallback>& callback) { - return PP_ERROR_FAILED; + if (TrackedCallback::IsPending(get_supported_profiles_callback_)) + return PP_ERROR_INPROGRESS; + + get_supported_profiles_callback_ = callback; + Call<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply>( + RENDERER, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(), + base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply, + this, output)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t VideoEncoderResource::GetFramesRequired() { + if (encoder_last_error_) + return encoder_last_error_; + return input_frame_count_; +} + +int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) { + if (encoder_last_error_) + return encoder_last_error_; + *size = input_coded_size_; + return PP_OK; } int32_t VideoEncoderResource::Initialize( @@ -34,45 +109,318 @@ int32_t VideoEncoderResource::Initialize( uint32_t initial_bitrate, PP_HardwareAcceleration acceleration, const scoped_refptr<TrackedCallback>& callback) { - return PP_ERROR_FAILED; -} + if (initialized_) + return PP_ERROR_FAILED; + if (TrackedCallback::IsPending(initialize_callback_)) + return PP_ERROR_INPROGRESS; -int32_t VideoEncoderResource::GetFramesRequired() { - return PP_ERROR_FAILED; -} - -int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) { - return PP_ERROR_FAILED; + initialize_callback_ = callback; + Call<PpapiPluginMsg_VideoEncoder_InitializeReply>( + RENDERER, PpapiHostMsg_VideoEncoder_Initialize( + input_format, *input_visible_size, output_profile, + initial_bitrate, acceleration), + base::Bind(&VideoEncoderResource::OnPluginMsgInitializeReply, this)); + return PP_OK_COMPLETIONPENDING; } int32_t VideoEncoderResource::GetVideoFrame( PP_Resource* video_frame, const scoped_refptr<TrackedCallback>& callback) { - return PP_ERROR_FAILED; + if (encoder_last_error_) + return encoder_last_error_; + + if (TrackedCallback::IsPending(get_video_frame_callback_)) + return PP_ERROR_INPROGRESS; + + get_video_frame_data_ = video_frame; + get_video_frame_callback_ = callback; + + // Lazily ask for a shared memory buffer in which video frames are allocated. + if (buffer_manager_.number_of_buffers() == 0) { + Call<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply>( + RENDERER, PpapiHostMsg_VideoEncoder_GetVideoFrames(), + base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply, + this)); + } else { + TryWriteVideoFrame(); + } + + return PP_OK_COMPLETIONPENDING; } int32_t VideoEncoderResource::Encode( PP_Resource video_frame, PP_Bool force_keyframe, const scoped_refptr<TrackedCallback>& callback) { - return PP_ERROR_FAILED; + if (encoder_last_error_) + return encoder_last_error_; + + VideoFrameMap::iterator it = video_frames_.find(video_frame); + if (it == video_frames_.end()) + // TODO(llandwerlin): accept MediaStreamVideoTrack's video frames. + return PP_ERROR_BADRESOURCE; + + scoped_refptr<VideoFrameResource> frame_resource = it->second; + + encode_callbacks_.insert(std::make_pair(video_frame, callback)); + + Call<PpapiPluginMsg_VideoEncoder_EncodeReply>( + RENDERER, + PpapiHostMsg_VideoEncoder_Encode(frame_resource->GetBufferIndex(), + PP_ToBool(force_keyframe)), + base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this, + video_frame)); + + // Invalidate the frame to prevent the plugin from modifying it. + it->second->Invalidate(); + video_frames_.erase(it); + + return PP_OK_COMPLETIONPENDING; } int32_t VideoEncoderResource::GetBitstreamBuffer( - PP_BitstreamBuffer* picture, + PP_BitstreamBuffer* bitstream_buffer, const scoped_refptr<TrackedCallback>& callback) { - return PP_ERROR_FAILED; + if (encoder_last_error_) + return encoder_last_error_; + if (TrackedCallback::IsPending(get_bitstream_buffer_callback_)) + return PP_ERROR_INPROGRESS; + + get_bitstream_buffer_callback_ = callback; + get_bitstream_buffer_data_ = bitstream_buffer; + + if (!available_bitstream_buffers_.empty()) { + BitstreamBuffer buffer(available_bitstream_buffers_.front()); + available_bitstream_buffers_.pop_front(); + WriteBitstreamBuffer(buffer); + } + + return PP_OK_COMPLETIONPENDING; } void VideoEncoderResource::RecycleBitstreamBuffer( - const PP_BitstreamBuffer* picture) { + const PP_BitstreamBuffer* bitstream_buffer) { + if (encoder_last_error_) + return; + BitstreamBufferMap::const_iterator iter = + bitstream_buffer_map_.find(bitstream_buffer->buffer); + if (iter != bitstream_buffer_map_.end()) { + Post(RENDERER, + PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer(iter->second)); + } } void VideoEncoderResource::RequestEncodingParametersChange(uint32_t bitrate, uint32_t framerate) { + if (encoder_last_error_) + return; + Post(RENDERER, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange( + bitrate, framerate)); } void VideoEncoderResource::Close() { + if (closed_) + return; + Post(RENDERER, PpapiHostMsg_VideoEncoder_Close()); + closed_ = true; + if (!encoder_last_error_ || !initialized_) + NotifyError(PP_ERROR_ABORTED); + ReleaseFrames(); +} + +void VideoEncoderResource::OnReplyReceived( + const ResourceMessageReplyParams& params, + const IPC::Message& msg) { + PPAPI_BEGIN_MESSAGE_MAP(VideoEncoderResource, msg) + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( + PpapiPluginMsg_VideoEncoder_BitstreamBuffers, + OnPluginMsgBitstreamBuffers) + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( + PpapiPluginMsg_VideoEncoder_BitstreamBufferReady, + OnPluginMsgBitstreamBufferReady) + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoEncoder_NotifyError, + OnPluginMsgNotifyError) + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED( + PluginResource::OnReplyReceived(params, msg)) + PPAPI_END_MESSAGE_MAP() +} + +void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply( + const PP_ArrayOutput& output, + const ResourceMessageReplyParams& params, + const std::vector<PP_VideoProfileDescription>& profiles) { + void* ptr = output.GetDataBuffer( + output.user_data, + base::checked_cast<uint32_t>(profiles.size()), + base::checked_cast<uint32_t>(sizeof(PP_VideoProfileDescription))); + + if (!ptr) { + RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED); + return; + } + + if (profiles.size() > 0) + memcpy(ptr, &profiles[0], + profiles.size() * sizeof(PP_VideoProfileDescription)); + RunCallback(&get_supported_profiles_callback_, PP_OK); +} + +void VideoEncoderResource::OnPluginMsgInitializeReply( + const ResourceMessageReplyParams& params, + uint32_t input_frame_count, + const PP_Size& input_coded_size) { + DCHECK(!initialized_); + + encoder_last_error_ = params.result(); + if (!encoder_last_error_) + initialized_ = true; + + input_frame_count_ = input_frame_count; + input_coded_size_ = input_coded_size; + + RunCallback(&initialize_callback_, encoder_last_error_); +} + +void VideoEncoderResource::OnPluginMsgGetVideoFramesReply( + const ResourceMessageReplyParams& params, + uint32_t frame_count, + uint32_t frame_length, + const PP_Size& frame_size) { + int32_t error = params.result(); + if (error) { + NotifyError(error); + return; + } + + base::SharedMemoryHandle buffer_handle; + params.TakeSharedMemoryHandleAtIndex(0, &buffer_handle); + + if (!buffer_manager_.SetBuffers( + frame_count, frame_length, + make_scoped_ptr(new base::SharedMemory(buffer_handle, false)), + true)) { + NotifyError(PP_ERROR_FAILED); + return; + } + + if (TrackedCallback::IsPending(get_video_frame_callback_)) + TryWriteVideoFrame(); +} + +void VideoEncoderResource::OnPluginMsgEncodeReply( + PP_Resource video_frame, + const ResourceMessageReplyParams& params, + uint32_t frame_id) { + DCHECK_NE(encode_callbacks_.size(), 0U); + encoder_last_error_ = params.result(); + + EncodeMap::iterator it = encode_callbacks_.find(video_frame); + DCHECK(encode_callbacks_.end() != it); + + scoped_refptr<TrackedCallback> callback = it->second; + encode_callbacks_.erase(it); + RunCallback(&callback, encoder_last_error_); + + buffer_manager_.EnqueueBuffer(frame_id); + // If the plugin is waiting for a video frame, we can give the one + // that just became available again. + if (TrackedCallback::IsPending(get_video_frame_callback_)) + TryWriteVideoFrame(); +} + +void VideoEncoderResource::OnPluginMsgBitstreamBuffers( + const ResourceMessageReplyParams& params, + uint32_t buffer_length) { + std::vector<base::SharedMemoryHandle> shm_handles; + params.TakeAllSharedMemoryHandles(&shm_handles); + if (shm_handles.size() == 0) { + NotifyError(PP_ERROR_FAILED); + return; + } + + for (uint32_t i = 0; i < shm_handles.size(); ++i) { + scoped_ptr<base::SharedMemory> shm( + new base::SharedMemory(shm_handles[i], true)); + CHECK(shm->Map(buffer_length)); + + ShmBuffer* buffer = new ShmBuffer(i, shm.Pass()); + shm_buffers_.push_back(buffer); + bitstream_buffer_map_.insert( + std::make_pair(buffer->shm->memory(), buffer->id)); + } +} + +void VideoEncoderResource::OnPluginMsgBitstreamBufferReady( + const ResourceMessageReplyParams& params, + uint32_t buffer_id, + uint32_t buffer_size, + bool key_frame) { + available_bitstream_buffers_.push_back( + BitstreamBuffer(buffer_id, buffer_size, key_frame)); + + if (TrackedCallback::IsPending(get_bitstream_buffer_callback_)) { + BitstreamBuffer buffer(available_bitstream_buffers_.front()); + available_bitstream_buffers_.pop_front(); + WriteBitstreamBuffer(buffer); + } +} + +void VideoEncoderResource::OnPluginMsgNotifyError( + const ResourceMessageReplyParams& params, + int32_t error) { + NotifyError(error); +} + +void VideoEncoderResource::NotifyError(int32_t error) { + encoder_last_error_ = error; + RunCallback(&get_supported_profiles_callback_, error); + RunCallback(&initialize_callback_, error); + RunCallback(&get_video_frame_callback_, error); + get_video_frame_data_ = nullptr; + RunCallback(&get_bitstream_buffer_callback_, error); + get_bitstream_buffer_data_ = nullptr; + for (EncodeMap::iterator it = encode_callbacks_.begin(); + it != encode_callbacks_.end(); ++it) { + scoped_refptr<TrackedCallback> callback = it->second; + RunCallback(&callback, error); + } + encode_callbacks_.clear(); +} + +void VideoEncoderResource::TryWriteVideoFrame() { + DCHECK(TrackedCallback::IsPending(get_video_frame_callback_)); + + int32_t frame_id = buffer_manager_.DequeueBuffer(); + if (frame_id < 0) + return; + + scoped_refptr<VideoFrameResource> resource = new VideoFrameResource( + pp_instance(), frame_id, buffer_manager_.GetBufferPointer(frame_id)); + video_frames_.insert( + VideoFrameMap::value_type(resource->pp_resource(), resource)); + + *get_video_frame_data_ = resource->GetReference(); + get_video_frame_data_ = nullptr; + RunCallback(&get_video_frame_callback_, PP_OK); +} + +void VideoEncoderResource::WriteBitstreamBuffer(const BitstreamBuffer& buffer) { + DCHECK_LT(buffer.id, shm_buffers_.size()); + + get_bitstream_buffer_data_->size = buffer.size; + get_bitstream_buffer_data_->buffer = shm_buffers_[buffer.id]->shm->memory(); + get_bitstream_buffer_data_->key_frame = PP_FromBool(buffer.key_frame); + get_bitstream_buffer_data_ = nullptr; + RunCallback(&get_bitstream_buffer_callback_, PP_OK); +} + +void VideoEncoderResource::ReleaseFrames() { + for (VideoFrameMap::iterator it = video_frames_.begin(); + it != video_frames_.end(); ++it) { + it->second->Invalidate(); + it->second = nullptr; + } + video_frames_.clear(); } } // namespace proxy diff --git a/ppapi/proxy/video_encoder_resource.h b/ppapi/proxy/video_encoder_resource.h index 585d5c1..3b46759 100644 --- a/ppapi/proxy/video_encoder_resource.h +++ b/ppapi/proxy/video_encoder_resource.h @@ -5,21 +5,34 @@ #ifndef PPAPI_PROXY_VIDEO_ENCODER_RESOURCE_H_ #define PPAPI_PROXY_VIDEO_ENCODER_RESOURCE_H_ +#include <deque> + #include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "ppapi/proxy/connection.h" #include "ppapi/proxy/plugin_resource.h" +#include "ppapi/shared_impl/media_stream_buffer_manager.h" #include "ppapi/shared_impl/resource.h" #include "ppapi/thunk/ppb_video_encoder_api.h" +namespace base { +class SharedMemory; +} + namespace ppapi { class TrackedCallback; namespace proxy { +class SerializedHandle; +class VideoFrameResource; + class PPAPI_PROXY_EXPORT VideoEncoderResource : public PluginResource, - public thunk::PPB_VideoEncoder_API { + public thunk::PPB_VideoEncoder_API, + public ppapi::MediaStreamBufferManager::Delegate { public: VideoEncoderResource(Connection connection, PP_Instance instance); ~VideoEncoderResource() override; @@ -27,6 +40,26 @@ class PPAPI_PROXY_EXPORT VideoEncoderResource thunk::PPB_VideoEncoder_API* AsPPB_VideoEncoder_API() override; private: + struct ShmBuffer { + ShmBuffer(uint32_t id, scoped_ptr<base::SharedMemory> shm); + ~ShmBuffer(); + + // Index of the buffer in the ScopedVector. Buffers have the same id in + // the plugin and the host. + uint32_t id; + scoped_ptr<base::SharedMemory> shm; + }; + + struct BitstreamBuffer { + BitstreamBuffer(uint32_t id, uint32_t size, bool key_frame); + ~BitstreamBuffer(); + + // Index of the buffer in the ScopedVector. Same as ShmBuffer::id. + uint32_t id; + uint32_t size; + bool key_frame; + }; + // PPB_VideoEncoder_API implementation. int32_t GetSupportedProfiles( const PP_ArrayOutput& output, @@ -53,6 +86,72 @@ class PPAPI_PROXY_EXPORT VideoEncoderResource uint32_t framerate) override; void Close() override; + // PluginResource implementation. + void OnReplyReceived(const ResourceMessageReplyParams& params, + const IPC::Message& msg) override; + + // Reply message handlers for operations that are done in the host. + void OnPluginMsgGetSupportedProfilesReply( + const PP_ArrayOutput& output, + const ResourceMessageReplyParams& params, + const std::vector<PP_VideoProfileDescription>& profiles); + void OnPluginMsgInitializeReply(const ResourceMessageReplyParams& params, + uint32_t input_frame_count, + const PP_Size& input_coded_size); + void OnPluginMsgGetVideoFramesReply(const ResourceMessageReplyParams& params, + uint32_t frame_count, + uint32_t frame_length, + const PP_Size& frame_size); + void OnPluginMsgEncodeReply(PP_Resource video_frame, + const ResourceMessageReplyParams& params, + uint32_t frame_id); + + // Unsolicited reply message handlers. + void OnPluginMsgBitstreamBuffers(const ResourceMessageReplyParams& params, + uint32_t buffer_length); + void OnPluginMsgBitstreamBufferReady(const ResourceMessageReplyParams& params, + uint32_t buffer_id, + uint32_t buffer_size, + bool key_frame); + void OnPluginMsgNotifyError(const ResourceMessageReplyParams& params, + int32_t error); + + // Internal utility functions. + void NotifyError(int32_t error); + void TryWriteVideoFrame(); + void WriteBitstreamBuffer(const BitstreamBuffer& buffer); + void ReleaseFrames(); + + bool initialized_; + bool closed_; + int32_t encoder_last_error_; + + int32_t input_frame_count_; + PP_Size input_coded_size_; + + MediaStreamBufferManager buffer_manager_; + + typedef std::map<PP_Resource, scoped_refptr<VideoFrameResource> > + VideoFrameMap; + VideoFrameMap video_frames_; + + ScopedVector<ShmBuffer> shm_buffers_; + + std::deque<BitstreamBuffer> available_bitstream_buffers_; + typedef std::map<void*, uint32_t> BitstreamBufferMap; + BitstreamBufferMap bitstream_buffer_map_; + + scoped_refptr<TrackedCallback> get_supported_profiles_callback_; + scoped_refptr<TrackedCallback> initialize_callback_; + scoped_refptr<TrackedCallback> get_video_frame_callback_; + PP_Resource* get_video_frame_data_; + + typedef std::map<PP_Resource, scoped_refptr<TrackedCallback> > EncodeMap; + EncodeMap encode_callbacks_; + + scoped_refptr<TrackedCallback> get_bitstream_buffer_callback_; + PP_BitstreamBuffer* get_bitstream_buffer_data_; + DISALLOW_COPY_AND_ASSIGN(VideoEncoderResource); }; diff --git a/ppapi/proxy/video_encoder_resource_unittest.cc b/ppapi/proxy/video_encoder_resource_unittest.cc new file mode 100644 index 0000000..0adc1ea --- /dev/null +++ b/ppapi/proxy/video_encoder_resource_unittest.cc @@ -0,0 +1,1085 @@ +// 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 { + +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 MediaStreamBufferManager::Delegate { + public: + VideoEncoderResourceTest() + : encoder_iface_(thunk::GetPPB_VideoEncoder_0_1_Thunk()), + video_frames_manager_(this) {} + ~VideoEncoderResourceTest() override {} + + const PPB_VideoEncoder_0_1* encoder_iface() const { return encoder_iface_; } + + 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, + 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) { + scoped_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, + shared_memory_frames.Pass(), + 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 (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_BitstreamBuffers(buffer_length), handles); + } + + void SendGetVideoFramesReply(const ResourceMessageCallParams& params, + uint32_t frame_count, + uint32_t frame_length, + const PP_Size& size) { + base::SharedMemoryHandle handle; + ASSERT_TRUE(video_frames_manager_.shm()->ShareToProcess( + base::Process::Current().Handle(), &handle)); + 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_1* encoder_iface_; + + ScopedVector<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.acceleration = PP_HARDWAREACCELERATION_ONLY; + 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.acceleration = PP_HARDWAREACCELERATION_NONE; + profiles_response.push_back(profile); + + SendGetSupportedProfilesReply(params, profiles_response); + ASSERT_EQ(PP_OK, cb.result()); + + ASSERT_EQ(2U, profiles_response.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 = 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 + // successfull. + 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()); + } +} + +} // namespace proxy +} // namespace ppapi diff --git a/ppapi/tests/test_video_encoder.cc b/ppapi/tests/test_video_encoder.cc new file mode 100644 index 0000000..4737ed0 --- /dev/null +++ b/ppapi/tests/test_video_encoder.cc @@ -0,0 +1,41 @@ +// 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/tests/test_video_encoder.h" + +#include "ppapi/c/pp_codecs.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/cpp/video_encoder.h" +#include "ppapi/tests/testing_instance.h" + +REGISTER_TEST_CASE(VideoEncoder); + +bool TestVideoEncoder::Init() { + video_encoder_interface_ = static_cast<const PPB_VideoEncoder_0_1*>( + pp::Module::Get()->GetBrowserInterface(PPB_VIDEOENCODER_INTERFACE_0_1)); + return video_encoder_interface_ && CheckTestingInterface(); +} + +void TestVideoEncoder::RunTests(const std::string& filter) { + RUN_CALLBACK_TEST(TestVideoEncoder, Create, filter); +} + +std::string TestVideoEncoder::TestCreate() { + // Test that we get results for supported formats. + { + pp::VideoEncoder video_encoder(instance_); + ASSERT_FALSE(video_encoder.is_null()); + + TestCompletionCallbackWithOutput<std::vector<PP_VideoProfileDescription> > + callback(instance_->pp_instance(), false); + callback.WaitForResult( + video_encoder.GetSupportedProfiles(callback.GetCallback())); + + ASSERT_GE(callback.output().size(), 0U); + } + // TODO(llandwerlin): add more tests once software video encode is + // available. + + PASS(); +} diff --git a/ppapi/tests/test_video_encoder.h b/ppapi/tests/test_video_encoder.h new file mode 100644 index 0000000..f814f9e --- /dev/null +++ b/ppapi/tests/test_video_encoder.h @@ -0,0 +1,29 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PPAPI_TESTS_TEST_VIDEO_ENCODER_H_ +#define PPAPI_TESTS_TEST_VIDEO_ENCODER_H_ + +#include <string> + +#include "ppapi/c/pp_stdint.h" +#include "ppapi/c/ppb_video_encoder.h" +#include "ppapi/tests/test_case.h" + +class TestVideoEncoder : public TestCase { + public: + explicit TestVideoEncoder(TestingInstance* instance) : TestCase(instance) {} + + private: + // TestCase implementation. + virtual bool Init(); + virtual void RunTests(const std::string& filter); + + std::string TestCreate(); + + // Used by the tests that access the C API directly. + const PPB_VideoEncoder_0_1* video_encoder_interface_; +}; + +#endif // PPAPI_TESTS_TEST_VIDEO_ENCODER_H_ |