OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014 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 <math.h> |
| 6 #include <stdio.h> |
| 7 #include <string.h> |
| 8 |
| 9 #include <iostream> |
| 10 #include <sstream> |
| 11 #include <vector> |
| 12 |
| 13 #include "ppapi/c/pp_errors.h" |
| 14 #include "ppapi/c/ppb_console.h" |
| 15 #include "ppapi/cpp/input_event.h" |
| 16 #include "ppapi/cpp/instance.h" |
| 17 #include "ppapi/cpp/media_stream_video_track.h" |
| 18 #include "ppapi/cpp/module.h" |
| 19 #include "ppapi/cpp/rect.h" |
| 20 #include "ppapi/cpp/var.h" |
| 21 #include "ppapi/cpp/var_array_buffer.h" |
| 22 #include "ppapi/cpp/var_dictionary.h" |
| 23 #include "ppapi/cpp/video_encoder.h" |
| 24 #include "ppapi/cpp/video_frame.h" |
| 25 #include "ppapi/utility/completion_callback_factory.h" |
| 26 |
| 27 // Use assert as a poor-man's CHECK, even in non-debug mode. |
| 28 // Since <assert.h> redefines assert on every inclusion (it doesn't use |
| 29 // include-guards), make sure this is the last file #include'd in this file. |
| 30 #undef NDEBUG |
| 31 #include <assert.h> |
| 32 |
| 33 namespace { |
| 34 |
| 35 // This object is the global object representing this plugin library as long |
| 36 // as it is loaded. |
| 37 class MediaStreamVideoEncoderModule : public pp::Module { |
| 38 public: |
| 39 MediaStreamVideoEncoderModule() : pp::Module() {} |
| 40 virtual ~MediaStreamVideoEncoderModule() {} |
| 41 |
| 42 virtual pp::Instance* CreateInstance(PP_Instance instance); |
| 43 }; |
| 44 |
| 45 class MediaStreamVideoEncoderInstance : public pp::Instance { |
| 46 public: |
| 47 MediaStreamVideoEncoderInstance(PP_Instance instance, pp::Module* module); |
| 48 virtual ~MediaStreamVideoEncoderInstance(); |
| 49 |
| 50 // pp::Instance implementation. |
| 51 virtual void DidChangeView(const pp::Rect& position, |
| 52 const pp::Rect& clip_ignored); |
| 53 virtual void HandleMessage(const pp::Var& var_message); |
| 54 |
| 55 private: |
| 56 void ConfigureTrack(); |
| 57 void OnConfiguredTrack(int32_t result); |
| 58 void InitializeEncoder(); |
| 59 void OnInitializedEncoder(int32_t result); |
| 60 void GetTrackFrame(); |
| 61 void OnGetTrackFrame(int32_t result, const pp::VideoFrame& frame); |
| 62 void OnGetEncoderFrame(int32_t result, |
| 63 pp::VideoFrame encoder_frame, |
| 64 pp::VideoFrame track_frame); |
| 65 void OnEncodeDone(int32_t result, pp::VideoFrame encoder_frame); |
| 66 void OnGetBitstreamBuffer(int32_t result, PP_BitstreamBuffer buffer); |
| 67 void StopEncode(); |
| 68 |
| 69 void LogError(int32_t error, std::string message); |
| 70 void LogWarning(std::string message); |
| 71 |
| 72 void PostDataMessage(const void* buffer, uint32_t size); |
| 73 void PostSignalMessage(const char* name); |
| 74 |
| 75 bool is_encoding_; |
| 76 |
| 77 pp::VideoEncoder video_encoder_; |
| 78 pp::MediaStreamVideoTrack video_track_; |
| 79 pp::CompletionCallbackFactory<MediaStreamVideoEncoderInstance> |
| 80 callback_factory_; |
| 81 std::vector<int32_t> attrib_list_; |
| 82 |
| 83 pp::VideoFrame last_track_frame_; |
| 84 |
| 85 PP_VideoFrame_Format frame_format_; |
| 86 |
| 87 pp::Size frame_size_; |
| 88 pp::Size plugin_size_; |
| 89 pp::Size encoder_size_; |
| 90 int32_t encoder_frames_; |
| 91 }; |
| 92 |
| 93 MediaStreamVideoEncoderInstance::MediaStreamVideoEncoderInstance( |
| 94 PP_Instance instance, |
| 95 pp::Module* module) |
| 96 : pp::Instance(instance), |
| 97 is_encoding_(false), |
| 98 callback_factory_(this), |
| 99 frame_format_(PP_VIDEOFRAME_FORMAT_I420) { |
| 100 } |
| 101 |
| 102 MediaStreamVideoEncoderInstance::~MediaStreamVideoEncoderInstance() { |
| 103 } |
| 104 |
| 105 // |
| 106 // |
| 107 // |
| 108 |
| 109 void MediaStreamVideoEncoderInstance::DidChangeView( |
| 110 const pp::Rect& position, |
| 111 const pp::Rect& clip_ignored) { |
| 112 plugin_size_ = position.size(); |
| 113 } |
| 114 |
| 115 void MediaStreamVideoEncoderInstance::ConfigureTrack() { |
| 116 if (encoder_size_.IsEmpty()) |
| 117 frame_size_ = plugin_size_; |
| 118 else |
| 119 frame_size_ = encoder_size_; |
| 120 |
| 121 int32_t attrib_list[] = {PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT, |
| 122 frame_format_, |
| 123 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH, |
| 124 frame_size_.width(), |
| 125 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT, |
| 126 frame_size_.height(), |
| 127 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE}; |
| 128 |
| 129 pp::VarDictionary dict; |
| 130 dict.Set(pp::Var("status"), pp::Var("configuring video track")); |
| 131 dict.Set(pp::Var("width"), pp::Var(frame_size_.width())); |
| 132 dict.Set(pp::Var("height"), pp::Var(frame_size_.height())); |
| 133 PostMessage(dict); |
| 134 |
| 135 video_track_.Configure( |
| 136 attrib_list, callback_factory_.NewCallback( |
| 137 &MediaStreamVideoEncoderInstance::OnConfiguredTrack)); |
| 138 } |
| 139 |
| 140 void MediaStreamVideoEncoderInstance::OnConfiguredTrack(int32_t result) { |
| 141 if (result != PP_OK) { |
| 142 LogError(result, "Cannot configure track"); |
| 143 return; |
| 144 } |
| 145 |
| 146 InitializeEncoder(); |
| 147 GetTrackFrame(); |
| 148 } |
| 149 |
| 150 void MediaStreamVideoEncoderInstance::InitializeEncoder() { |
| 151 video_encoder_ = pp::VideoEncoder(this); |
| 152 encoder_frames_ = 0; |
| 153 |
| 154 pp::VarDictionary dict; |
| 155 dict.Set(pp::Var("status"), pp::Var("initializing encoder")); |
| 156 dict.Set(pp::Var("width"), pp::Var(encoder_size_.width())); |
| 157 dict.Set(pp::Var("height"), pp::Var(encoder_size_.height())); |
| 158 PostMessage(dict); |
| 159 |
| 160 int32_t error = video_encoder_.Initialize( |
| 161 frame_format_, frame_size_, PP_VIDEOPROFILE_H264MAIN, 2000000, |
| 162 PP_HARDWAREACCELERATION_ONLY, |
| 163 callback_factory_.NewCallback( |
| 164 &MediaStreamVideoEncoderInstance::OnInitializedEncoder)); |
| 165 if (error != PP_OK_COMPLETIONPENDING) { |
| 166 LogError(error, "Cannot initialize encoder"); |
| 167 return; |
| 168 } |
| 169 |
| 170 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput( |
| 171 &MediaStreamVideoEncoderInstance::OnGetBitstreamBuffer)); |
| 172 } |
| 173 |
| 174 void MediaStreamVideoEncoderInstance::OnInitializedEncoder(int32_t result) { |
| 175 if (result != PP_OK) { |
| 176 LogError(result, "Encoder initialization failed"); |
| 177 return; |
| 178 } |
| 179 |
| 180 if (video_encoder_.GetFrameCodedSize(&encoder_size_) != PP_OK) { |
| 181 LogError(result, "Cannot get coded size from encoder"); |
| 182 return; |
| 183 } |
| 184 |
| 185 is_encoding_ = true; |
| 186 |
| 187 pp::VarDictionary dict; |
| 188 dict.Set(pp::Var("status"), pp::Var("encoder initialized")); |
| 189 dict.Set(pp::Var("width"), pp::Var(encoder_size_.width())); |
| 190 dict.Set(pp::Var("height"), pp::Var(encoder_size_.height())); |
| 191 PostMessage(dict); |
| 192 |
| 193 if (frame_size_ != encoder_size_) |
| 194 ConfigureTrack(); |
| 195 else |
| 196 GetTrackFrame(); |
| 197 } |
| 198 |
| 199 void MediaStreamVideoEncoderInstance::GetTrackFrame() { |
| 200 if (!is_encoding_) |
| 201 return; |
| 202 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput( |
| 203 &MediaStreamVideoEncoderInstance::OnGetTrackFrame)); |
| 204 } |
| 205 |
| 206 void MediaStreamVideoEncoderInstance::OnGetTrackFrame( |
| 207 int32_t result, |
| 208 const pp::VideoFrame& frame) { |
| 209 if (result == PP_ERROR_ABORTED) |
| 210 return; |
| 211 if (result != PP_OK) { |
| 212 LogError(result, "Cannot get video frame from video track"); |
| 213 return; |
| 214 } |
| 215 |
| 216 frame.GetSize(&frame_size_); |
| 217 |
| 218 if (frame_size_ != encoder_size_) { |
| 219 LogError(PP_ERROR_FAILED, "MediaStreamVideoTrack frame size incorrect"); |
| 220 return; |
| 221 } |
| 222 |
| 223 if (encoder_frames_ >= 4) { |
| 224 LogWarning("Too many frames waiting to be encoded, dropping"); |
| 225 video_track_.RecycleFrame(frame); |
| 226 GetTrackFrame(); |
| 227 } else { |
| 228 video_encoder_.GetVideoFrame(callback_factory_.NewCallbackWithOutput( |
| 229 &MediaStreamVideoEncoderInstance::OnGetEncoderFrame, frame)); |
| 230 } |
| 231 } |
| 232 |
| 233 void MediaStreamVideoEncoderInstance::OnGetEncoderFrame( |
| 234 int32_t result, |
| 235 pp::VideoFrame encoder_frame, |
| 236 pp::VideoFrame track_frame) { |
| 237 if (result == PP_ERROR_ABORTED) |
| 238 return; |
| 239 if (result != PP_OK) { |
| 240 LogError(result, "Cannot get video frame from video encoder"); |
| 241 return; |
| 242 } |
| 243 |
| 244 if (encoder_frame.GetDataBufferSize() < track_frame.GetDataBufferSize()) { |
| 245 std::ostringstream oss; |
| 246 oss << "Incorrect encoder video frame buffer size : " |
| 247 << encoder_frame.GetDataBufferSize() << " < " |
| 248 << track_frame.GetDataBufferSize(); |
| 249 LogError(PP_ERROR_FAILED, oss.str()); |
| 250 return; |
| 251 } |
| 252 |
| 253 memcpy(encoder_frame.GetDataBuffer(), track_frame.GetDataBuffer(), |
| 254 track_frame.GetDataBufferSize()); |
| 255 |
| 256 video_track_.RecycleFrame(track_frame); |
| 257 |
| 258 video_encoder_.Encode( |
| 259 encoder_frame, PP_FALSE, |
| 260 callback_factory_.NewCallback( |
| 261 &MediaStreamVideoEncoderInstance::OnEncodeDone, encoder_frame)); |
| 262 encoder_frames_++; |
| 263 GetTrackFrame(); |
| 264 } |
| 265 |
| 266 void MediaStreamVideoEncoderInstance::OnEncodeDone( |
| 267 int32_t result, |
| 268 pp::VideoFrame encoder_frame) { |
| 269 encoder_frames_--; |
| 270 } |
| 271 |
| 272 void MediaStreamVideoEncoderInstance::OnGetBitstreamBuffer( |
| 273 int32_t result, |
| 274 PP_BitstreamBuffer buffer) { |
| 275 if (result == PP_ERROR_ABORTED) |
| 276 return; |
| 277 if (result != PP_OK) { |
| 278 LogError(result, "Cannot get bitstream buffer"); |
| 279 return; |
| 280 } |
| 281 |
| 282 PostDataMessage(buffer.buffer, buffer.size); |
| 283 video_encoder_.RecycleBitstreamBuffer(buffer); |
| 284 |
| 285 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput( |
| 286 &MediaStreamVideoEncoderInstance::OnGetBitstreamBuffer)); |
| 287 } |
| 288 |
| 289 void MediaStreamVideoEncoderInstance::StopEncode() { |
| 290 video_track_.Close(); |
| 291 video_track_.detach(); |
| 292 is_encoding_ = false; |
| 293 } |
| 294 |
| 295 // |
| 296 |
| 297 void MediaStreamVideoEncoderInstance::HandleMessage( |
| 298 const pp::Var& var_message) { |
| 299 if (!var_message.is_dictionary()) { |
| 300 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid message!")); |
| 301 return; |
| 302 } |
| 303 |
| 304 pp::VarDictionary var_dictionary_message(var_message); |
| 305 std::string command = var_dictionary_message.Get("command").AsString(); |
| 306 |
| 307 if (command == "start") { |
| 308 pp::Var var_track = var_dictionary_message.Get("track"); |
| 309 if (!var_track.is_resource()) { |
| 310 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Given track is not a resource")); |
| 311 return; |
| 312 } |
| 313 pp::Resource resource_track = var_track.AsResource(); |
| 314 video_track_ = pp::MediaStreamVideoTrack(resource_track); |
| 315 ConfigureTrack(); |
| 316 } else if (command == "stop") { |
| 317 StopEncode(); |
| 318 PostSignalMessage("stopped"); |
| 319 } else { |
| 320 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid command!")); |
| 321 } |
| 322 } |
| 323 |
| 324 void MediaStreamVideoEncoderInstance::PostDataMessage(const void* buffer, |
| 325 uint32_t size) { |
| 326 pp::VarDictionary dictionary; |
| 327 |
| 328 dictionary.Set(pp::Var("name"), pp::Var("data")); |
| 329 |
| 330 pp::VarArrayBuffer array_buffer(size); |
| 331 void* data_ptr = array_buffer.Map(); |
| 332 memcpy(data_ptr, buffer, size); |
| 333 array_buffer.Unmap(); |
| 334 dictionary.Set(pp::Var("data"), array_buffer); |
| 335 |
| 336 PostMessage(dictionary); |
| 337 } |
| 338 |
| 339 void MediaStreamVideoEncoderInstance::PostSignalMessage(const char* name) { |
| 340 pp::VarDictionary dictionary; |
| 341 dictionary.Set(pp::Var("name"), pp::Var(name)); |
| 342 |
| 343 PostMessage(dictionary); |
| 344 } |
| 345 |
| 346 void MediaStreamVideoEncoderInstance::LogError(int32_t error, |
| 347 std::string message) { |
| 348 std::string msg("Error: "); |
| 349 msg.append(pp::Var(error).DebugString()); |
| 350 msg.append(" : "); |
| 351 msg.append(message); |
| 352 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var(msg)); |
| 353 } |
| 354 |
| 355 void MediaStreamVideoEncoderInstance::LogWarning(std::string message) { |
| 356 std::string msg("Warning: "); |
| 357 msg.append(message); |
| 358 LogToConsole(PP_LOGLEVEL_WARNING, pp::Var(msg)); |
| 359 } |
| 360 |
| 361 pp::Instance* MediaStreamVideoEncoderModule::CreateInstance( |
| 362 PP_Instance instance) { |
| 363 return new MediaStreamVideoEncoderInstance(instance, this); |
| 364 } |
| 365 |
| 366 } // anonymous namespace |
| 367 |
| 368 namespace pp { |
| 369 // Factory function for your specialization of the Module object. |
| 370 Module* CreateModule() { |
| 371 return new MediaStreamVideoEncoderModule(); |
| 372 } |
| 373 } // namespace pp |
OLD | NEW |