OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 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. |
| 4 |
| 5 #include "base/memory/shared_memory.h" |
| 6 #include "ppapi/c/pp_array_output.h" |
| 7 #include "ppapi/proxy/ppapi_messages.h" |
| 8 #include "ppapi/proxy/video_encoder_resource.h" |
| 9 #include "ppapi/proxy/video_frame_resource.h" |
| 10 #include "ppapi/shared_impl/media_stream_buffer.h" |
| 11 #include "ppapi/shared_impl/media_stream_buffer_manager.h" |
| 12 #include "ppapi/thunk/enter.h" |
| 13 |
| 14 using ppapi::proxy::SerializedHandle; |
| 15 using ppapi::thunk::EnterResourceNoLock; |
| 16 using ppapi::thunk::PPB_VideoEncoder_API; |
| 17 |
| 18 namespace ppapi { |
| 19 namespace proxy { |
| 20 |
| 21 namespace { |
| 22 |
| 23 class BufferManagerDelegate : public MediaStreamBufferManager::Delegate { |
| 24 public: |
| 25 BufferManagerDelegate() {} |
| 26 ~BufferManagerDelegate() override {} |
| 27 |
| 28 private: |
| 29 DISALLOW_COPY_AND_ASSIGN(BufferManagerDelegate); |
| 30 }; |
| 31 |
| 32 void RunCallback(scoped_refptr<TrackedCallback>* callback, int32_t error) { |
| 33 if (!TrackedCallback::IsPending(*callback)) |
| 34 return; |
| 35 |
| 36 scoped_refptr<TrackedCallback> temp; |
| 37 callback->swap(temp); |
| 38 temp->Run(error); |
| 39 } |
| 40 |
| 41 } // namespace |
| 42 |
| 43 VideoEncoderResource::ShmBuffer::ShmBuffer(base::SharedMemoryHandle handle, |
| 44 uint32_t id, |
| 45 uint32_t size) |
| 46 : shm(new base::SharedMemory(handle, true)), id(id), size(size) { |
| 47 if (shm) |
| 48 shm->Map(size); |
| 49 } |
| 50 |
| 51 VideoEncoderResource::ShmBuffer::~ShmBuffer() { |
| 52 } |
| 53 |
| 54 VideoEncoderResource::BitstreamBuffer::BitstreamBuffer(uint32_t id, |
| 55 uint32_t size, |
| 56 bool key_frame) |
| 57 : id(id), size(size), key_frame(key_frame) { |
| 58 } |
| 59 |
| 60 VideoEncoderResource::BitstreamBuffer::BitstreamBuffer( |
| 61 const BitstreamBuffer& other) |
| 62 : id(other.id), size(other.size), key_frame(other.key_frame) { |
| 63 } |
| 64 |
| 65 VideoEncoderResource::BitstreamBuffer::~BitstreamBuffer() { |
| 66 } |
| 67 |
| 68 VideoEncoderResource::VideoEncoderResource(Connection connection, |
| 69 PP_Instance instance) |
| 70 : PluginResource(connection, instance), |
| 71 initialized_(false), |
| 72 // Set |encoder_last_error_| to PP_OK after successful initialization. |
| 73 // This makes error checking a little more concise, since we can check |
| 74 // that the encoder has been initialized and hasn't returned an error by |
| 75 // just testing |encoder_last_error_|. |
| 76 encoder_last_error_(PP_ERROR_FAILED), |
| 77 encoder_frame_count_(0), |
| 78 waiting_for_video_frames_(false), |
| 79 buffer_manager_delegate_(new BufferManagerDelegate()), |
| 80 media_stream_buffer_manager_( |
| 81 new MediaStreamBufferManager(buffer_manager_delegate_.get())) { |
| 82 SendCreate(RENDERER, PpapiHostMsg_VideoEncoder_Create()); |
| 83 } |
| 84 |
| 85 VideoEncoderResource::~VideoEncoderResource() { |
| 86 encoder_last_error_ = PP_ERROR_ABORTED; |
| 87 RunCallback(&initialize_callback_, encoder_last_error_); |
| 88 RunCallback(&get_bitstreamer_buffer_callback_, encoder_last_error_); |
| 89 NotifyGetVideoFrameCallbacksError(encoder_last_error_); |
| 90 |
| 91 for (VideoFrameMap::iterator it = video_frames_.begin(); |
| 92 it != video_frames_.end(); ++it) { |
| 93 it->second->Invalidate(); |
| 94 it->second = nullptr; |
| 95 } |
| 96 } |
| 97 |
| 98 PPB_VideoEncoder_API* VideoEncoderResource::AsPPB_VideoEncoder_API() { |
| 99 return this; |
| 100 } |
| 101 |
| 102 int32_t VideoEncoderResource::GetSupportedProfiles( |
| 103 const PP_ArrayOutput& output, |
| 104 const scoped_refptr<TrackedCallback>& callback) { |
| 105 // TODO(llandwerlin): should we prevent concurrent calls? |
| 106 Call<PpapiPluginMsg_VideoEncoder_GetSupportedProfilesReply>( |
| 107 RENDERER, PpapiHostMsg_VideoEncoder_GetSupportedProfiles(), |
| 108 base::Bind(&VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply, |
| 109 this, output, callback)); |
| 110 return PP_OK_COMPLETIONPENDING; |
| 111 } |
| 112 |
| 113 int32_t VideoEncoderResource::GetFramesRequired() { |
| 114 if (encoder_last_error_) |
| 115 return encoder_last_error_; |
| 116 return encoder_frame_count_; |
| 117 } |
| 118 |
| 119 int32_t VideoEncoderResource::GetFrameCodedSize(PP_Size* size) { |
| 120 if (encoder_last_error_) |
| 121 return encoder_last_error_; |
| 122 *size = encoder_coded_size_; |
| 123 return PP_OK; |
| 124 } |
| 125 |
| 126 int32_t VideoEncoderResource::Initialize( |
| 127 PP_VideoFrame_Format input_format, |
| 128 const PP_Size* input_visible_size, |
| 129 PP_VideoProfile output_profile, |
| 130 uint32_t initial_bitrate, |
| 131 PP_HardwareAcceleration acceleration, |
| 132 const scoped_refptr<TrackedCallback>& callback) { |
| 133 if (initialized_) |
| 134 return PP_ERROR_FAILED; |
| 135 if (TrackedCallback::IsPending(initialize_callback_)) |
| 136 return PP_ERROR_INPROGRESS; |
| 137 |
| 138 initialize_callback_ = callback; |
| 139 Call<PpapiPluginMsg_VideoEncoder_InitializeReply>( |
| 140 RENDERER, PpapiHostMsg_VideoEncoder_Initialize( |
| 141 input_format, *input_visible_size, output_profile, |
| 142 initial_bitrate, acceleration), |
| 143 base::Bind(&VideoEncoderResource::OnPluginMsgInitializeReply, this)); |
| 144 return PP_OK_COMPLETIONPENDING; |
| 145 } |
| 146 |
| 147 int32_t VideoEncoderResource::GetVideoFrame( |
| 148 PP_Resource* video_frame, |
| 149 const scoped_refptr<TrackedCallback>& callback) { |
| 150 if (encoder_last_error_) |
| 151 return encoder_last_error_; |
| 152 |
| 153 get_video_frame_cbs_.push_back(std::make_pair(video_frame, callback)); |
| 154 |
| 155 // Lazily ask for video frames buffers. |
| 156 if (media_stream_buffer_manager_->number_of_buffers() < 1) { |
| 157 // Check that we haven't ask for the buffers already. |
| 158 if (!waiting_for_video_frames_) { |
| 159 waiting_for_video_frames_ = true; |
| 160 Call<PpapiPluginMsg_VideoEncoder_GetVideoFramesReply>( |
| 161 RENDERER, PpapiHostMsg_VideoEncoder_GetVideoFrames(), |
| 162 base::Bind(&VideoEncoderResource::OnPluginMsgGetVideoFramesReply, |
| 163 this)); |
| 164 } |
| 165 return PP_OK_COMPLETIONPENDING; |
| 166 } |
| 167 |
| 168 NotifyGetVideoFrameCallbacks(); |
| 169 |
| 170 return PP_OK_COMPLETIONPENDING; |
| 171 } |
| 172 |
| 173 int32_t VideoEncoderResource::Encode( |
| 174 PP_Resource video_frame, |
| 175 PP_Bool force_keyframe, |
| 176 const scoped_refptr<TrackedCallback>& callback) { |
| 177 if (encoder_last_error_) |
| 178 return encoder_last_error_; |
| 179 |
| 180 EnterResourceNoLock<thunk::PPB_VideoFrame_API> enter(video_frame, true); |
| 181 if (enter.failed()) |
| 182 return PP_ERROR_BADRESOURCE; |
| 183 |
| 184 thunk::PPB_VideoFrame_API* ppb_video_frame = enter.object(); |
| 185 |
| 186 if (media_stream_buffer_manager_->ContainsBuffer( |
| 187 ppb_video_frame->GetBuffer())) { |
| 188 int32_t frame_id = ppb_video_frame->GetBufferIndex(); |
| 189 Call<PpapiPluginMsg_VideoEncoder_EncodeReply>( |
| 190 RENDERER, PpapiHostMsg_VideoEncoder_Encode(frame_id, force_keyframe), |
| 191 base::Bind(&VideoEncoderResource::OnPluginMsgEncodeReply, this, |
| 192 callback)); |
| 193 } else { |
| 194 // TODO(llandwerlin): deal MediaStreamVideoTrack's video frames. |
| 195 return PP_ERROR_BADARGUMENT; |
| 196 } |
| 197 |
| 198 return PP_OK_COMPLETIONPENDING; |
| 199 } |
| 200 |
| 201 int32_t VideoEncoderResource::GetBitstreamBuffer( |
| 202 PP_BitstreamBuffer* bitstream_buffer, |
| 203 const scoped_refptr<TrackedCallback>& callback) { |
| 204 if (TrackedCallback::IsPending(get_bitstreamer_buffer_callback_)) |
| 205 return PP_ERROR_INPROGRESS; |
| 206 |
| 207 if (available_bitstream_buffers_.empty()) { |
| 208 get_bitstreamer_buffer_callback_ = callback; |
| 209 get_bitstreamer_buffer_data_ = bitstream_buffer; |
| 210 return PP_OK_COMPLETIONPENDING; |
| 211 } |
| 212 |
| 213 BitstreamBuffer buffer(available_bitstream_buffers_.front()); |
| 214 |
| 215 available_bitstream_buffers_.pop_front(); |
| 216 WriteBitstreamerBuffer(bitstream_buffer, buffer); |
| 217 |
| 218 return PP_OK; |
| 219 } |
| 220 |
| 221 void VideoEncoderResource::RecycleBitstreamBuffer( |
| 222 const PP_BitstreamBuffer* bitstream_buffer) { |
| 223 BitstreamBufferMap::const_iterator iter = |
| 224 bitstream_buffers_map_.find(bitstream_buffer->buffer); |
| 225 if (iter != bitstream_buffers_map_.end()) |
| 226 Post(RENDERER, |
| 227 PpapiHostMsg_VideoEncoder_RecycleBitstreamBuffer(iter->second)); |
| 228 } |
| 229 |
| 230 void VideoEncoderResource::RequestEncodingParametersChange(uint32_t bitrate, |
| 231 uint32_t framerate) { |
| 232 Post(RENDERER, PpapiHostMsg_VideoEncoder_RequestEncodingParametersChange( |
| 233 bitrate, framerate)); |
| 234 } |
| 235 |
| 236 void VideoEncoderResource::OnReplyReceived( |
| 237 const ResourceMessageReplyParams& params, |
| 238 const IPC::Message& msg) { |
| 239 PPAPI_BEGIN_MESSAGE_MAP(VideoEncoderResource, msg) |
| 240 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( |
| 241 PpapiPluginMsg_VideoEncoder_BitstreamBufferReady, |
| 242 OnPluginMsgBitstreamBufferReady) |
| 243 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoDecoder_NotifyError, |
| 244 OnPluginMsgNotifyError) |
| 245 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED( |
| 246 PluginResource::OnReplyReceived(params, msg)) |
| 247 PPAPI_END_MESSAGE_MAP() |
| 248 } |
| 249 |
| 250 void VideoEncoderResource::OnPluginMsgGetSupportedProfilesReply( |
| 251 const PP_ArrayOutput& output, |
| 252 const scoped_refptr<TrackedCallback>& callback, |
| 253 const ResourceMessageReplyParams& params, |
| 254 const std::vector<PP_SupportedVideoProfile>& profiles) { |
| 255 void* ptr = output.GetDataBuffer(output.user_data, profiles.size(), |
| 256 sizeof(PP_SupportedVideoProfile)); |
| 257 |
| 258 if (!ptr) { |
| 259 callback->Run(PP_ERROR_BADARGUMENT); |
| 260 return; |
| 261 } |
| 262 |
| 263 memcpy(ptr, &profiles[0], profiles.size() * sizeof(PP_SupportedVideoProfile)); |
| 264 callback->Run(PP_OK); |
| 265 } |
| 266 |
| 267 void VideoEncoderResource::OnPluginMsgInitializeReply( |
| 268 const ResourceMessageReplyParams& params, |
| 269 uint32_t buffer_count, |
| 270 uint32_t buffer_length, |
| 271 uint32_t input_frame_count, |
| 272 const PP_Size& input_coded_size) { |
| 273 encoder_last_error_ = params.result(); |
| 274 if (encoder_last_error_) { |
| 275 RunCallback(&initialize_callback_, encoder_last_error_); |
| 276 return; |
| 277 } |
| 278 |
| 279 std::vector<base::SharedMemoryHandle> buffer_handles; |
| 280 params.TakeAllSharedMemoryHandles(&buffer_handles); |
| 281 |
| 282 CHECK(buffer_handles.size() >= buffer_count); |
| 283 |
| 284 bitstream_buffers_.clear(); |
| 285 bitstream_buffers_map_.clear(); |
| 286 for (uint32_t i = 0; i < buffer_count; ++i) { |
| 287 ShmBuffer* buffer = new ShmBuffer(buffer_handles[i], i, buffer_length); |
| 288 bitstream_buffers_.push_back(buffer); |
| 289 bitstream_buffers_map_.insert( |
| 290 std::make_pair(buffer->shm->memory(), buffer->id)); |
| 291 } |
| 292 |
| 293 encoder_frame_count_ = input_frame_count; |
| 294 encoder_coded_size_ = input_coded_size; |
| 295 initialized_ = true; |
| 296 RunCallback(&initialize_callback_, encoder_last_error_); |
| 297 } |
| 298 |
| 299 void VideoEncoderResource::OnPluginMsgGetVideoFramesReply( |
| 300 const ResourceMessageReplyParams& params, |
| 301 uint32_t frame_count, |
| 302 uint32_t frame_length, |
| 303 const PP_Size& frame_size) { |
| 304 encoder_last_error_ = params.result(); |
| 305 if (encoder_last_error_) { |
| 306 OnPluginMsgNotifyError(params, encoder_last_error_); |
| 307 return; |
| 308 } |
| 309 |
| 310 std::vector<base::SharedMemoryHandle> buffer_handles; |
| 311 params.TakeAllSharedMemoryHandles(&buffer_handles); |
| 312 CHECK(buffer_handles.size() == 1); |
| 313 |
| 314 if (!media_stream_buffer_manager_->SetBuffers( |
| 315 frame_count, frame_length, |
| 316 make_scoped_ptr(new base::SharedMemory(buffer_handles[0], false)), |
| 317 true)) { |
| 318 OnPluginMsgNotifyError(params, PP_ERROR_FAILED); |
| 319 return; |
| 320 } |
| 321 |
| 322 for (uint32_t frame_id = 0; frame_id < frame_count; frame_id++) { |
| 323 video_frames_[frame_id] = make_scoped_refptr(new VideoFrameResource( |
| 324 pp_instance(), frame_id, |
| 325 media_stream_buffer_manager_->GetBufferPointer(frame_id))); |
| 326 } |
| 327 waiting_for_video_frames_ = false; |
| 328 |
| 329 NotifyGetVideoFrameCallbacks(); |
| 330 } |
| 331 |
| 332 void VideoEncoderResource::OnPluginMsgEncodeReply( |
| 333 const scoped_refptr<TrackedCallback>& callback, |
| 334 const ResourceMessageReplyParams& params, |
| 335 uint32_t frame_id) { |
| 336 callback->Run(encoder_last_error_); |
| 337 media_stream_buffer_manager_->EnqueueBuffer(frame_id); |
| 338 NotifyGetVideoFrameCallbacks(); |
| 339 } |
| 340 |
| 341 void VideoEncoderResource::OnPluginMsgBitstreamBufferReady( |
| 342 const ResourceMessageReplyParams& params, |
| 343 uint32_t buffer_id, |
| 344 uint32_t buffer_size, |
| 345 bool key_frame) { |
| 346 available_bitstream_buffers_.push_back( |
| 347 BitstreamBuffer(buffer_id, buffer_size, key_frame)); |
| 348 |
| 349 if (TrackedCallback::IsPending(get_bitstreamer_buffer_callback_)) { |
| 350 BitstreamBuffer buffer(available_bitstream_buffers_.front()); |
| 351 available_bitstream_buffers_.pop_front(); |
| 352 WriteBitstreamerBuffer(get_bitstreamer_buffer_data_, buffer); |
| 353 get_bitstreamer_buffer_data_ = nullptr; |
| 354 |
| 355 scoped_refptr<TrackedCallback> callback; |
| 356 get_bitstreamer_buffer_callback_.swap(callback); |
| 357 callback->Run(PP_OK); |
| 358 } |
| 359 } |
| 360 |
| 361 void VideoEncoderResource::OnPluginMsgNotifyError( |
| 362 const ResourceMessageReplyParams& params, |
| 363 int32_t error) { |
| 364 encoder_last_error_ = error; |
| 365 RunCallback(&initialize_callback_, error); |
| 366 RunCallback(&get_bitstreamer_buffer_callback_, error); |
| 367 NotifyGetVideoFrameCallbacksError(error); |
| 368 } |
| 369 |
| 370 void VideoEncoderResource::NotifyGetVideoFrameCallbacks() { |
| 371 while (!get_video_frame_cbs_.empty() && |
| 372 media_stream_buffer_manager_->BuffersAvailable() > 0) { |
| 373 std::pair<PP_Resource*, scoped_refptr<TrackedCallback>> cb = |
| 374 get_video_frame_cbs_.front(); |
| 375 get_video_frame_cbs_.pop_front(); |
| 376 |
| 377 int32_t frame_id = media_stream_buffer_manager_->DequeueBuffer(); |
| 378 *cb.first = video_frames_[frame_id]->GetReference(); |
| 379 cb.second->Run(PP_OK); |
| 380 } |
| 381 } |
| 382 |
| 383 void VideoEncoderResource::NotifyGetVideoFrameCallbacksError(int32_t error) { |
| 384 while (!get_video_frame_cbs_.empty()) { |
| 385 std::pair<PP_Resource*, scoped_refptr<TrackedCallback>> cb = |
| 386 get_video_frame_cbs_.front(); |
| 387 get_video_frame_cbs_.pop_front(); |
| 388 cb.second->Run(error); |
| 389 } |
| 390 } |
| 391 |
| 392 void VideoEncoderResource::WriteBitstreamerBuffer( |
| 393 PP_BitstreamBuffer* bitstream_buffer, |
| 394 const BitstreamBuffer& buffer) { |
| 395 bitstream_buffer->size = buffer.size; |
| 396 bitstream_buffer->buffer = bitstream_buffers_[buffer.id]->shm->memory(); |
| 397 bitstream_buffer->key_frame = PP_FromBool(buffer.key_frame); |
| 398 } |
| 399 |
| 400 } // namespace proxy |
| 401 } // namespace ppapi |
OLD | NEW |