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
;
25 void RunCallback(scoped_refptr
<TrackedCallback
>* callback
, int32_t error
) {
26 if (!TrackedCallback::IsPending(*callback
))
29 scoped_refptr
<TrackedCallback
> temp
;
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
);
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
,
69 : id(id
), size(size
), key_frame(key_frame
) {
72 VideoEncoderResource::BitstreamBuffer::~BitstreamBuffer() {
75 VideoEncoderResource::VideoEncoderResource(Connection connection
,
77 : PluginResource(connection
, instance
),
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() {
97 PPB_VideoEncoder_API
* VideoEncoderResource::AsPPB_VideoEncoder_API() {
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_
;
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
) {
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
,
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
>(
206 PpapiHostMsg_VideoEncoder_Encode(frame_resource
->GetBufferIndex(),
207 PP_ToBool(force_keyframe
)),
208 base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply
, this,
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_
)
242 BitstreamBufferMap::const_iterator iter
=
243 bitstream_buffer_map_
.find(bitstream_buffer
->buffer
);
244 if (iter
!= bitstream_buffer_map_
.end()) {
246 PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer(iter
->second
));
250 void VideoEncoderResource::RequestEncodingParametersChange(uint32_t bitrate
,
251 uint32_t framerate
) {
252 if (encoder_last_error_
)
254 Post(RENDERER
, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange(
255 bitrate
, framerate
));
258 void VideoEncoderResource::Close() {
261 Post(RENDERER
, PpapiHostMsg_VideoEncoder_Close());
263 if (!encoder_last_error_
|| !initialized_
)
264 NotifyError(PP_ERROR_ABORTED
);
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
,
288 const ResourceMessageReplyParams
& params
,
289 const std::vector
<PP_VideoProfileDescription
>& profiles
) {
290 int32_t error
= params
.result();
296 ArrayWriter
writer(output
);
297 if (!writer
.is_valid()) {
298 RunCallback(&get_supported_profiles_callback_
, PP_ERROR_BADARGUMENT
);
305 writer
.StoreVector(PP_VideoProfileDescriptionTo_0_1(profiles
));
307 write_result
= writer
.StoreVector(profiles
);
310 RunCallback(&get_supported_profiles_callback_
, PP_ERROR_FAILED
);
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_
)
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();
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)),
352 NotifyError(PP_ERROR_FAILED
);
356 if (TrackedCallback::IsPending(get_video_frame_callback_
))
357 TryWriteVideoFrame();
360 void VideoEncoderResource::OnPluginMsgEncodeReply(
361 PP_Resource video_frame
,
362 const ResourceMessageReplyParams
& params
,
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())
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
);
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
,
411 uint32_t buffer_size
,
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
,
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();
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();