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.
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
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.
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))
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.
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
);
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
,
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
93 uint32_t IVFWriter::WriteFrameHeader(uint8_t* mem
,
96 PutLE32(mem
, (int)frame_size
);
97 PutLE32(mem
+ 4, (int)(pts
& 0xFFFFFFFF));
98 PutLE32(mem
+ 8, (int)(pts
>> 32));
103 // This object is the global object representing this plugin library as long
105 class VideoEncoderModule
: public pp::Module
{
107 VideoEncoderModule() : pp::Module() {}
108 virtual ~VideoEncoderModule() {}
110 virtual pp::Instance
* CreateInstance(PP_Instance instance
);
113 class VideoEncoderInstance
: public pp::Instance
{
115 VideoEncoderInstance(PP_Instance instance
, pp::Module
* module
);
116 virtual ~VideoEncoderInstance();
118 // pp::Instance implementation.
119 virtual void HandleMessage(const pp::Var
& var_message
);
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
);
130 void OnEncoderProbed(int32_t result
,
131 const std::vector
<PP_VideoProfileDescription
> profiles
);
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
);
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_
;
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
,
186 : pp::Instance(instance
),
188 callback_factory_(this),
189 #if defined(USE_VP8_INSTEAD_OF_H264)
190 video_profile_(PP_VIDEOPROFILE_VP8_ANY
),
192 video_profile_(PP_VIDEOPROFILE_H264MAIN
),
194 frame_format_(PP_VIDEOFRAME_FORMAT_I420
),
196 last_encode_tick_(0) {
197 InitializeVideoProfiles();
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
;
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())
243 void VideoEncoderInstance::ConfigureTrack() {
244 if (encoder_size_
.IsEmpty())
245 frame_size_
= requested_size_
;
247 frame_size_
= encoder_size_
;
249 int32_t attrib_list
[] = {PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT
,
251 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_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()));
263 video_track_
.Configure(
265 callback_factory_
.NewCallback(&VideoEncoderInstance::OnConfiguredTrack
));
268 void VideoEncoderInstance::OnConfiguredTrack(int32_t result
) {
269 if (result
!= PP_OK
) {
270 LogError(result
, "Cannot configure track");
276 ScheduleNextEncode();
281 void VideoEncoderInstance::ProbeEncoder() {
282 video_encoder_
= pp::VideoEncoder(this);
283 video_encoder_
.GetSupportedProfiles(callback_factory_
.NewCallbackWithOutput(
284 &VideoEncoderInstance::OnEncoderProbed
));
287 void VideoEncoderInstance::OnEncoderProbed(
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
);
296 LogError(result
, "Cannot get supported profiles");
301 for (const PP_VideoProfileDescription
& profile
: profiles
)
302 js_profiles
.Set(idx
++, pp::Var(VideoProfileToString(profile
.profile
)));
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");
320 void VideoEncoderInstance::OnInitializedEncoder(int32_t result
) {
321 if (result
!= PP_OK
) {
322 LogError(result
, "Encoder initialization failed");
327 PostSignalMessage("started");
329 if (video_encoder_
.GetFrameCodedSize(&encoder_size_
) != PP_OK
) {
330 LogError(result
, "Cannot get encoder coded frame size");
334 video_encoder_
.GetBitstreamBuffer(callback_factory_
.NewCallbackWithOutput(
335 &VideoEncoderInstance::OnGetBitstreamBuffer
));
337 if (encoder_size_
!= frame_size_
)
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
) {
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
);
377 if (result
!= PP_OK
) {
378 video_track_
.RecycleFrame(track_frame
);
379 LogError(result
, "Cannot get video frame from video encoder");
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");
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());
411 void VideoEncoderInstance::EncodeFrame(const pp::VideoFrame
& frame
) {
412 video_encoder_
.Encode(
414 callback_factory_
.NewCallback(&VideoEncoderInstance::OnEncodeDone
));
417 void VideoEncoderInstance::OnEncodeDone(int32_t result
) {
418 if (result
== PP_ERROR_ABORTED
)
421 LogError(result
, "Encode failed");
424 void VideoEncoderInstance::OnGetBitstreamBuffer(int32_t result
,
425 PP_BitstreamBuffer buffer
) {
426 if (result
== PP_ERROR_ABORTED
)
428 if (result
!= PP_OK
) {
429 LogError(result
, "Cannot get bitstream buffer");
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
)
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");
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();
478 video_track_
.Close();
479 is_encoding_
= false;
485 void VideoEncoderInstance::HandleMessage(const pp::Var
& var_message
) {
486 if (!var_message
.is_dictionary()) {
487 LogToConsole(PP_LOGLEVEL_ERROR
, pp::Var("Invalid message!"));
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"));
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());
508 } else if (command
== "stop") {
510 PostSignalMessage("stopped");
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
;
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());
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
,
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());
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
581 // Factory function for your specialization of the Module object.
582 Module
* CreateModule() {
583 return new VideoEncoderModule();