Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ppapi / examples / video_encode / video_encode.cc
blob12e52802f6da46a26a3ea99c6bf67926d709c6ca
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 can be #define-d to
31 // something else.
32 #ifdef PostMessage
33 #undef PostMessage
34 #endif
36 // Use assert as a poor-man's CHECK, even in non-debug mode.
37 // Since <assert.h> redefines assert on every inclusion (it doesn't use
38 // include-guards), make sure this is the last file #include'd in this file.
39 #undef NDEBUG
40 #include <assert.h>
42 #define fourcc(a, b, c, d) \
43 (((uint32_t)(a) << 0) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | \
44 ((uint32_t)(d) << 24))
46 namespace {
48 double clamp(double min, double max, double value) {
49 return std::max(std::min(value, max), min);
52 // IVF container writer. It is possible to parse H264 bitstream using
53 // NAL units but for VP8 we need a container to at least find encoded
54 // pictures as well as the picture sizes.
55 class IVFWriter {
56 public:
57 IVFWriter() {}
58 ~IVFWriter() {}
60 uint32_t GetFileHeaderSize() const { return 32; }
61 uint32_t GetFrameHeaderSize() const { return 12; }
62 uint32_t WriteFileHeader(uint8_t* mem, int32_t width, int32_t height);
63 uint32_t WriteFrameHeader(uint8_t* mem, uint64_t pts, size_t frame_size);
65 private:
66 void PutLE16(uint8_t* mem, int val) const {
67 mem[0] = (val >> 0) & 0xff;
68 mem[1] = (val >> 8) & 0xff;
70 void PutLE32(uint8_t* mem, int val) const {
71 mem[0] = (val >> 0) & 0xff;
72 mem[1] = (val >> 8) & 0xff;
73 mem[2] = (val >> 16) & 0xff;
74 mem[3] = (val >> 24) & 0xff;
78 uint32_t IVFWriter::WriteFileHeader(uint8_t* mem,
79 int32_t width,
80 int32_t height) {
81 mem[0] = 'D';
82 mem[1] = 'K';
83 mem[2] = 'I';
84 mem[3] = 'F';
85 PutLE16(mem + 4, 0); // version
86 PutLE16(mem + 6, 32); // header size
87 PutLE32(mem + 8, fourcc('V', 'P', '8', '0')); // fourcc
88 PutLE16(mem + 12, static_cast<uint16_t>(width)); // width
89 PutLE16(mem + 14, static_cast<uint16_t>(height)); // height
90 PutLE32(mem + 16, 1000); // rate
91 PutLE32(mem + 20, 1); // scale
92 PutLE32(mem + 24, 0xffffffff); // length
93 PutLE32(mem + 28, 0); // unused
95 return 32;
98 uint32_t IVFWriter::WriteFrameHeader(uint8_t* mem,
99 uint64_t pts,
100 size_t frame_size) {
101 PutLE32(mem, (int)frame_size);
102 PutLE32(mem + 4, (int)(pts & 0xFFFFFFFF));
103 PutLE32(mem + 8, (int)(pts >> 32));
105 return 12;
108 // This object is the global object representing this plugin library as long
109 // as it is loaded.
110 class VideoEncoderModule : public pp::Module {
111 public:
112 VideoEncoderModule() : pp::Module() {}
113 virtual ~VideoEncoderModule() {}
115 virtual pp::Instance* CreateInstance(PP_Instance instance);
118 class VideoEncoderInstance : public pp::Instance {
119 public:
120 VideoEncoderInstance(PP_Instance instance, pp::Module* module);
121 virtual ~VideoEncoderInstance();
123 // pp::Instance implementation.
124 virtual void HandleMessage(const pp::Var& var_message);
126 private:
127 void AddVideoProfile(PP_VideoProfile profile, const std::string& profile_str);
128 void InitializeVideoProfiles();
129 PP_VideoProfile VideoProfileFromString(const std::string& str);
130 std::string VideoProfileToString(PP_VideoProfile profile);
132 void ConfigureTrack();
133 void OnConfiguredTrack(int32_t result);
134 void ProbeEncoder();
135 void OnEncoderProbed(int32_t result,
136 const std::vector<PP_VideoProfileDescription> profiles);
137 void StartEncoder();
138 void OnInitializedEncoder(int32_t result);
139 void ScheduleNextEncode();
140 void GetEncoderFrameTick(int32_t result);
141 void GetEncoderFrame(const pp::VideoFrame& track_frame);
142 void OnEncoderFrame(int32_t result,
143 pp::VideoFrame encoder_frame,
144 pp::VideoFrame track_frame);
145 int32_t CopyVideoFrame(pp::VideoFrame dest, pp::VideoFrame src);
146 void EncodeFrame(const pp::VideoFrame& frame);
147 void OnEncodeDone(int32_t result);
148 void OnGetBitstreamBuffer(int32_t result, PP_BitstreamBuffer buffer);
149 void StartTrackFrames();
150 void StopTrackFrames();
151 void OnTrackFrame(int32_t result, pp::VideoFrame frame);
153 void StopEncode();
155 void LogError(int32_t error, const std::string& message);
156 void Log(const std::string& message);
158 void PostDataMessage(const void* buffer, uint32_t size);
159 void PostSignalMessage(const char* name);
161 typedef std::map<std::string, PP_VideoProfile> VideoProfileFromStringMap;
162 VideoProfileFromStringMap profile_from_string_;
164 typedef std::map<PP_VideoProfile, std::string> VideoProfileToStringMap;
165 VideoProfileToStringMap profile_to_string_;
167 bool is_encoding_;
168 bool is_encode_ticking_;
169 bool is_receiving_track_frames_;
171 pp::VideoEncoder video_encoder_;
172 pp::MediaStreamVideoTrack video_track_;
173 pp::CompletionCallbackFactory<VideoEncoderInstance> callback_factory_;
175 PP_VideoProfile video_profile_;
176 PP_VideoFrame_Format frame_format_;
178 pp::Size requested_size_;
179 pp::Size frame_size_;
180 pp::Size encoder_size_;
181 uint32_t encoded_frames_;
183 std::deque<uint64_t> frames_timestamps_;
185 pp::VideoFrame current_track_frame_;
187 IVFWriter ivf_writer_;
189 PP_Time last_encode_tick_;
192 VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance,
193 pp::Module* module)
194 : pp::Instance(instance),
195 is_encoding_(false),
196 is_encode_ticking_(false),
197 callback_factory_(this),
198 #if defined(USE_VP8_INSTEAD_OF_H264)
199 video_profile_(PP_VIDEOPROFILE_VP8_ANY),
200 #else
201 video_profile_(PP_VIDEOPROFILE_H264MAIN),
202 #endif
203 frame_format_(PP_VIDEOFRAME_FORMAT_I420),
204 encoded_frames_(0),
205 last_encode_tick_(0) {
206 InitializeVideoProfiles();
207 ProbeEncoder();
210 VideoEncoderInstance::~VideoEncoderInstance() {
213 void VideoEncoderInstance::AddVideoProfile(PP_VideoProfile profile,
214 const std::string& profile_str) {
215 profile_to_string_.insert(std::make_pair(profile, profile_str));
216 profile_from_string_.insert(std::make_pair(profile_str, profile));
219 void VideoEncoderInstance::InitializeVideoProfiles() {
220 AddVideoProfile(PP_VIDEOPROFILE_H264BASELINE, "h264baseline");
221 AddVideoProfile(PP_VIDEOPROFILE_H264MAIN, "h264main");
222 AddVideoProfile(PP_VIDEOPROFILE_H264EXTENDED, "h264extended");
223 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH, "h264high");
224 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH10PROFILE, "h264high10");
225 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH422PROFILE, "h264high422");
226 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE,
227 "h264high444predictive");
228 AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEBASELINE, "h264scalablebaseline");
229 AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEHIGH, "h264scalablehigh");
230 AddVideoProfile(PP_VIDEOPROFILE_H264STEREOHIGH, "h264stereohigh");
231 AddVideoProfile(PP_VIDEOPROFILE_H264MULTIVIEWHIGH, "h264multiviewhigh");
232 AddVideoProfile(PP_VIDEOPROFILE_VP8_ANY, "vp8");
233 AddVideoProfile(PP_VIDEOPROFILE_VP9_ANY, "vp9");
236 PP_VideoProfile VideoEncoderInstance::VideoProfileFromString(
237 const std::string& str) {
238 VideoProfileFromStringMap::iterator it = profile_from_string_.find(str);
239 if (it == profile_from_string_.end())
240 return PP_VIDEOPROFILE_VP8_ANY;
241 return it->second;
244 std::string VideoEncoderInstance::VideoProfileToString(
245 PP_VideoProfile profile) {
246 VideoProfileToStringMap::iterator it = profile_to_string_.find(profile);
247 if (it == profile_to_string_.end())
248 return "unknown";
249 return it->second;
252 void VideoEncoderInstance::ConfigureTrack() {
253 if (encoder_size_.IsEmpty())
254 frame_size_ = requested_size_;
255 else
256 frame_size_ = encoder_size_;
258 int32_t attrib_list[] = {PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT,
259 frame_format_,
260 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH,
261 frame_size_.width(),
262 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT,
263 frame_size_.height(),
264 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE};
266 pp::VarDictionary dict;
267 dict.Set(pp::Var("status"), pp::Var("configuring video track"));
268 dict.Set(pp::Var("width"), pp::Var(frame_size_.width()));
269 dict.Set(pp::Var("height"), pp::Var(frame_size_.height()));
270 PostMessage(dict);
272 video_track_.Configure(
273 attrib_list,
274 callback_factory_.NewCallback(&VideoEncoderInstance::OnConfiguredTrack));
277 void VideoEncoderInstance::OnConfiguredTrack(int32_t result) {
278 if (result != PP_OK) {
279 LogError(result, "Cannot configure track");
280 return;
283 if (is_encoding_) {
284 StartTrackFrames();
285 ScheduleNextEncode();
286 } else
287 StartEncoder();
290 void VideoEncoderInstance::ProbeEncoder() {
291 video_encoder_ = pp::VideoEncoder(this);
292 video_encoder_.GetSupportedProfiles(callback_factory_.NewCallbackWithOutput(
293 &VideoEncoderInstance::OnEncoderProbed));
296 void VideoEncoderInstance::OnEncoderProbed(
297 int32_t result,
298 const std::vector<PP_VideoProfileDescription> profiles) {
299 pp::VarDictionary dict;
300 dict.Set(pp::Var("name"), pp::Var("supportedProfiles"));
301 pp::VarArray js_profiles;
302 dict.Set(pp::Var("profiles"), js_profiles);
304 if (result < 0) {
305 LogError(result, "Cannot get supported profiles");
306 PostMessage(dict);
309 int32_t idx = 0;
310 for (const PP_VideoProfileDescription& profile : profiles)
311 js_profiles.Set(idx++, pp::Var(VideoProfileToString(profile.profile)));
312 PostMessage(dict);
315 void VideoEncoderInstance::StartEncoder() {
316 video_encoder_ = pp::VideoEncoder(this);
317 frames_timestamps_.clear();
319 int32_t error = video_encoder_.Initialize(
320 frame_format_, frame_size_, video_profile_, 2000000,
321 PP_HARDWAREACCELERATION_WITHFALLBACK,
322 callback_factory_.NewCallback(
323 &VideoEncoderInstance::OnInitializedEncoder));
324 if (error != PP_OK_COMPLETIONPENDING) {
325 LogError(error, "Cannot initialize encoder");
326 return;
330 void VideoEncoderInstance::OnInitializedEncoder(int32_t result) {
331 if (result != PP_OK) {
332 LogError(result, "Encoder initialization failed");
333 return;
336 is_encoding_ = true;
337 PostSignalMessage("started");
339 if (video_encoder_.GetFrameCodedSize(&encoder_size_) != PP_OK) {
340 LogError(result, "Cannot get encoder coded frame size");
341 return;
344 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
345 &VideoEncoderInstance::OnGetBitstreamBuffer));
347 if (encoder_size_ != frame_size_)
348 ConfigureTrack();
349 else {
350 StartTrackFrames();
351 ScheduleNextEncode();
355 void VideoEncoderInstance::ScheduleNextEncode() {
356 // Avoid scheduling more than once at a time.
357 if (is_encode_ticking_)
358 return;
360 PP_Time now = pp::Module::Get()->core()->GetTime();
361 PP_Time tick = 1.0 / 30;
362 // If the callback was triggered late, we need to account for that
363 // delay for the next tick.
364 PP_Time delta = tick - clamp(0, tick, now - last_encode_tick_ - tick);
366 pp::Module::Get()->core()->CallOnMainThread(
367 delta * 1000,
368 callback_factory_.NewCallback(&VideoEncoderInstance::GetEncoderFrameTick),
371 last_encode_tick_ = now;
372 is_encode_ticking_ = true;
375 void VideoEncoderInstance::GetEncoderFrameTick(int32_t result) {
376 is_encode_ticking_ = false;
378 if (is_encoding_) {
379 if (!current_track_frame_.is_null()) {
380 pp::VideoFrame frame = current_track_frame_;
381 current_track_frame_.detach();
382 GetEncoderFrame(frame);
384 ScheduleNextEncode();
388 void VideoEncoderInstance::GetEncoderFrame(const pp::VideoFrame& track_frame) {
389 video_encoder_.GetVideoFrame(callback_factory_.NewCallbackWithOutput(
390 &VideoEncoderInstance::OnEncoderFrame, track_frame));
393 void VideoEncoderInstance::OnEncoderFrame(int32_t result,
394 pp::VideoFrame encoder_frame,
395 pp::VideoFrame track_frame) {
396 if (result == PP_ERROR_ABORTED) {
397 video_track_.RecycleFrame(track_frame);
398 return;
400 if (result != PP_OK) {
401 video_track_.RecycleFrame(track_frame);
402 LogError(result, "Cannot get video frame from video encoder");
403 return;
406 track_frame.GetSize(&frame_size_);
408 if (frame_size_ != encoder_size_) {
409 video_track_.RecycleFrame(track_frame);
410 LogError(PP_ERROR_FAILED, "MediaStreamVideoTrack frame size incorrect");
411 return;
414 if (CopyVideoFrame(encoder_frame, track_frame) == PP_OK)
415 EncodeFrame(encoder_frame);
416 video_track_.RecycleFrame(track_frame);
419 int32_t VideoEncoderInstance::CopyVideoFrame(pp::VideoFrame dest,
420 pp::VideoFrame src) {
421 if (dest.GetDataBufferSize() < src.GetDataBufferSize()) {
422 std::ostringstream oss;
423 oss << "Incorrect destination video frame buffer size : "
424 << dest.GetDataBufferSize() << " < " << src.GetDataBufferSize();
425 LogError(PP_ERROR_FAILED, oss.str());
426 return PP_ERROR_FAILED;
429 dest.SetTimestamp(src.GetTimestamp());
430 memcpy(dest.GetDataBuffer(), src.GetDataBuffer(), src.GetDataBufferSize());
431 return PP_OK;
434 void VideoEncoderInstance::EncodeFrame(const pp::VideoFrame& frame) {
435 frames_timestamps_.push_back(
436 static_cast<uint64_t>(frame.GetTimestamp() * 1000));
437 video_encoder_.Encode(
438 frame, PP_FALSE,
439 callback_factory_.NewCallback(&VideoEncoderInstance::OnEncodeDone));
442 void VideoEncoderInstance::OnEncodeDone(int32_t result) {
443 if (result == PP_ERROR_ABORTED)
444 return;
445 if (result != PP_OK)
446 LogError(result, "Encode failed");
449 void VideoEncoderInstance::OnGetBitstreamBuffer(int32_t result,
450 PP_BitstreamBuffer buffer) {
451 if (result == PP_ERROR_ABORTED)
452 return;
453 if (result != PP_OK) {
454 LogError(result, "Cannot get bitstream buffer");
455 return;
458 encoded_frames_++;
459 PostDataMessage(buffer.buffer, buffer.size);
460 video_encoder_.RecycleBitstreamBuffer(buffer);
462 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
463 &VideoEncoderInstance::OnGetBitstreamBuffer));
466 void VideoEncoderInstance::StartTrackFrames() {
467 is_receiving_track_frames_ = true;
468 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput(
469 &VideoEncoderInstance::OnTrackFrame));
472 void VideoEncoderInstance::StopTrackFrames() {
473 is_receiving_track_frames_ = false;
474 if (!current_track_frame_.is_null()) {
475 video_track_.RecycleFrame(current_track_frame_);
476 current_track_frame_.detach();
480 void VideoEncoderInstance::OnTrackFrame(int32_t result, pp::VideoFrame frame) {
481 if (result == PP_ERROR_ABORTED)
482 return;
484 if (!current_track_frame_.is_null()) {
485 video_track_.RecycleFrame(current_track_frame_);
486 current_track_frame_.detach();
489 if (result != PP_OK) {
490 LogError(result, "Cannot get video frame from video track");
491 return;
494 current_track_frame_ = frame;
495 if (is_receiving_track_frames_)
496 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput(
497 &VideoEncoderInstance::OnTrackFrame));
500 void VideoEncoderInstance::StopEncode() {
501 video_encoder_.Close();
502 StopTrackFrames();
503 video_track_.Close();
504 is_encoding_ = false;
505 encoded_frames_ = 0;
510 void VideoEncoderInstance::HandleMessage(const pp::Var& var_message) {
511 if (!var_message.is_dictionary()) {
512 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid message!"));
513 return;
516 pp::VarDictionary dict_message(var_message);
517 std::string command = dict_message.Get("command").AsString();
519 if (command == "start") {
520 requested_size_ = pp::Size(dict_message.Get("width").AsInt(),
521 dict_message.Get("height").AsInt());
522 pp::Var var_track = dict_message.Get("track");
523 if (!var_track.is_resource()) {
524 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Given track is not a resource"));
525 return;
527 pp::Resource resource_track = var_track.AsResource();
528 video_track_ = pp::MediaStreamVideoTrack(resource_track);
529 video_encoder_ = pp::VideoEncoder();
530 video_profile_ = VideoProfileFromString(
531 dict_message.Get("profile").AsString());
532 ConfigureTrack();
533 } else if (command == "stop") {
534 StopEncode();
535 PostSignalMessage("stopped");
536 } else {
537 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid command!"));
541 void VideoEncoderInstance::PostDataMessage(const void* buffer, uint32_t size) {
542 pp::VarDictionary dictionary;
544 dictionary.Set(pp::Var("name"), pp::Var("data"));
546 pp::VarArrayBuffer array_buffer;
547 uint8_t* data_ptr;
548 uint32_t data_offset = 0;
549 if (video_profile_ == PP_VIDEOPROFILE_VP8_ANY ||
550 video_profile_ == PP_VIDEOPROFILE_VP9_ANY) {
551 uint32_t frame_offset = 0;
552 if (encoded_frames_ == 1) {
553 array_buffer = pp::VarArrayBuffer(
554 size + ivf_writer_.GetFileHeaderSize() +
555 ivf_writer_.GetFrameHeaderSize());
556 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
557 frame_offset = ivf_writer_.WriteFileHeader(
558 data_ptr, frame_size_.width(), frame_size_.height());
559 } else {
560 array_buffer = pp::VarArrayBuffer(
561 size + ivf_writer_.GetFrameHeaderSize());
562 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
564 uint64_t timestamp = frames_timestamps_.front();
565 frames_timestamps_.pop_front();
566 data_offset =
567 frame_offset +
568 ivf_writer_.WriteFrameHeader(data_ptr + frame_offset, timestamp, size);
569 } else {
570 array_buffer = pp::VarArrayBuffer(size);
571 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
574 memcpy(data_ptr + data_offset, buffer, size);
575 array_buffer.Unmap();
576 dictionary.Set(pp::Var("data"), array_buffer);
578 PostMessage(dictionary);
581 void VideoEncoderInstance::PostSignalMessage(const char* name) {
582 pp::VarDictionary dictionary;
583 dictionary.Set(pp::Var("name"), pp::Var(name));
585 PostMessage(dictionary);
588 void VideoEncoderInstance::LogError(int32_t error, const std::string& message) {
589 std::string msg("Error: ");
590 msg.append(pp::Var(error).DebugString());
591 msg.append(" : ");
592 msg.append(message);
593 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var(msg));
596 void VideoEncoderInstance::Log(const std::string& message) {
597 LogToConsole(PP_LOGLEVEL_LOG, pp::Var(message));
600 pp::Instance* VideoEncoderModule::CreateInstance(PP_Instance instance) {
601 return new VideoEncoderInstance(instance, this);
604 } // anonymous namespace
606 namespace pp {
607 // Factory function for your specialization of the Module object.
608 Module* CreateModule() {
609 return new VideoEncoderModule();
611 } // namespace pp