Reland "Non-SFI mode: Switch to newlib. (patchset #4 id:60001 of https://codereview...
[chromium-blink-merge.git] / ppapi / examples / video_encode / video_encode.cc
blob7a55df294cfa2e89ce2ea21f64d974761d554871
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 <iostream>
11 #include <map>
12 #include <sstream>
13 #include <vector>
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/ppb_console.h"
17 #include "ppapi/cpp/input_event.h"
18 #include "ppapi/cpp/instance.h"
19 #include "ppapi/cpp/media_stream_video_track.h"
20 #include "ppapi/cpp/module.h"
21 #include "ppapi/cpp/rect.h"
22 #include "ppapi/cpp/var.h"
23 #include "ppapi/cpp/var_array_buffer.h"
24 #include "ppapi/cpp/var_dictionary.h"
25 #include "ppapi/cpp/video_encoder.h"
26 #include "ppapi/cpp/video_frame.h"
27 #include "ppapi/utility/completion_callback_factory.h"
29 // When compiling natively on Windows, PostMessage can be #define-d to
30 // something else.
31 #ifdef PostMessage
32 #undef PostMessage
33 #endif
35 // Use assert as a poor-man's CHECK, even in non-debug mode.
36 // Since <assert.h> redefines assert on every inclusion (it doesn't use
37 // include-guards), make sure this is the last file #include'd in this file.
38 #undef NDEBUG
39 #include <assert.h>
41 #define fourcc(a, b, c, d) \
42 (((uint32_t)(a) << 0) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | \
43 ((uint32_t)(d) << 24))
45 namespace {
47 // IVF container writer. It is possible to parse H264 bitstream using
48 // NAL units but for VP8 we need a container to at least find encoded
49 // pictures as well as the picture sizes.
50 class IVFWriter {
51 public:
52 IVFWriter() {}
53 ~IVFWriter() {}
55 uint32_t GetFileHeaderSize() const { return 32; }
56 uint32_t GetFrameHeaderSize() const { return 12; }
57 uint32_t WriteFileHeader(uint8_t* mem, int32_t width, int32_t height);
58 uint32_t WriteFrameHeader(uint8_t* mem, uint64_t pts, size_t frame_size);
60 private:
61 void PutLE16(uint8_t* mem, int val) const {
62 mem[0] = (val >> 0) & 0xff;
63 mem[1] = (val >> 8) & 0xff;
65 void PutLE32(uint8_t* mem, int val) const {
66 mem[0] = (val >> 0) & 0xff;
67 mem[1] = (val >> 8) & 0xff;
68 mem[2] = (val >> 16) & 0xff;
69 mem[3] = (val >> 24) & 0xff;
73 uint32_t IVFWriter::WriteFileHeader(uint8_t* mem,
74 int32_t width,
75 int32_t height) {
76 mem[0] = 'D';
77 mem[1] = 'K';
78 mem[2] = 'I';
79 mem[3] = 'F';
80 PutLE16(mem + 4, 0); // version
81 PutLE16(mem + 6, 32); // header size
82 PutLE32(mem + 8, fourcc('V', 'P', '8', '0')); // fourcc
83 PutLE16(mem + 12, static_cast<uint16_t>(width)); // width
84 PutLE16(mem + 14, static_cast<uint16_t>(height)); // height
85 PutLE32(mem + 16, 30); // rate
86 PutLE32(mem + 20, 1); // scale
87 PutLE32(mem + 24, 0xffffffff); // length
88 PutLE32(mem + 28, 0); // unused
90 return 32;
93 uint32_t IVFWriter::WriteFrameHeader(uint8_t* mem,
94 uint64_t pts,
95 size_t frame_size) {
96 PutLE32(mem, (int)frame_size);
97 PutLE32(mem + 4, (int)(pts & 0xFFFFFFFF));
98 PutLE32(mem + 8, (int)(pts >> 32));
100 return 12;
103 // This object is the global object representing this plugin library as long
104 // as it is loaded.
105 class VideoEncoderModule : public pp::Module {
106 public:
107 VideoEncoderModule() : pp::Module() {}
108 virtual ~VideoEncoderModule() {}
110 virtual pp::Instance* CreateInstance(PP_Instance instance);
113 class VideoEncoderInstance : public pp::Instance {
114 public:
115 VideoEncoderInstance(PP_Instance instance, pp::Module* module);
116 virtual ~VideoEncoderInstance();
118 // pp::Instance implementation.
119 virtual void HandleMessage(const pp::Var& var_message);
121 private:
122 void AddVideoProfile(PP_VideoProfile profile, const std::string& profile_str);
123 void InitializeVideoProfiles();
124 PP_VideoProfile VideoProfileFromString(const std::string& str);
125 std::string VideoProfileToString(PP_VideoProfile profile);
127 void ConfigureTrack();
128 void OnConfiguredTrack(int32_t result);
129 void ProbeEncoder();
130 void OnEncoderProbed(int32_t result,
131 const std::vector<PP_VideoProfileDescription> profiles);
132 void StartEncoder();
133 void OnInitializedEncoder(int32_t result);
134 void ScheduleNextEncode();
135 void GetEncoderFrameTick(int32_t result);
136 void GetEncoderFrame(const pp::VideoFrame& track_frame);
137 void OnEncoderFrame(int32_t result,
138 pp::VideoFrame encoder_frame,
139 pp::VideoFrame track_frame);
140 int32_t CopyVideoFrame(pp::VideoFrame dest, pp::VideoFrame src);
141 void EncodeFrame(const pp::VideoFrame& frame);
142 void OnEncodeDone(int32_t result);
143 void OnGetBitstreamBuffer(int32_t result, PP_BitstreamBuffer buffer);
144 void StartTrackFrames();
145 void StopTrackFrames();
146 void OnTrackFrame(int32_t result, pp::VideoFrame frame);
148 void StopEncode();
150 void LogError(int32_t error, const std::string& message);
151 void Log(const std::string& message);
153 void PostDataMessage(const void* buffer, uint32_t size);
154 void PostSignalMessage(const char* name);
156 typedef std::map<std::string, PP_VideoProfile> VideoProfileFromStringMap;
157 VideoProfileFromStringMap profile_from_string_;
159 typedef std::map<PP_VideoProfile, std::string> VideoProfileToStringMap;
160 VideoProfileToStringMap profile_to_string_;
162 bool is_encoding_;
163 bool is_receiving_track_frames_;
165 pp::VideoEncoder video_encoder_;
166 pp::MediaStreamVideoTrack video_track_;
167 pp::CompletionCallbackFactory<VideoEncoderInstance> callback_factory_;
169 PP_VideoProfile video_profile_;
170 PP_VideoFrame_Format frame_format_;
172 pp::Size requested_size_;
173 pp::Size frame_size_;
174 pp::Size encoder_size_;
175 uint32_t encoded_frames_;
177 pp::VideoFrame current_track_frame_;
179 IVFWriter ivf_writer_;
181 PP_Time last_encode_tick_;
184 VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance,
185 pp::Module* module)
186 : pp::Instance(instance),
187 is_encoding_(false),
188 callback_factory_(this),
189 #if defined(USE_VP8_INSTEAD_OF_H264)
190 video_profile_(PP_VIDEOPROFILE_VP8_ANY),
191 #else
192 video_profile_(PP_VIDEOPROFILE_H264MAIN),
193 #endif
194 frame_format_(PP_VIDEOFRAME_FORMAT_I420),
195 encoded_frames_(0),
196 last_encode_tick_(0) {
197 InitializeVideoProfiles();
198 ProbeEncoder();
201 VideoEncoderInstance::~VideoEncoderInstance() {
204 void VideoEncoderInstance::AddVideoProfile(PP_VideoProfile profile,
205 const std::string& profile_str) {
206 profile_to_string_.insert(std::make_pair(profile, profile_str));
207 profile_from_string_.insert(std::make_pair(profile_str, profile));
210 void VideoEncoderInstance::InitializeVideoProfiles() {
211 AddVideoProfile(PP_VIDEOPROFILE_H264BASELINE, "h264baseline");
212 AddVideoProfile(PP_VIDEOPROFILE_H264MAIN, "h264main");
213 AddVideoProfile(PP_VIDEOPROFILE_H264EXTENDED, "h264extended");
214 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH, "h264high");
215 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH10PROFILE, "h264high10");
216 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH422PROFILE, "h264high422");
217 AddVideoProfile(PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE,
218 "h264high444predictive");
219 AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEBASELINE, "h264scalablebaseline");
220 AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEHIGH, "h264scalablehigh");
221 AddVideoProfile(PP_VIDEOPROFILE_H264STEREOHIGH, "h264stereohigh");
222 AddVideoProfile(PP_VIDEOPROFILE_H264MULTIVIEWHIGH, "h264multiviewhigh");
223 AddVideoProfile(PP_VIDEOPROFILE_VP8_ANY, "vp8");
224 AddVideoProfile(PP_VIDEOPROFILE_VP9_ANY, "vp9");
227 PP_VideoProfile VideoEncoderInstance::VideoProfileFromString(
228 const std::string& str) {
229 VideoProfileFromStringMap::iterator it = profile_from_string_.find(str);
230 if (it == profile_from_string_.end())
231 return PP_VIDEOPROFILE_VP8_ANY;
232 return it->second;
235 std::string VideoEncoderInstance::VideoProfileToString(
236 PP_VideoProfile profile) {
237 VideoProfileToStringMap::iterator it = profile_to_string_.find(profile);
238 if (it == profile_to_string_.end())
239 return "unknown";
240 return it->second;
243 void VideoEncoderInstance::ConfigureTrack() {
244 if (encoder_size_.IsEmpty())
245 frame_size_ = requested_size_;
246 else
247 frame_size_ = encoder_size_;
249 int32_t attrib_list[] = {PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT,
250 frame_format_,
251 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH,
252 frame_size_.width(),
253 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT,
254 frame_size_.height(),
255 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE};
257 pp::VarDictionary dict;
258 dict.Set(pp::Var("status"), pp::Var("configuring video track"));
259 dict.Set(pp::Var("width"), pp::Var(frame_size_.width()));
260 dict.Set(pp::Var("height"), pp::Var(frame_size_.height()));
261 PostMessage(dict);
263 video_track_.Configure(
264 attrib_list,
265 callback_factory_.NewCallback(&VideoEncoderInstance::OnConfiguredTrack));
268 void VideoEncoderInstance::OnConfiguredTrack(int32_t result) {
269 if (result != PP_OK) {
270 LogError(result, "Cannot configure track");
271 return;
274 if (is_encoding_) {
275 StartTrackFrames();
276 ScheduleNextEncode();
277 } else
278 StartEncoder();
281 void VideoEncoderInstance::ProbeEncoder() {
282 video_encoder_ = pp::VideoEncoder(this);
283 video_encoder_.GetSupportedProfiles(callback_factory_.NewCallbackWithOutput(
284 &VideoEncoderInstance::OnEncoderProbed));
287 void VideoEncoderInstance::OnEncoderProbed(
288 int32_t result,
289 const std::vector<PP_VideoProfileDescription> profiles) {
290 pp::VarDictionary dict;
291 dict.Set(pp::Var("name"), pp::Var("supportedProfiles"));
292 pp::VarArray js_profiles;
293 dict.Set(pp::Var("profiles"), js_profiles);
295 if (result < 0) {
296 LogError(result, "Cannot get supported profiles");
297 PostMessage(dict);
300 int32_t idx = 0;
301 for (const PP_VideoProfileDescription& profile : profiles)
302 js_profiles.Set(idx++, pp::Var(VideoProfileToString(profile.profile)));
303 PostMessage(dict);
306 void VideoEncoderInstance::StartEncoder() {
307 video_encoder_ = pp::VideoEncoder(this);
309 int32_t error = video_encoder_.Initialize(
310 frame_format_, frame_size_, video_profile_, 2000000,
311 PP_HARDWAREACCELERATION_WITHFALLBACK,
312 callback_factory_.NewCallback(
313 &VideoEncoderInstance::OnInitializedEncoder));
314 if (error != PP_OK_COMPLETIONPENDING) {
315 LogError(error, "Cannot initialize encoder");
316 return;
320 void VideoEncoderInstance::OnInitializedEncoder(int32_t result) {
321 if (result != PP_OK) {
322 LogError(result, "Encoder initialization failed");
323 return;
326 is_encoding_ = true;
327 PostSignalMessage("started");
329 if (video_encoder_.GetFrameCodedSize(&encoder_size_) != PP_OK) {
330 LogError(result, "Cannot get encoder coded frame size");
331 return;
334 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
335 &VideoEncoderInstance::OnGetBitstreamBuffer));
337 if (encoder_size_ != frame_size_)
338 ConfigureTrack();
339 else {
340 StartTrackFrames();
341 ScheduleNextEncode();
345 void VideoEncoderInstance::ScheduleNextEncode() {
346 PP_Time now = pp::Module::Get()->core()->GetTime();
347 pp::Module::Get()->core()->CallOnMainThread(
348 std::min(std::max(now - last_encode_tick_, 0.0), 1000.0 / 30),
349 callback_factory_.NewCallback(&VideoEncoderInstance::GetEncoderFrameTick),
351 last_encode_tick_ = now;
354 void VideoEncoderInstance::GetEncoderFrameTick(int32_t result) {
355 if (is_encoding_) {
356 if (!current_track_frame_.is_null()) {
357 pp::VideoFrame frame = current_track_frame_;
358 current_track_frame_.detach();
359 GetEncoderFrame(frame);
361 ScheduleNextEncode();
365 void VideoEncoderInstance::GetEncoderFrame(const pp::VideoFrame& track_frame) {
366 video_encoder_.GetVideoFrame(callback_factory_.NewCallbackWithOutput(
367 &VideoEncoderInstance::OnEncoderFrame, track_frame));
370 void VideoEncoderInstance::OnEncoderFrame(int32_t result,
371 pp::VideoFrame encoder_frame,
372 pp::VideoFrame track_frame) {
373 if (result == PP_ERROR_ABORTED) {
374 video_track_.RecycleFrame(track_frame);
375 return;
377 if (result != PP_OK) {
378 video_track_.RecycleFrame(track_frame);
379 LogError(result, "Cannot get video frame from video encoder");
380 return;
383 track_frame.GetSize(&frame_size_);
385 if (frame_size_ != encoder_size_) {
386 video_track_.RecycleFrame(track_frame);
387 LogError(PP_ERROR_FAILED, "MediaStreamVideoTrack frame size incorrect");
388 return;
391 if (CopyVideoFrame(encoder_frame, track_frame) == PP_OK)
392 EncodeFrame(encoder_frame);
393 video_track_.RecycleFrame(track_frame);
396 int32_t VideoEncoderInstance::CopyVideoFrame(pp::VideoFrame dest,
397 pp::VideoFrame src) {
398 if (dest.GetDataBufferSize() < src.GetDataBufferSize()) {
399 std::ostringstream oss;
400 oss << "Incorrect destination video frame buffer size : "
401 << dest.GetDataBufferSize() << " < " << src.GetDataBufferSize();
402 LogError(PP_ERROR_FAILED, oss.str());
403 return PP_ERROR_FAILED;
406 dest.SetTimestamp(src.GetTimestamp());
407 memcpy(dest.GetDataBuffer(), src.GetDataBuffer(), src.GetDataBufferSize());
408 return PP_OK;
411 void VideoEncoderInstance::EncodeFrame(const pp::VideoFrame& frame) {
412 video_encoder_.Encode(
413 frame, PP_FALSE,
414 callback_factory_.NewCallback(&VideoEncoderInstance::OnEncodeDone));
417 void VideoEncoderInstance::OnEncodeDone(int32_t result) {
418 if (result == PP_ERROR_ABORTED)
419 return;
420 if (result != PP_OK)
421 LogError(result, "Encode failed");
424 void VideoEncoderInstance::OnGetBitstreamBuffer(int32_t result,
425 PP_BitstreamBuffer buffer) {
426 if (result == PP_ERROR_ABORTED)
427 return;
428 if (result != PP_OK) {
429 LogError(result, "Cannot get bitstream buffer");
430 return;
433 encoded_frames_++;
434 PostDataMessage(buffer.buffer, buffer.size);
435 video_encoder_.RecycleBitstreamBuffer(buffer);
437 video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput(
438 &VideoEncoderInstance::OnGetBitstreamBuffer));
441 void VideoEncoderInstance::StartTrackFrames() {
442 is_receiving_track_frames_ = true;
443 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput(
444 &VideoEncoderInstance::OnTrackFrame));
447 void VideoEncoderInstance::StopTrackFrames() {
448 is_receiving_track_frames_ = false;
449 if (!current_track_frame_.is_null()) {
450 video_track_.RecycleFrame(current_track_frame_);
451 current_track_frame_.detach();
455 void VideoEncoderInstance::OnTrackFrame(int32_t result, pp::VideoFrame frame) {
456 if (result == PP_ERROR_ABORTED)
457 return;
459 if (!current_track_frame_.is_null()) {
460 video_track_.RecycleFrame(current_track_frame_);
461 current_track_frame_.detach();
464 if (result != PP_OK) {
465 LogError(result, "Cannot get video frame from video track");
466 return;
469 current_track_frame_ = frame;
470 if (is_receiving_track_frames_)
471 video_track_.GetFrame(callback_factory_.NewCallbackWithOutput(
472 &VideoEncoderInstance::OnTrackFrame));
475 void VideoEncoderInstance::StopEncode() {
476 video_encoder_.Close();
477 StopTrackFrames();
478 video_track_.Close();
479 is_encoding_ = false;
480 encoded_frames_ = 0;
485 void VideoEncoderInstance::HandleMessage(const pp::Var& var_message) {
486 if (!var_message.is_dictionary()) {
487 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid message!"));
488 return;
491 pp::VarDictionary dict_message(var_message);
492 std::string command = dict_message.Get("command").AsString();
494 if (command == "start") {
495 requested_size_ = pp::Size(dict_message.Get("width").AsInt(),
496 dict_message.Get("height").AsInt());
497 pp::Var var_track = dict_message.Get("track");
498 if (!var_track.is_resource()) {
499 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Given track is not a resource"));
500 return;
502 pp::Resource resource_track = var_track.AsResource();
503 video_track_ = pp::MediaStreamVideoTrack(resource_track);
504 video_encoder_ = pp::VideoEncoder();
505 video_profile_ = VideoProfileFromString(
506 dict_message.Get("profile").AsString());
507 ConfigureTrack();
508 } else if (command == "stop") {
509 StopEncode();
510 PostSignalMessage("stopped");
511 } else {
512 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid command!"));
516 void VideoEncoderInstance::PostDataMessage(const void* buffer, uint32_t size) {
517 pp::VarDictionary dictionary;
519 dictionary.Set(pp::Var("name"), pp::Var("data"));
521 pp::VarArrayBuffer array_buffer;
522 uint8_t* data_ptr;
523 uint32_t data_offset = 0;
524 if (video_profile_ == PP_VIDEOPROFILE_VP8_ANY ||
525 video_profile_ == PP_VIDEOPROFILE_VP9_ANY) {
526 uint32_t frame_offset = 0;
527 if (encoded_frames_ == 1) {
528 array_buffer = pp::VarArrayBuffer(
529 size + ivf_writer_.GetFileHeaderSize() +
530 ivf_writer_.GetFrameHeaderSize());
531 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
532 frame_offset = ivf_writer_.WriteFileHeader(
533 data_ptr, frame_size_.width(), frame_size_.height());
534 } else {
535 array_buffer = pp::VarArrayBuffer(
536 size + ivf_writer_.GetFrameHeaderSize());
537 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
539 data_offset = frame_offset +
540 ivf_writer_.WriteFrameHeader(data_ptr + frame_offset,
541 encoded_frames_,
542 size);
543 } else {
544 array_buffer = pp::VarArrayBuffer(size);
545 data_ptr = static_cast<uint8_t*>(array_buffer.Map());
548 memcpy(data_ptr + data_offset, buffer, size);
549 array_buffer.Unmap();
550 dictionary.Set(pp::Var("data"), array_buffer);
552 PostMessage(dictionary);
555 void VideoEncoderInstance::PostSignalMessage(const char* name) {
556 pp::VarDictionary dictionary;
557 dictionary.Set(pp::Var("name"), pp::Var(name));
559 PostMessage(dictionary);
562 void VideoEncoderInstance::LogError(int32_t error, const std::string& message) {
563 std::string msg("Error: ");
564 msg.append(pp::Var(error).DebugString());
565 msg.append(" : ");
566 msg.append(message);
567 LogToConsole(PP_LOGLEVEL_ERROR, pp::Var(msg));
570 void VideoEncoderInstance::Log(const std::string& message) {
571 LogToConsole(PP_LOGLEVEL_LOG, pp::Var(message));
574 pp::Instance* VideoEncoderModule::CreateInstance(PP_Instance instance) {
575 return new VideoEncoderInstance(instance, this);
578 } // anonymous namespace
580 namespace pp {
581 // Factory function for your specialization of the Module object.
582 Module* CreateModule() {
583 return new VideoEncoderModule();
585 } // namespace pp