Elim cr-checkbox
[chromium-blink-merge.git] / ppapi / proxy / video_encoder_resource.cc
blob01c6b0e1612fccb43510f552847830c96519851d
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/memory/shared_memory.h"
6 #include "base/numerics/safe_conversions.h"
7 #include "ppapi/c/pp_array_output.h"
8 #include "ppapi/proxy/ppapi_messages.h"
9 #include "ppapi/proxy/video_encoder_resource.h"
10 #include "ppapi/proxy/video_frame_resource.h"
11 #include "ppapi/shared_impl/array_writer.h"
12 #include "ppapi/shared_impl/media_stream_buffer.h"
13 #include "ppapi/shared_impl/media_stream_buffer_manager.h"
14 #include "ppapi/thunk/enter.h"
16 using ppapi::proxy::SerializedHandle;
17 using ppapi::thunk::EnterResourceNoLock;
18 using ppapi::thunk::PPB_VideoEncoder_API;
20 namespace ppapi {
21 namespace proxy {
23 namespace {
25 void RunCallback(scoped_refptr<TrackedCallback>* callback, int32_t error) {
26 if (!TrackedCallback::IsPending(*callback))
27 return;
29 scoped_refptr<TrackedCallback> temp;
30 callback->swap(temp);
31 temp->Run(error);
34 std::vector<PP_VideoProfileDescription_0_1> PP_VideoProfileDescriptionTo_0_1(
35 std::vector<PP_VideoProfileDescription> profiles) {
36 std::vector<PP_VideoProfileDescription_0_1> profiles_0_1;
38 for (uint32_t i = 0; i < profiles.size(); ++i) {
39 const PP_VideoProfileDescription& profile = profiles[i];
40 PP_VideoProfileDescription_0_1 profile_0_1;
42 profile_0_1.profile = profile.profile;
43 profile_0_1.max_resolution = profile.max_resolution;
44 profile_0_1.max_framerate_numerator = profile.max_framerate_numerator;
45 profile_0_1.max_framerate_denominator = profile.max_framerate_denominator;
46 profile_0_1.acceleration = profile.hardware_accelerated == PP_TRUE
47 ? PP_HARDWAREACCELERATION_ONLY
48 : PP_HARDWAREACCELERATION_NONE;
50 profiles_0_1.push_back(profile_0_1);
53 return profiles_0_1;
56 } // namespace
58 VideoEncoderResource::ShmBuffer::ShmBuffer(uint32_t id,
59 scoped_ptr<base::SharedMemory> shm)
60 : id(id), shm(shm.Pass()) {
63 VideoEncoderResource::ShmBuffer::~ShmBuffer() {
66 VideoEncoderResource::BitstreamBuffer::BitstreamBuffer(uint32_t id,
67 uint32_t size,
68 bool key_frame)
69 : id(id), size(size), key_frame(key_frame) {
72 VideoEncoderResource::BitstreamBuffer::~BitstreamBuffer() {
75 VideoEncoderResource::VideoEncoderResource(Connection connection,
76 PP_Instance instance)
77 : PluginResource(connection, instance),
78 initialized_(false),
79 closed_(false),
80 // Set |encoder_last_error_| to PP_OK after successful initialization.
81 // This makes error checking a little more concise, since we can check
82 // that the encoder has been initialized and hasn't returned an error by
83 // just testing |encoder_last_error_|.
84 encoder_last_error_(PP_ERROR_FAILED),
85 input_frame_count_(0),
86 input_coded_size_(PP_MakeSize(0, 0)),
87 buffer_manager_(this),
88 get_video_frame_data_(nullptr),
89 get_bitstream_buffer_data_(nullptr) {
90 SendCreate(RENDERER, PpapiHostMsg_VideoEncoder_Create());
93 VideoEncoderResource::~VideoEncoderResource() {
94 Close();
97 PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() {
98 return this;
101 int32_t VideoEncoderResource::GetSupportedProfiles(
102 const PP_ArrayOutput& output,
103 const scoped_refptr<TrackedCallback>& callback) {
104 if (TrackedCallback::IsPending(get_supported_profiles_callback_))
105 return PP_ERROR_INPROGRESS;
107 get_supported_profiles_callback_ = callback;
108 Call<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply>(
109 RENDERER, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(),
110 base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply,
111 this, output, false));
112 return PP_OK_COMPLETIONPENDING;
115 int32_t VideoEncoderResource::GetSupportedProfiles0_1(
116 const PP_ArrayOutput& output,
117 const scoped_refptr<TrackedCallback>& callback) {
118 if (TrackedCallback::IsPending(get_supported_profiles_callback_))
119 return PP_ERROR_INPROGRESS;
121 get_supported_profiles_callback_ = callback;
122 Call<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply>(
123 RENDERER, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(),
124 base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply,
125 this, output, true));
126 return PP_OK_COMPLETIONPENDING;
129 int32_t VideoEncoderResource::GetFramesRequired() {
130 if (encoder_last_error_)
131 return encoder_last_error_;
132 return input_frame_count_;
135 int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) {
136 if (encoder_last_error_)
137 return encoder_last_error_;
138 *size = input_coded_size_;
139 return PP_OK;
142 int32_t VideoEncoderResource::Initialize(
143 PP_VideoFrame_Format input_format,
144 const PP_Size* input_visible_size,
145 PP_VideoProfile output_profile,
146 uint32_t initial_bitrate,
147 PP_HardwareAcceleration acceleration,
148 const scoped_refptr<TrackedCallback>& callback) {
149 if (initialized_)
150 return PP_ERROR_FAILED;
151 if (TrackedCallback::IsPending(initialize_callback_))
152 return PP_ERROR_INPROGRESS;
154 initialize_callback_ = callback;
155 Call<PpapiPluginMsg_VideoEncoder_InitializeReply>(
156 RENDERER, PpapiHostMsg_VideoEncoder_Initialize(
157 input_format, *input_visible_size, output_profile,
158 initial_bitrate, acceleration),
159 base::Bind(&VideoEncoderResource::OnPluginMsgInitializeReply, this));
160 return PP_OK_COMPLETIONPENDING;
163 int32_t VideoEncoderResource::GetVideoFrame(
164 PP_Resource* video_frame,
165 const scoped_refptr<TrackedCallback>& callback) {
166 if (encoder_last_error_)
167 return encoder_last_error_;
169 if (TrackedCallback::IsPending(get_video_frame_callback_))
170 return PP_ERROR_INPROGRESS;
172 get_video_frame_data_ = video_frame;
173 get_video_frame_callback_ = callback;
175 // Lazily ask for a shared memory buffer in which video frames are allocated.
176 if (buffer_manager_.number_of_buffers() == 0) {
177 Call<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply>(
178 RENDERER, PpapiHostMsg_VideoEncoder_GetVideoFrames(),
179 base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply,
180 this));
181 } else {
182 TryWriteVideoFrame();
185 return PP_OK_COMPLETIONPENDING;
188 int32_t VideoEncoderResource::Encode(
189 PP_Resource video_frame,
190 PP_Bool force_keyframe,
191 const scoped_refptr<TrackedCallback>& callback) {
192 if (encoder_last_error_)
193 return encoder_last_error_;
195 VideoFrameMap::iterator it = video_frames_.find(video_frame);
196 if (it == video_frames_.end())
197 // TODO(llandwerlin): accept MediaStreamVideoTrack's video frames.
198 return PP_ERROR_BADRESOURCE;
200 scoped_refptr<VideoFrameResource> frame_resource = it->second;
202 encode_callbacks_.insert(std::make_pair(video_frame, callback));
204 Call<PpapiPluginMsg_VideoEncoder_EncodeReply>(
205 RENDERER,
206 PpapiHostMsg_VideoEncoder_Encode(frame_resource->GetBufferIndex(),
207 PP_ToBool(force_keyframe)),
208 base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this,
209 video_frame));
211 // Invalidate the frame to prevent the plugin from modifying it.
212 it->second->Invalidate();
213 video_frames_.erase(it);
215 return PP_OK_COMPLETIONPENDING;
218 int32_t VideoEncoderResource::GetBitstreamBuffer(
219 PP_BitstreamBuffer* bitstream_buffer,
220 const scoped_refptr<TrackedCallback>& callback) {
221 if (encoder_last_error_)
222 return encoder_last_error_;
223 if (TrackedCallback::IsPending(get_bitstream_buffer_callback_))
224 return PP_ERROR_INPROGRESS;
226 get_bitstream_buffer_callback_ = callback;
227 get_bitstream_buffer_data_ = bitstream_buffer;
229 if (!available_bitstream_buffers_.empty()) {
230 BitstreamBuffer buffer(available_bitstream_buffers_.front());
231 available_bitstream_buffers_.pop_front();
232 WriteBitstreamBuffer(buffer);
235 return PP_OK_COMPLETIONPENDING;
238 void VideoEncoderResource::RecycleBitstreamBuffer(
239 const PP_BitstreamBuffer* bitstream_buffer) {
240 if (encoder_last_error_)
241 return;
242 BitstreamBufferMap::const_iterator iter =
243 bitstream_buffer_map_.find(bitstream_buffer->buffer);
244 if (iter != bitstream_buffer_map_.end()) {
245 Post(RENDERER,
246 PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer(iter->second));
250 void VideoEncoderResource::RequestEncodingParametersChange(uint32_t bitrate,
251 uint32_t framerate) {
252 if (encoder_last_error_)
253 return;
254 Post(RENDERER, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange(
255 bitrate, framerate));
258 void VideoEncoderResource::Close() {
259 if (closed_)
260 return;
261 Post(RENDERER, PpapiHostMsg_VideoEncoder_Close());
262 closed_ = true;
263 if (!encoder_last_error_ || !initialized_)
264 NotifyError(PP_ERROR_ABORTED);
265 ReleaseFrames();
268 void VideoEncoderResource::OnReplyReceived(
269 const ResourceMessageReplyParams& params,
270 const IPC::Message& msg) {
271 PPAPI_BEGIN_MESSAGE_MAP(VideoEncoderResource, msg)
272 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
273 PpapiPluginMsg_VideoEncoder_BitstreamBuffers,
274 OnPluginMsgBitstreamBuffers)
275 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
276 PpapiPluginMsg_VideoEncoder_BitstreamBufferReady,
277 OnPluginMsgBitstreamBufferReady)
278 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoEncoder_NotifyError,
279 OnPluginMsgNotifyError)
280 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
281 PluginResource::OnReplyReceived(params, msg))
282 PPAPI_END_MESSAGE_MAP()
285 void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply(
286 const PP_ArrayOutput& output,
287 bool version0_1,
288 const ResourceMessageReplyParams& params,
289 const std::vector<PP_VideoProfileDescription>& profiles) {
290 int32_t error = params.result();
291 if (error) {
292 NotifyError(error);
293 return;
296 ArrayWriter writer(output);
297 if (!writer.is_valid()) {
298 RunCallback(&get_supported_profiles_callback_, PP_ERROR_BADARGUMENT);
299 return;
302 bool write_result;
303 if (version0_1)
304 write_result =
305 writer.StoreVector(PP_VideoProfileDescriptionTo_0_1(profiles));
306 else
307 write_result = writer.StoreVector(profiles);
309 if (!write_result) {
310 RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED);
311 return;
314 RunCallback(&get_supported_profiles_callback_,
315 base::checked_cast<int32_t>(profiles.size()));
318 void VideoEncoderResource::OnPluginMsgInitializeReply(
319 const ResourceMessageReplyParams& params,
320 uint32_t input_frame_count,
321 const PP_Size& input_coded_size) {
322 DCHECK(!initialized_);
324 encoder_last_error_ = params.result();
325 if (!encoder_last_error_)
326 initialized_ = true;
328 input_frame_count_ = input_frame_count;
329 input_coded_size_ = input_coded_size;
331 RunCallback(&initialize_callback_, encoder_last_error_);
334 void VideoEncoderResource::OnPluginMsgGetVideoFramesReply(
335 const ResourceMessageReplyParams& params,
336 uint32_t frame_count,
337 uint32_t frame_length,
338 const PP_Size& frame_size) {
339 int32_t error = params.result();
340 if (error) {
341 NotifyError(error);
342 return;
345 base::SharedMemoryHandle buffer_handle;
346 params.TakeSharedMemoryHandleAtIndex(0, &buffer_handle);
348 if (!buffer_manager_.SetBuffers(
349 frame_count, frame_length,
350 make_scoped_ptr(new base::SharedMemory(buffer_handle, false)),
351 true)) {
352 NotifyError(PP_ERROR_FAILED);
353 return;
356 if (TrackedCallback::IsPending(get_video_frame_callback_))
357 TryWriteVideoFrame();
360 void VideoEncoderResource::OnPluginMsgEncodeReply(
361 PP_Resource video_frame,
362 const ResourceMessageReplyParams& params,
363 uint32_t frame_id) {
364 // We need to ensure there are still callbacks to be called before
365 // processing this message. We might receive a EncodeReply message
366 // after having sent a Close message to the renderer. In this case,
367 // we don't have any callback left to call.
368 if (encode_callbacks_.empty())
369 return;
370 encoder_last_error_ = params.result();
372 EncodeMap::iterator it = encode_callbacks_.find(video_frame);
373 DCHECK(encode_callbacks_.end() != it);
375 scoped_refptr<TrackedCallback> callback = it->second;
376 encode_callbacks_.erase(it);
377 RunCallback(&callback, encoder_last_error_);
379 buffer_manager_.EnqueueBuffer(frame_id);
380 // If the plugin is waiting for a video frame, we can give the one
381 // that just became available again.
382 if (TrackedCallback::IsPending(get_video_frame_callback_))
383 TryWriteVideoFrame();
386 void VideoEncoderResource::OnPluginMsgBitstreamBuffers(
387 const ResourceMessageReplyParams& params,
388 uint32_t buffer_length) {
389 std::vector<base::SharedMemoryHandle> shm_handles;
390 params.TakeAllSharedMemoryHandles(&shm_handles);
391 if (shm_handles.size() == 0) {
392 NotifyError(PP_ERROR_FAILED);
393 return;
396 for (uint32_t i = 0; i < shm_handles.size(); ++i) {
397 scoped_ptr<base::SharedMemory> shm(
398 new base::SharedMemory(shm_handles[i], true));
399 CHECK(shm->Map(buffer_length));
401 ShmBuffer* buffer = new ShmBuffer(i, shm.Pass());
402 shm_buffers_.push_back(buffer);
403 bitstream_buffer_map_.insert(
404 std::make_pair(buffer->shm->memory(), buffer->id));
408 void VideoEncoderResource::OnPluginMsgBitstreamBufferReady(
409 const ResourceMessageReplyParams& params,
410 uint32_t buffer_id,
411 uint32_t buffer_size,
412 bool key_frame) {
413 available_bitstream_buffers_.push_back(
414 BitstreamBuffer(buffer_id, buffer_size, key_frame));
416 if (TrackedCallback::IsPending(get_bitstream_buffer_callback_)) {
417 BitstreamBuffer buffer(available_bitstream_buffers_.front());
418 available_bitstream_buffers_.pop_front();
419 WriteBitstreamBuffer(buffer);
423 void VideoEncoderResource::OnPluginMsgNotifyError(
424 const ResourceMessageReplyParams& params,
425 int32_t error) {
426 NotifyError(error);
429 void VideoEncoderResource::NotifyError(int32_t error) {
430 encoder_last_error_ = error;
431 RunCallback(&get_supported_profiles_callback_, error);
432 RunCallback(&initialize_callback_, error);
433 RunCallback(&get_video_frame_callback_, error);
434 get_video_frame_data_ = nullptr;
435 RunCallback(&get_bitstream_buffer_callback_, error);
436 get_bitstream_buffer_data_ = nullptr;
437 for (EncodeMap::iterator it = encode_callbacks_.begin();
438 it != encode_callbacks_.end(); ++it) {
439 scoped_refptr<TrackedCallback> callback = it->second;
440 RunCallback(&callback, error);
442 encode_callbacks_.clear();
445 void VideoEncoderResource::TryWriteVideoFrame() {
446 DCHECK(TrackedCallback::IsPending(get_video_frame_callback_));
448 int32_t frame_id = buffer_manager_.DequeueBuffer();
449 if (frame_id < 0)
450 return;
452 scoped_refptr<VideoFrameResource> resource = new VideoFrameResource(
453 pp_instance(), frame_id, buffer_manager_.GetBufferPointer(frame_id));
454 video_frames_.insert(
455 VideoFrameMap::value_type(resource->pp_resource(), resource));
457 *get_video_frame_data_ = resource->GetReference();
458 get_video_frame_data_ = nullptr;
459 RunCallback(&get_video_frame_callback_, PP_OK);
462 void VideoEncoderResource::WriteBitstreamBuffer(const BitstreamBuffer& buffer) {
463 DCHECK_LT(buffer.id, shm_buffers_.size());
465 get_bitstream_buffer_data_->size = buffer.size;
466 get_bitstream_buffer_data_->buffer = shm_buffers_[buffer.id]->shm->memory();
467 get_bitstream_buffer_data_->key_frame = PP_FromBool(buffer.key_frame);
468 get_bitstream_buffer_data_ = nullptr;
469 RunCallback(&get_bitstream_buffer_callback_, PP_OK);
472 void VideoEncoderResource::ReleaseFrames() {
473 for (VideoFrameMap::iterator it = video_frames_.begin();
474 it != video_frames_.end(); ++it) {
475 it->second->Invalidate();
476 it->second = nullptr;
478 video_frames_.clear();
481 } // namespace proxy
482 } // namespace ppapi