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
;
36 VideoEncoderResource::ShmBuffer::ShmBuffer(uint32_t id
,
37 scoped_ptr
<base::SharedMemory
> shm
)
38 : id(id
), shm(shm
.Pass()) {
41 VideoEncoderResource::ShmBuffer::~ShmBuffer() {
44 VideoEncoderResource::BitstreamBuffer::BitstreamBuffer(uint32_t id
,
47 : id(id
), size(size
), key_frame(key_frame
) {
50 VideoEncoderResource::BitstreamBuffer::~BitstreamBuffer() {
53 VideoEncoderResource::VideoEncoderResource(Connection connection
,
55 : PluginResource(connection
, instance
),
58 // Set |encoder_last_error_| to PP_OK after successful initialization.
59 // This makes error checking a little more concise, since we can check
60 // that the encoder has been initialized and hasn't returned an error by
61 // just testing |encoder_last_error_|.
62 encoder_last_error_(PP_ERROR_FAILED
),
63 input_frame_count_(0),
64 input_coded_size_(PP_MakeSize(0, 0)),
65 buffer_manager_(this),
66 get_video_frame_data_(nullptr),
67 get_bitstream_buffer_data_(nullptr) {
68 SendCreate(RENDERER
, PpapiHostMsg_VideoEncoder_Create());
71 VideoEncoderResource::~VideoEncoderResource() {
75 PPB_VideoEncoder_API
* VideoEncoderResource::AsPPB_VideoEncoder_API() {
79 int32_t VideoEncoderResource::GetSupportedProfiles(
80 const PP_ArrayOutput
& output
,
81 const scoped_refptr
<TrackedCallback
>& callback
) {
82 if (TrackedCallback::IsPending(get_supported_profiles_callback_
))
83 return PP_ERROR_INPROGRESS
;
85 get_supported_profiles_callback_
= callback
;
86 Call
<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply
>(
87 RENDERER
, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(),
88 base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply
,
90 return PP_OK_COMPLETIONPENDING
;
93 int32_t VideoEncoderResource::GetFramesRequired() {
94 if (encoder_last_error_
)
95 return encoder_last_error_
;
96 return input_frame_count_
;
99 int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size
* size
) {
100 if (encoder_last_error_
)
101 return encoder_last_error_
;
102 *size
= input_coded_size_
;
106 int32_t VideoEncoderResource::Initialize(
107 PP_VideoFrame_Format input_format
,
108 const PP_Size
* input_visible_size
,
109 PP_VideoProfile output_profile
,
110 uint32_t initial_bitrate
,
111 PP_HardwareAcceleration acceleration
,
112 const scoped_refptr
<TrackedCallback
>& callback
) {
114 return PP_ERROR_FAILED
;
115 if (TrackedCallback::IsPending(initialize_callback_
))
116 return PP_ERROR_INPROGRESS
;
118 initialize_callback_
= callback
;
119 Call
<PpapiPluginMsg_VideoEncoder_InitializeReply
>(
120 RENDERER
, PpapiHostMsg_VideoEncoder_Initialize(
121 input_format
, *input_visible_size
, output_profile
,
122 initial_bitrate
, acceleration
),
123 base::Bind(&VideoEncoderResource::OnPluginMsgInitializeReply
, this));
124 return PP_OK_COMPLETIONPENDING
;
127 int32_t VideoEncoderResource::GetVideoFrame(
128 PP_Resource
* video_frame
,
129 const scoped_refptr
<TrackedCallback
>& callback
) {
130 if (encoder_last_error_
)
131 return encoder_last_error_
;
133 if (TrackedCallback::IsPending(get_video_frame_callback_
))
134 return PP_ERROR_INPROGRESS
;
136 get_video_frame_data_
= video_frame
;
137 get_video_frame_callback_
= callback
;
139 // Lazily ask for a shared memory buffer in which video frames are allocated.
140 if (buffer_manager_
.number_of_buffers() == 0) {
141 Call
<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply
>(
142 RENDERER
, PpapiHostMsg_VideoEncoder_GetVideoFrames(),
143 base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply
,
146 TryWriteVideoFrame();
149 return PP_OK_COMPLETIONPENDING
;
152 int32_t VideoEncoderResource::Encode(
153 PP_Resource video_frame
,
154 PP_Bool force_keyframe
,
155 const scoped_refptr
<TrackedCallback
>& callback
) {
156 if (encoder_last_error_
)
157 return encoder_last_error_
;
159 VideoFrameMap::iterator it
= video_frames_
.find(video_frame
);
160 if (it
== video_frames_
.end())
161 // TODO(llandwerlin): accept MediaStreamVideoTrack's video frames.
162 return PP_ERROR_BADRESOURCE
;
164 scoped_refptr
<VideoFrameResource
> frame_resource
= it
->second
;
166 encode_callbacks_
.insert(std::make_pair(video_frame
, callback
));
168 Call
<PpapiPluginMsg_VideoEncoder_EncodeReply
>(
170 PpapiHostMsg_VideoEncoder_Encode(frame_resource
->GetBufferIndex(),
171 PP_ToBool(force_keyframe
)),
172 base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply
, this,
175 // Invalidate the frame to prevent the plugin from modifying it.
176 it
->second
->Invalidate();
177 video_frames_
.erase(it
);
179 return PP_OK_COMPLETIONPENDING
;
182 int32_t VideoEncoderResource::GetBitstreamBuffer(
183 PP_BitstreamBuffer
* bitstream_buffer
,
184 const scoped_refptr
<TrackedCallback
>& callback
) {
185 if (encoder_last_error_
)
186 return encoder_last_error_
;
187 if (TrackedCallback::IsPending(get_bitstream_buffer_callback_
))
188 return PP_ERROR_INPROGRESS
;
190 get_bitstream_buffer_callback_
= callback
;
191 get_bitstream_buffer_data_
= bitstream_buffer
;
193 if (!available_bitstream_buffers_
.empty()) {
194 BitstreamBuffer
buffer(available_bitstream_buffers_
.front());
195 available_bitstream_buffers_
.pop_front();
196 WriteBitstreamBuffer(buffer
);
199 return PP_OK_COMPLETIONPENDING
;
202 void VideoEncoderResource::RecycleBitstreamBuffer(
203 const PP_BitstreamBuffer
* bitstream_buffer
) {
204 if (encoder_last_error_
)
206 BitstreamBufferMap::const_iterator iter
=
207 bitstream_buffer_map_
.find(bitstream_buffer
->buffer
);
208 if (iter
!= bitstream_buffer_map_
.end()) {
210 PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer(iter
->second
));
214 void VideoEncoderResource::RequestEncodingParametersChange(uint32_t bitrate
,
215 uint32_t framerate
) {
216 if (encoder_last_error_
)
218 Post(RENDERER
, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange(
219 bitrate
, framerate
));
222 void VideoEncoderResource::Close() {
225 Post(RENDERER
, PpapiHostMsg_VideoEncoder_Close());
227 if (!encoder_last_error_
|| !initialized_
)
228 NotifyError(PP_ERROR_ABORTED
);
232 void VideoEncoderResource::OnReplyReceived(
233 const ResourceMessageReplyParams
& params
,
234 const IPC::Message
& msg
) {
235 PPAPI_BEGIN_MESSAGE_MAP(VideoEncoderResource
, msg
)
236 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
237 PpapiPluginMsg_VideoEncoder_BitstreamBuffers
,
238 OnPluginMsgBitstreamBuffers
)
239 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
240 PpapiPluginMsg_VideoEncoder_BitstreamBufferReady
,
241 OnPluginMsgBitstreamBufferReady
)
242 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoEncoder_NotifyError
,
243 OnPluginMsgNotifyError
)
244 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
245 PluginResource::OnReplyReceived(params
, msg
))
246 PPAPI_END_MESSAGE_MAP()
249 void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply(
250 const PP_ArrayOutput
& output
,
251 const ResourceMessageReplyParams
& params
,
252 const std::vector
<PP_VideoProfileDescription
>& profiles
) {
253 int32_t error
= params
.result();
259 ArrayWriter
writer(output
);
260 if (!writer
.is_valid()) {
261 RunCallback(&get_supported_profiles_callback_
, PP_ERROR_BADARGUMENT
);
265 if (!writer
.StoreVector(profiles
)) {
266 RunCallback(&get_supported_profiles_callback_
, PP_ERROR_FAILED
);
270 RunCallback(&get_supported_profiles_callback_
, PP_OK
);
273 void VideoEncoderResource::OnPluginMsgInitializeReply(
274 const ResourceMessageReplyParams
& params
,
275 uint32_t input_frame_count
,
276 const PP_Size
& input_coded_size
) {
277 DCHECK(!initialized_
);
279 encoder_last_error_
= params
.result();
280 if (!encoder_last_error_
)
283 input_frame_count_
= input_frame_count
;
284 input_coded_size_
= input_coded_size
;
286 RunCallback(&initialize_callback_
, encoder_last_error_
);
289 void VideoEncoderResource::OnPluginMsgGetVideoFramesReply(
290 const ResourceMessageReplyParams
& params
,
291 uint32_t frame_count
,
292 uint32_t frame_length
,
293 const PP_Size
& frame_size
) {
294 int32_t error
= params
.result();
300 base::SharedMemoryHandle buffer_handle
;
301 params
.TakeSharedMemoryHandleAtIndex(0, &buffer_handle
);
303 if (!buffer_manager_
.SetBuffers(
304 frame_count
, frame_length
,
305 make_scoped_ptr(new base::SharedMemory(buffer_handle
, false)),
307 NotifyError(PP_ERROR_FAILED
);
311 if (TrackedCallback::IsPending(get_video_frame_callback_
))
312 TryWriteVideoFrame();
315 void VideoEncoderResource::OnPluginMsgEncodeReply(
316 PP_Resource video_frame
,
317 const ResourceMessageReplyParams
& params
,
319 DCHECK_NE(encode_callbacks_
.size(), 0U);
320 encoder_last_error_
= params
.result();
322 EncodeMap::iterator it
= encode_callbacks_
.find(video_frame
);
323 DCHECK(encode_callbacks_
.end() != it
);
325 scoped_refptr
<TrackedCallback
> callback
= it
->second
;
326 encode_callbacks_
.erase(it
);
327 RunCallback(&callback
, encoder_last_error_
);
329 buffer_manager_
.EnqueueBuffer(frame_id
);
330 // If the plugin is waiting for a video frame, we can give the one
331 // that just became available again.
332 if (TrackedCallback::IsPending(get_video_frame_callback_
))
333 TryWriteVideoFrame();
336 void VideoEncoderResource::OnPluginMsgBitstreamBuffers(
337 const ResourceMessageReplyParams
& params
,
338 uint32_t buffer_length
) {
339 std::vector
<base::SharedMemoryHandle
> shm_handles
;
340 params
.TakeAllSharedMemoryHandles(&shm_handles
);
341 if (shm_handles
.size() == 0) {
342 NotifyError(PP_ERROR_FAILED
);
346 for (uint32_t i
= 0; i
< shm_handles
.size(); ++i
) {
347 scoped_ptr
<base::SharedMemory
> shm(
348 new base::SharedMemory(shm_handles
[i
], true));
349 CHECK(shm
->Map(buffer_length
));
351 ShmBuffer
* buffer
= new ShmBuffer(i
, shm
.Pass());
352 shm_buffers_
.push_back(buffer
);
353 bitstream_buffer_map_
.insert(
354 std::make_pair(buffer
->shm
->memory(), buffer
->id
));
358 void VideoEncoderResource::OnPluginMsgBitstreamBufferReady(
359 const ResourceMessageReplyParams
& params
,
361 uint32_t buffer_size
,
363 available_bitstream_buffers_
.push_back(
364 BitstreamBuffer(buffer_id
, buffer_size
, key_frame
));
366 if (TrackedCallback::IsPending(get_bitstream_buffer_callback_
)) {
367 BitstreamBuffer
buffer(available_bitstream_buffers_
.front());
368 available_bitstream_buffers_
.pop_front();
369 WriteBitstreamBuffer(buffer
);
373 void VideoEncoderResource::OnPluginMsgNotifyError(
374 const ResourceMessageReplyParams
& params
,
379 void VideoEncoderResource::NotifyError(int32_t error
) {
380 encoder_last_error_
= error
;
381 RunCallback(&get_supported_profiles_callback_
, error
);
382 RunCallback(&initialize_callback_
, error
);
383 RunCallback(&get_video_frame_callback_
, error
);
384 get_video_frame_data_
= nullptr;
385 RunCallback(&get_bitstream_buffer_callback_
, error
);
386 get_bitstream_buffer_data_
= nullptr;
387 for (EncodeMap::iterator it
= encode_callbacks_
.begin();
388 it
!= encode_callbacks_
.end(); ++it
) {
389 scoped_refptr
<TrackedCallback
> callback
= it
->second
;
390 RunCallback(&callback
, error
);
392 encode_callbacks_
.clear();
395 void VideoEncoderResource::TryWriteVideoFrame() {
396 DCHECK(TrackedCallback::IsPending(get_video_frame_callback_
));
398 int32_t frame_id
= buffer_manager_
.DequeueBuffer();
402 scoped_refptr
<VideoFrameResource
> resource
= new VideoFrameResource(
403 pp_instance(), frame_id
, buffer_manager_
.GetBufferPointer(frame_id
));
404 video_frames_
.insert(
405 VideoFrameMap::value_type(resource
->pp_resource(), resource
));
407 *get_video_frame_data_
= resource
->GetReference();
408 get_video_frame_data_
= nullptr;
409 RunCallback(&get_video_frame_callback_
, PP_OK
);
412 void VideoEncoderResource::WriteBitstreamBuffer(const BitstreamBuffer
& buffer
) {
413 DCHECK_LT(buffer
.id
, shm_buffers_
.size());
415 get_bitstream_buffer_data_
->size
= buffer
.size
;
416 get_bitstream_buffer_data_
->buffer
= shm_buffers_
[buffer
.id
]->shm
->memory();
417 get_bitstream_buffer_data_
->key_frame
= PP_FromBool(buffer
.key_frame
);
418 get_bitstream_buffer_data_
= nullptr;
419 RunCallback(&get_bitstream_buffer_callback_
, PP_OK
);
422 void VideoEncoderResource::ReleaseFrames() {
423 for (VideoFrameMap::iterator it
= video_frames_
.begin();
424 it
!= video_frames_
.end(); ++it
) {
425 it
->second
->Invalidate();
426 it
->second
= nullptr;
428 video_frames_
.clear();