Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / video_encode / video_encode.cc
blob20b55a15a79db1559adfe9f31753495754dbc884
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 <math.h>
6 #include <stdio.h>
7 #include <string.h>
9 #include <algorithm>
10 #include <deque>
11 #include <iostream>
12 #include <map>
13 #include <sstream>
14 #include <vector>
16 #include "ppapi/c/pp_errors.h"
17 #include "ppapi/c/ppb_console.h"
18 #include "ppapi/cpp/input_event.h"
19 #include "ppapi/cpp/instance.h"
20 #include "ppapi/cpp/media_stream_video_track.h"
21 #include "ppapi/cpp/module.h"
22 #include "ppapi/cpp/rect.h"
23 #include "ppapi/cpp/var.h"
24 #include "ppapi/cpp/var_array_buffer.h"
25 #include "ppapi/cpp/var_dictionary.h"
26 #include "ppapi/cpp/video_encoder.h"
27 #include "ppapi/cpp/video_frame.h"
28 #include "ppapi/utility/completion_callback_factory.h"
30 // When compiling natively on Windows, PostMessage, min and max can be
31 // #define-d to something else.
32 #ifdef WIN32
33 #undef min
34 #undef max
35 #undef PostMessage
36 #endif
38 // Use assert as a poor-man's CHECK, even in non-debug mode.
39 // Since <assert.h> redefines assert on every inclusion (it doesn't use
40 // include-guards), make sure this is the last file #include'd in this file.
41 #undef NDEBUG
42 #include <assert.h>
44 #define fourcc(a, b, c, d) \
45 (((uint32_t)(a) << 0) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | \
46 ((uint32_t)(d) << 24))
48 namespace {
50 double clamp(double min, double max, double value) {
51 return std::max(std::min(value, max), min);
54 // IVF container writer. It is possible to parse H264 bitstream using
55 // NAL units but for VP8 we need a container to at least find encoded
56 // pictures as well as the picture sizes.
57 class IVFWriter {
58 public:
59 IVFWriter() {}
60 ~IVFWriter() {}
62 uint32_t GetFileHeaderSize() const { return 32; }
63 uint32_t GetFrameHeaderSize() const { return 12; }
64 uint32_t WriteFileHeader(uint8_t* mem, int32_t width, int32_t height);
65 uint32_t WriteFrameHeader(uint8_t* mem, uint64_t pts, size_t frame_size);
67 private:
68 void PutLE16(uint8_t* mem, int val) const {
69 mem[0] = (val >> 0) & 0xff;
70 mem[1] = (val >> 8) & 0xff;
72 void PutLE32(uint8_t* mem, int val) const {
73 mem[0] = (val >> 0) & 0xff;
74 mem[1] = (val >> 8) & 0xff;
75 mem[2] = (val >> 16) & 0xff;
76 mem[3] = (val >> 24) & 0xff;
80 uint32_t IVFWriter::WriteFileHeader(uint8_t* mem,
81 int32_t width,
82 int32_t height) {
83 mem[0] = 'D';
84 mem[1] = 'K';
85 mem[2] = 'I';
86 mem[3] = 'F';
87 PutLE16(mem + 4, 0); // version
88 PutLE16(mem + 6, 32); // header size
89 PutLE32(mem + 8, fourcc('V', 'P', '8', '0')); // fourcc
90 PutLE16(mem + 12, static_cast<uint16_t>(width)); // width
91 PutLE16(mem + 14, static_cast<uint16_t>(height)); // height
92 PutLE32(mem + 16, 1000); // rate
93 PutLE32(mem + 20, 1); // scale
94 PutLE32(mem + 24, 0xffffffff); // length
95 PutLE32(mem + 28, 0); // unused
97 return 32;
100 uint32_t IVFWriter::WriteFrameHeader(uint8_t* mem,
101 uint64_t pts,
102 size_t frame_size) {
103 PutLE32(mem, (int)frame_size);
104 PutLE32(mem + 4, (int)(pts & 0xFFFFFFFF));
105 PutLE32(mem + 8, (int)(pts >> 32));
107 return 12;
110 // This object is the global object representing this plugin library as long
111 // as it is loaded.
112 class VideoEncoderModule : public pp::Module {
113 public:
114 VideoEncoderModule() : pp::Module() {}
115 virtual ~VideoEncoderModule() {}
117 virtual pp::Instance* CreateInstance(PP_Instance instance);
120 class VideoEncoderInstance : public pp::Instance {
121 public:
122 VideoEncoderInstance(PP_Instance instance, pp::Module* module);
123 virtual ~VideoEncoderInstance();
125 // pp::Instance implementation.
126 virtual void HandleMessage(const pp::Var& var_message);
128 private:
129 void AddVideoProfile(PP_VideoProfile profile, const std::string& profile_str);
130 void InitializeVideoProfiles();
131 PP_VideoProfile VideoProfileFromString(const std::string& str);
132 std::string VideoProfileToString(PP_VideoProfile profile);
134 void ConfigureTrack();
135 void OnConfiguredTrack(int32_t result);
136 void ProbeEncoder();
137 void OnEncoderProbed(int32_t result,
138 const std::vector<PP_VideoProfileDescription> profiles);
139 void StartEncoder();
140 void OnInitializedEncoder(int32_t result);
141 void ScheduleNextEncode();
142 void GetEncoderFrameTick(int32_t result);
143 void GetEncoderFrame(const pp::VideoFrame& track_frame);
144 void OnEncoderFrame(int32_t result,
145 pp::VideoFrame encoder_frame,
146 pp::VideoFrame track_frame);
147 int32_t CopyVideoFrame(pp::VideoFrame dest, pp::VideoFrame src);
148 void EncodeFrame(const pp::VideoFrame& frame);
149 void OnEncodeDone(int32_t result);
150 void OnGetBitstreamBuffer(int32_t result, PP_BitstreamBuffer buffer);
151 void StartTrackFrames();
152 void StopTrackFrames();
153 void OnTrackFrame(int32_t result, pp::VideoFrame frame);
155 void StopEncode();
157 void LogError(int32_t error, const std::string& message);
158 void Log(const std::string& message);
160 void PostDataMessage(const void* buffer, uint32_t size);
162 typedef std::map<std::string, PP_VideoProfile> VideoProfileFromStringMap;
163 VideoProfileFromStringMap profile_from_string_;
165 typedef std::map<PP_VideoProfile, std::string> VideoProfileToStringMap;
166 VideoProfileToStringMap profile_to_string_;
168 bool is_encoding_;
169 bool is_encode_ticking_;
170 bool is_receiving_track_frames_;
172 pp::VideoEncoder video_encoder_;
173 pp::MediaStreamVideoTrack video_track_;
174 pp::CompletionCallbackFactory<VideoEncoderInstance> callback_factory_;
176 PP_VideoProfile video_profile_;
177 PP_VideoFrame_Format frame_format_;
179 pp::Size requested_size_;
180 pp::Size frame_size_;
181 pp::Size encoder_size_;
182 uint32_t encoded_frames_;
184 std::deque<uint64_t> frames_timestamps_;
186 pp::VideoFrame current_track_frame_;
188 IVFWriter ivf_writer_;
190 PP_Time last_encode_tick_;
193 VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance,
194 pp::Module* module)
195 : pp::Instance(instance),
196 is_encoding_(false),
197 is_encode_ticking_(false),
198 callback_factory_(this),
199 #if defined(USE_VP8_INSTEAD_OF_H264)
200 video_profile_(PP_VIDEOPROFILE_VP8_ANY),
201 #else
202 video_profile_(PP_VIDEOPROFILE_H264MAIN),
203 #endif
204 frame_format_(PP_VIDEOFRAME_FORMAT_I420),
205 encoded_frames_(0),
206 last_encode_tick_(0) {
207 InitializeVideoProfiles();
208 ProbeEncoder();
211 VideoEncoderInstance::~VideoEncoderInstance() {
214 void VideoEncoderInstance::AddVideoProfile(PP_VideoProfile profile,
215 const std::string& profile_str) {
216 profile_to_string_.insert(std::make_pair(profile, profile_str));
217 profile_from_string_.insert(std::make_pair(profile_str, profile));
220 void VideoEncoderInstance::InitializeVideoProfiles() {
221 AddVideoProfile(PP_VIDEOPROFILE_H264BASELINE, "h264baseline");
222 AddVideoProfile(PP_VIDEOPROFILE_H264MAIN, "h264main");
223 AddVideoProfile(PP_VIDEOPROFILE_H264EXTENDED, "h264extended");
224 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH, "h264high");
225 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH10PROFILE, "h264high10");
226 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH422PROFILE, "h264high422");
227 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE,
228 "h264high444predictive");
229 AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEBASELINE, "h264scalablebaseline");
230 AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEHIGH, "h264scalablehigh");
231 AddVideoProfile(PP_VIDEOPROFILE_H264STEREOHIGH, "h264stereohigh");
232 AddVideoProfile(PP_VIDEOPROFILE_H264MULTIVIEWHIGH, "h264multiviewhigh");
233 AddVideoProfile(PP_VIDEOPROFILE_VP8_ANY, "vp8");
234 AddVideoProfile(PP_VIDEOPROFILE_VP9_ANY, "vp9");
237 PP_VideoProfile VideoEncoderInstance::VideoProfileFromString(
238 const std::string& str) {
239 VideoProfileFromStringMap::iterator it = profile_from_string_.find(str);
240 if (it == profile_from_string_.end())
241 return PP_VIDEOPROFILE_VP8_ANY;
242 return it->second;
245 std::string VideoEncoderInstance::VideoProfileToString(
246 PP_VideoProfile profile) {
247 VideoProfileToStringMap::iterator it = profile_to_string_.find(profile);
248 if (it == profile_to_string_.end())
249 return "unknown";
250 return it->second;
253 void VideoEncoderInstance::ConfigureTrack() {
254 if (encoder_size_.IsEmpty())
255 frame_size_ = requested_size_;
256 else
257 frame_size_ = encoder_size_;
259 int32_t attrib_list[] = {PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT,
260 frame_format_,
261 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH,
262 frame_size_.width(),
263 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT,
264 frame_size_.height(),
265 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE};
267 video_track_.Configure(
268 attrib_list,
269 callback_factory_.NewCallback(&VideoEncoderInstance::OnConfiguredTrack));
272 void VideoEncoderInstance::OnConfiguredTrack(int32_t result) {
273 if (result != PP_OK) {
274 LogError(result, "Cannot configure track");
275 return;
278 if (is_encoding_) {
279 StartTrackFrames();
280 ScheduleNextEncode();
281 } else
282 StartEncoder();
285 void VideoEncoderInstance::ProbeEncoder() {
286 video_encoder_ = pp::VideoEncoder(this);
287 video_encoder_.GetSupportedProfiles(callback_factory_.NewCallbackWithOutput(
288 &VideoEncoderInstance::OnEncoderProbed));
291 void VideoEncoderInstance::OnEncoderProbed(
292 int32_t result,
293 const std::vector<PP_VideoProfileDescription> profiles) {
294 pp::VarDictionary dict;
295 dict.Set(pp::Var("name"), pp::Var("supportedProfiles"));
296 pp::VarArray js_profiles;
297 dict.Set(pp::Var("profiles"), js_profiles);
299 if (result < 0) {
300 LogError(result, "Cannot get supported profiles");
301 PostMessage(dict);
304 int32_t idx = 0;
305 for (uint32_t i = 0; i < profiles.size(); i++) {
306 const PP_VideoProfileDescription& profile = profiles[i];
307 js_profiles.Set(idx++, pp::Var(VideoProfileToString(profile.profile)));
309 PostMessage(dict);
312 void VideoEncoderInstance::StartEncoder() {
313 video_encoder_ = pp::VideoEncoder(this);
314 frames_timestamps_.clear();
316 int32_t error = video_encoder_.Initialize(
317 frame_format_, frame_size_, video_profile_, 2000000,
318 PP_HARDWAREACCELERATION_WITHFALLBACK,
319 callback_factory_.NewCallback(
320 &VideoEncoderInstance::OnInitializedEncoder));
321 if (error != PP_OK_COMPLETIONPENDING) {
322 LogError(error, "Cannot initialize encoder");
323 return;
327 void VideoEncoderInstance::OnInitializedEncoder(int32_t result) {
328 if (result != PP_OK) {
329 LogError(result, "Encoder initialization failed");
330 return;
333 is_encoding_ = true;
334 Log("started");
336 if (video_encoder_.GetFrameCodedSize(&encoder_size_) != PP_OK) {
337 LogError(result, "Cannot get encoder coded frame size");
338 return;
341 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
342 &VideoEncoderInstance::OnGetBitstreamBuffer));
344 if (encoder_size_ != frame_size_)
345 ConfigureTrack();
346 else {
347 StartTrackFrames();
348 ScheduleNextEncode();
352 void VideoEncoderInstance::ScheduleNextEncode() {
353 // Avoid scheduling more than once at a time.
354 if (is_encode_ticking_)
355 return;
357 PP_Time now = pp::Module::Get()->core()->GetTime();
358 PP_Time tick = 1.0 / 30;
359 // If the callback was triggered late, we need to account for that
360 // delay for the next tick.
361 PP_Time delta = tick - clamp(0, tick, now - last_encode_tick_ - tick);
363 pp::Module::Get()->core()->CallOnMainThread(
364 delta * 1000,
365 callback_factory_.NewCallback(&VideoEncoderInstance::GetEncoderFrameTick),
368 last_encode_tick_ = now;
369 is_encode_ticking_ = true;
372 void VideoEncoderInstance::GetEncoderFrameTick(int32_t result) {
373 is_encode_ticking_ = false;
375 if (is_encoding_) {
376 if (!current_track_frame_.is_null()) {
377 pp::VideoFrame frame = current_track_frame_;
378 current_track_frame_.detach();
379 GetEncoderFrame(frame);
381 ScheduleNextEncode();
385 void VideoEncoderInstance::GetEncoderFrame(const pp::VideoFrame& track_frame) {
386 video_encoder_.GetVideoFrame(callback_factory_.NewCallbackWithOutput(
387 &VideoEncoderInstance::OnEncoderFrame, track_frame));
390 void VideoEncoderInstance::OnEncoderFrame(int32_t result,
391 pp::VideoFrame encoder_frame,
392 pp::VideoFrame track_frame) {
393 if (result == PP_ERROR_ABORTED) {
394 video_track_.RecycleFrame(track_frame);
395 return;
397 if (result != PP_OK) {
398 video_track_.RecycleFrame(track_frame);
399 LogError(result, "Cannot get video frame from video encoder");
400 return;
403 track_frame.GetSize(&frame_size_);
405 if (frame_size_ != encoder_size_) {
406 video_track_.RecycleFrame(track_frame);
407 LogError(PP_ERROR_FAILED, "MediaStreamVideoTrack frame size incorrect");
408 return;
411 if (CopyVideoFrame(encoder_frame, track_frame) == PP_OK)
412 EncodeFrame(encoder_frame);
413 video_track_.RecycleFrame(track_frame);
416 int32_t VideoEncoderInstance::CopyVideoFrame(pp::VideoFrame dest,
417 pp::VideoFrame src) {
418 if (dest.GetDataBufferSize() < src.GetDataBufferSize()) {
419 std::ostringstream oss;
420 oss << "Incorrect destination video frame buffer size : "
421 << dest.GetDataBufferSize() << " < " << src.GetDataBufferSize();
422 LogError(PP_ERROR_FAILED, oss.str());
423 return PP_ERROR_FAILED;
426 dest.SetTimestamp(src.GetTimestamp());
427 memcpy(dest.GetDataBuffer(), src.GetDataBuffer(), src.GetDataBufferSize());
428 return PP_OK;
431 void VideoEncoderInstance::EncodeFrame(const pp::VideoFrame& frame) {
432 frames_timestamps_.push_back(
433 static_cast<uint64_t>(frame.GetTimestamp() * 1000));
434 video_encoder_.Encode(
435 frame, PP_FALSE,
436 callback_factory_.NewCallback(&VideoEncoderInstance::OnEncodeDone));
439 void VideoEncoderInstance::OnEncodeDone(int32_t result) {
440 if (result == PP_ERROR_ABORTED)
441 return;
442 if (result != PP_OK)
443 LogError(result, "Encode failed");
446 void VideoEncoderInstance::OnGetBitstreamBuffer(int32_t result,
447 PP_BitstreamBuffer buffer) {
448 if (result == PP_ERROR_ABORTED)
449 return;
450 if (result != PP_OK) {
451 LogError(result, "Cannot get bitstream buffer");
452 return;
455 encoded_frames_++;
456 PostDataMessage(buffer.buffer, buffer.size);
457 video_encoder_.RecycleBitstreamBuffer(buffer);
459 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
460 &VideoEncoderInstance::OnGetBitstreamBuffer));
463 void VideoEncoderInstance::StartTrackFrames() {
464 is_receiving_track_frames_ = true;
465 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput(
466 &VideoEncoderInstance::OnTrackFrame));
469 void VideoEncoderInstance::StopTrackFrames() {
470 is_receiving_track_frames_ = false;
471 if (!current_track_frame_.is_null()) {
472 video_track_.RecycleFrame(current_track_frame_);
473 current_track_frame_.detach();
477 void VideoEncoderInstance::OnTrackFrame(int32_t result, pp::VideoFrame frame) {
478 if (result == PP_ERROR_ABORTED)
479 return;
481 if (!current_track_frame_.is_null()) {
482 video_track_.RecycleFrame(current_track_frame_);
483 current_track_frame_.detach();
486 if (result != PP_OK) {
487 LogError(result, "Cannot get video frame from video track");
488 return;
491 current_track_frame_ = frame;
492 if (is_receiving_track_frames_)
493 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput(
494 &VideoEncoderInstance::OnTrackFrame));
497 void VideoEncoderInstance::StopEncode() {
498 video_encoder_.Close();
499 StopTrackFrames();
500 video_track_.Close();
501 is_encoding_ = false;
502 encoded_frames_ = 0;
507 void VideoEncoderInstance::HandleMessage(const pp::Var& var_message) {
508 if (!var_message.is_dictionary()) {
509 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid message!"));
510 return;
513 pp::VarDictionary dict_message(var_message);
514 std::string command = dict_message.Get("command").AsString();
516 if (command == "start") {
517 requested_size_ = pp::Size(dict_message.Get("width").AsInt(),
518 dict_message.Get("height").AsInt());
519 pp::Var var_track = dict_message.Get("track");
520 if (!var_track.is_resource()) {
521 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Given track is not a resource"));
522 return;
524 pp::Resource resource_track = var_track.AsResource();
525 video_track_ = pp::MediaStreamVideoTrack(resource_track);
526 video_encoder_ = pp::VideoEncoder();
527 video_profile_ = VideoProfileFromString(
528 dict_message.Get("profile").AsString());
529 ConfigureTrack();
530 } else if (command == "stop") {
531 StopEncode();
532 Log("stopped");
533 } else {
534 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid command!"));
538 void VideoEncoderInstance::PostDataMessage(const void* buffer, uint32_t size) {
539 pp::VarDictionary dictionary;
541 dictionary.Set(pp::Var("name"), pp::Var("data"));
543 pp::VarArrayBuffer array_buffer;
544 uint8_t* data_ptr;
545 uint32_t data_offset = 0;
546 if (video_profile_ == PP_VIDEOPROFILE_VP8_ANY ||
547 video_profile_ == PP_VIDEOPROFILE_VP9_ANY) {
548 uint32_t frame_offset = 0;
549 if (encoded_frames_ == 1) {
550 array_buffer = pp::VarArrayBuffer(
551 size + ivf_writer_.GetFileHeaderSize() +
552 ivf_writer_.GetFrameHeaderSize());
553 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
554 frame_offset = ivf_writer_.WriteFileHeader(
555 data_ptr, frame_size_.width(), frame_size_.height());
556 } else {
557 array_buffer = pp::VarArrayBuffer(
558 size + ivf_writer_.GetFrameHeaderSize());
559 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
561 uint64_t timestamp = frames_timestamps_.front();
562 frames_timestamps_.pop_front();
563 data_offset =
564 frame_offset +
565 ivf_writer_.WriteFrameHeader(data_ptr + frame_offset, timestamp, size);
566 } else {
567 array_buffer = pp::VarArrayBuffer(size);
568 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
571 memcpy(data_ptr + data_offset, buffer, size);
572 array_buffer.Unmap();
573 dictionary.Set(pp::Var("data"), array_buffer);
575 PostMessage(dictionary);
578 void VideoEncoderInstance::LogError(int32_t error, const std::string& message) {
579 std::string msg("Error: ");
580 msg.append(pp::Var(error).DebugString());
581 msg.append(" : ");
582 msg.append(message);
584 Log(msg);
587 void VideoEncoderInstance::Log(const std::string& message) {
588 pp::VarDictionary dictionary;
589 dictionary.Set(pp::Var("name"), pp::Var("log"));
590 dictionary.Set(pp::Var("message"), pp::Var(message));
592 PostMessage(dictionary);
595 pp::Instance* VideoEncoderModule::CreateInstance(PP_Instance instance) {
596 return new VideoEncoderInstance(instance, this);
599 } // anonymous namespace
601 namespace pp {
602 // Factory function for your specialization of the Module object.
603 Module* CreateModule() {
604 return new VideoEncoderModule();
606 } // namespace pp