Chromium Code Reviews| Index: ppapi/examples/video_encode/video_encode.cc |
| diff --git a/ppapi/examples/video_encode/video_encode.cc b/ppapi/examples/video_encode/video_encode.cc |
| index 26c66dff1fa34d7a8d30bf9619455843ffd17049..34c429b39fbe3561326b886ea2c3c68903f9ee4e 100644 |
| --- a/ppapi/examples/video_encode/video_encode.cc |
| +++ b/ppapi/examples/video_encode/video_encode.cc |
| @@ -7,6 +7,7 @@ |
| #include <string.h> |
| #include <iostream> |
| +#include <map> |
| #include <sstream> |
| #include <vector> |
| @@ -24,9 +25,6 @@ |
| #include "ppapi/cpp/video_frame.h" |
| #include "ppapi/utility/completion_callback_factory.h" |
| -// TODO(llandwerlin): turn on by default when we have software encode. |
| -// #define USE_VP8_INSTEAD_OF_H264 |
| - |
| // When compiling natively on Windows, PostMessage can be #define-d to |
| // something else. |
| #ifdef PostMessage |
| @@ -39,52 +37,63 @@ |
| #undef NDEBUG |
| #include <assert.h> |
| +#define fourcc(a, b, c, d) \ |
| + (((uint32_t)(a) << 0) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | \ |
| + ((uint32_t)(d) << 24)) |
| + |
| namespace { |
| -std::string VideoProfileToString(PP_VideoProfile profile) { |
| - switch (profile) { |
| - case PP_VIDEOPROFILE_H264BASELINE: |
| - return "h264baseline"; |
| - case PP_VIDEOPROFILE_H264MAIN: |
| - return "h264main"; |
| - case PP_VIDEOPROFILE_H264EXTENDED: |
| - return "h264extended"; |
| - case PP_VIDEOPROFILE_H264HIGH: |
| - return "h264high"; |
| - case PP_VIDEOPROFILE_H264HIGH10PROFILE: |
| - return "h264high10"; |
| - case PP_VIDEOPROFILE_H264HIGH422PROFILE: |
| - return "h264high422"; |
| - case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE: |
| - return "h264high444predictive"; |
| - case PP_VIDEOPROFILE_H264SCALABLEBASELINE: |
| - return "h264scalablebaseline"; |
| - case PP_VIDEOPROFILE_H264SCALABLEHIGH: |
| - return "h264scalablehigh"; |
| - case PP_VIDEOPROFILE_H264STEREOHIGH: |
| - return "h264stereohigh"; |
| - case PP_VIDEOPROFILE_H264MULTIVIEWHIGH: |
| - return "h264multiviewhigh"; |
| - case PP_VIDEOPROFILE_VP8_ANY: |
| - return "vp8"; |
| - case PP_VIDEOPROFILE_VP9_ANY: |
| - return "vp9"; |
| - // No default to catch unhandled profiles. |
| +class IVFWriter { |
|
bbudge
2015/03/16 18:27:48
Since this is an example, some general comments ab
llandwerlin-old
2015/03/16 19:08:09
Done.
|
| + public: |
| + IVFWriter() {} |
| + ~IVFWriter() {} |
| + |
| + uint32_t GetFileHeaderSize() const { return 32; } |
| + uint32_t GetFrameHeaderSize() const { return 12; } |
| + uint32_t WriteFileHeader(uint8_t* mem, int32_t width, int32_t height); |
| + uint32_t WriteFrameHeader(uint8_t* mem, uint64_t pts, size_t frame_size); |
| + |
| + private: |
| + void PutLE16(uint8_t* mem, int val) const { |
| + mem[0] = (val >> 0) & 0xff; |
| + mem[1] = (val >> 8) & 0xff; |
| } |
| - return "unknown"; |
| -} |
| - |
| -std::string HardwareAccelerationToString(PP_HardwareAcceleration acceleration) { |
| - switch (acceleration) { |
| - case PP_HARDWAREACCELERATION_ONLY: |
| - return "hardware"; |
| - case PP_HARDWAREACCELERATION_WITHFALLBACK: |
| - return "hardware/software"; |
| - case PP_HARDWAREACCELERATION_NONE: |
| - return "software"; |
| - // No default to catch unhandled accelerations. |
| + void PutLE32(uint8_t* mem, int val) const { |
| + mem[0] = (val >> 0) & 0xff; |
| + mem[1] = (val >> 8) & 0xff; |
| + mem[2] = (val >> 16) & 0xff; |
| + mem[3] = (val >> 24) & 0xff; |
| } |
| - return "unknown"; |
| +}; |
| + |
| +uint32_t IVFWriter::WriteFileHeader(uint8_t* mem, |
| + int32_t width, |
| + int32_t height) { |
| + mem[0] = 'D'; |
| + mem[1] = 'K'; |
| + mem[2] = 'I'; |
| + mem[3] = 'F'; |
| + PutLE16(mem + 4, 0); // version |
| + PutLE16(mem + 6, 32); // header size |
| + PutLE32(mem + 8, fourcc('V', 'P', '8', '0')); // fourcc |
| + PutLE16(mem + 12, static_cast<uint16_t>(width)); // width |
| + PutLE16(mem + 14, static_cast<uint16_t>(height)); // height |
| + PutLE32(mem + 16, 30); // rate |
| + PutLE32(mem + 20, 1); // scale |
| + PutLE32(mem + 24, 0xffffffff); // length |
| + PutLE32(mem + 28, 0); // unused |
| + |
| + return 32; |
| +} |
| + |
| +uint32_t IVFWriter::WriteFrameHeader(uint8_t* mem, |
| + uint64_t pts, |
| + size_t frame_size) { |
| + PutLE32(mem, (int)frame_size); |
| + PutLE32(mem + 4, (int)(pts & 0xFFFFFFFF)); |
| + PutLE32(mem + 8, (int)(pts >> 32)); |
| + |
| + return 12; |
| } |
| // This object is the global object representing this plugin library as long |
| @@ -106,11 +115,17 @@ class VideoEncoderInstance : public pp::Instance { |
| virtual void HandleMessage(const pp::Var& var_message); |
| private: |
| + void AddVideoProfile(PP_VideoProfile profile, const std::string& profile_str); |
| + void InitializeVideoProfiles(); |
| + PP_VideoProfile VideoProfileFromString(const std::string& str); |
| + std::string VideoProfileToString(PP_VideoProfile profile); |
| + |
| void ConfigureTrack(); |
| void OnConfiguredTrack(int32_t result); |
| void ProbeEncoder(); |
| void OnEncoderProbed(int32_t result, |
| const std::vector<PP_VideoProfileDescription> profiles); |
| + void StartEncoder(); |
| void OnInitializedEncoder(int32_t result); |
| void ScheduleNextEncode(); |
| void GetEncoderFrameTick(int32_t result); |
| @@ -134,6 +149,12 @@ class VideoEncoderInstance : public pp::Instance { |
| void PostDataMessage(const void* buffer, uint32_t size); |
| void PostSignalMessage(const char* name); |
| + typedef std::map<std::string, PP_VideoProfile> VideoProfileFromStringMap; |
| + VideoProfileFromStringMap profile_from_string_; |
| + |
| + typedef std::map<PP_VideoProfile, std::string> VideoProfileToStringMap; |
| + VideoProfileToStringMap profile_to_string_; |
| + |
| bool is_encoding_; |
| bool is_receiving_track_frames_; |
| @@ -150,6 +171,8 @@ class VideoEncoderInstance : public pp::Instance { |
| uint32_t encoded_frames_; |
| pp::VideoFrame current_track_frame_; |
| + |
| + IVFWriter ivf_writer_; |
| }; |
| VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance, |
| @@ -164,11 +187,52 @@ VideoEncoderInstance::VideoEncoderInstance(PP_Instance instance, |
| #endif |
| frame_format_(PP_VIDEOFRAME_FORMAT_I420), |
| encoded_frames_(0) { |
| + InitializeVideoProfiles(); |
| + ProbeEncoder(); |
| } |
| VideoEncoderInstance::~VideoEncoderInstance() { |
| } |
| +void VideoEncoderInstance::AddVideoProfile(PP_VideoProfile profile, |
| + const std::string& profile_str) { |
| + profile_to_string_.insert(std::make_pair(profile, profile_str)); |
| + profile_from_string_.insert(std::make_pair(profile_str, profile)); |
| +} |
| + |
| +void VideoEncoderInstance::InitializeVideoProfiles() { |
| + AddVideoProfile(PP_VIDEOPROFILE_H264BASELINE, "h264baseline"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264MAIN, "h264main"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264EXTENDED, "h264extended"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264HIGH, "h264high"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264HIGH10PROFILE, "h264high10"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264HIGH422PROFILE, "h264high422"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE, |
| + "h264high444predictive"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEBASELINE, "h264scalablebaseline"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264SCALABLEHIGH, "h264scalablehigh"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264STEREOHIGH, "h264stereohigh"); |
| + AddVideoProfile(PP_VIDEOPROFILE_H264MULTIVIEWHIGH, "h264multiviewhigh"); |
| + AddVideoProfile(PP_VIDEOPROFILE_VP8_ANY, "vp8"); |
| + AddVideoProfile(PP_VIDEOPROFILE_VP9_ANY, "vp9"); |
| +} |
| + |
| +PP_VideoProfile VideoEncoderInstance::VideoProfileFromString( |
| + const std::string& str) { |
| + VideoProfileFromStringMap::iterator it = profile_from_string_.find(str); |
| + if (it == profile_from_string_.end()) |
| + return PP_VIDEOPROFILE_VP8_ANY; |
| + return it->second; |
| +} |
| + |
| +std::string VideoEncoderInstance::VideoProfileToString( |
| + PP_VideoProfile profile) { |
| + VideoProfileToStringMap::iterator it = profile_to_string_.find(profile); |
| + if (it == profile_to_string_.end()) |
| + return "unknown"; |
| + return it->second; |
| +} |
| + |
| void VideoEncoderInstance::ConfigureTrack() { |
| if (encoder_size_.IsEmpty()) |
| frame_size_ = requested_size_; |
| @@ -204,7 +268,7 @@ void VideoEncoderInstance::OnConfiguredTrack(int32_t result) { |
| StartTrackFrames(); |
| ScheduleNextEncode(); |
| } else |
| - ProbeEncoder(); |
| + StartEncoder(); |
| } |
| void VideoEncoderInstance::ProbeEncoder() { |
| @@ -216,38 +280,25 @@ void VideoEncoderInstance::ProbeEncoder() { |
| void VideoEncoderInstance::OnEncoderProbed( |
| int32_t result, |
| const std::vector<PP_VideoProfileDescription> profiles) { |
| - bool has_required_profile = false; |
| + pp::VarDictionary dict; |
| + dict.Set(pp::Var("name"), pp::Var("supportedProfiles")); |
| + pp::VarArray js_profiles; |
| + dict.Set(pp::Var("profiles"), js_profiles); |
| - Log("Available profiles:"); |
| - for (const PP_VideoProfileDescription& profile : profiles) { |
| - std::ostringstream oss; |
| - oss << " profile=" << VideoProfileToString(profile.profile) |
| - << " max_resolution=" << profile.max_resolution.width << "x" |
| - << profile.max_resolution.height |
| - << " max_framerate=" << profile.max_framerate_numerator << "/" |
| - << profile.max_framerate_denominator << " acceleration=" |
| - << HardwareAccelerationToString(profile.acceleration); |
| - Log(oss.str()); |
| - |
| - has_required_profile |= profile.profile == video_profile_; |
| + if (result != PP_OK) { |
| + LogError(result, "Cannot get supported profiles"); |
| + PostMessage(dict); |
| } |
| - if (!has_required_profile) { |
| - std::ostringstream oss; |
| - oss << "Cannot find required video profile: "; |
| - oss << VideoProfileToString(video_profile_); |
| - LogError(PP_ERROR_FAILED, oss.str()); |
| - return; |
| - } |
| + int32_t idx = 0; |
| + for (const PP_VideoProfileDescription& profile : profiles) |
| + js_profiles.Set(idx++, pp::Var(VideoProfileToString(profile.profile))); |
| + PostMessage(dict); |
| +} |
| +void VideoEncoderInstance::StartEncoder() { |
| video_encoder_ = pp::VideoEncoder(this); |
| - pp::VarDictionary dict; |
| - dict.Set(pp::Var("status"), pp::Var("initializing encoder")); |
| - dict.Set(pp::Var("width"), pp::Var(encoder_size_.width())); |
| - dict.Set(pp::Var("height"), pp::Var(encoder_size_.height())); |
| - PostMessage(dict); |
| - |
| int32_t error = video_encoder_.Initialize( |
| frame_format_, frame_size_, video_profile_, 2000000, |
| PP_HARDWAREACCELERATION_WITHFALLBACK, |
| @@ -266,18 +317,13 @@ void VideoEncoderInstance::OnInitializedEncoder(int32_t result) { |
| } |
| is_encoding_ = true; |
| + PostSignalMessage("started"); |
| if (video_encoder_.GetFrameCodedSize(&encoder_size_) != PP_OK) { |
| LogError(result, "Cannot get encoder coded frame size"); |
| return; |
| } |
| - pp::VarDictionary dict; |
| - dict.Set(pp::Var("status"), pp::Var("encoder initialized")); |
| - dict.Set(pp::Var("width"), pp::Var(encoder_size_.width())); |
| - dict.Set(pp::Var("height"), pp::Var(encoder_size_.height())); |
| - PostMessage(dict); |
| - |
| video_encoder_.GetBitstreamBuffer(callback_factory_.NewCallbackWithOutput( |
| &VideoEncoderInstance::OnGetBitstreamBuffer)); |
| @@ -348,6 +394,7 @@ int32_t VideoEncoderInstance::CopyVideoFrame(pp::VideoFrame dest, |
| return PP_ERROR_FAILED; |
| } |
| + dest.SetTimestamp(src.GetTimestamp()); |
| memcpy(dest.GetDataBuffer(), src.GetDataBuffer(), src.GetDataBufferSize()); |
| return PP_OK; |
| } |
| @@ -446,6 +493,8 @@ void VideoEncoderInstance::HandleMessage(const pp::Var& var_message) { |
| pp::Resource resource_track = var_track.AsResource(); |
| video_track_ = pp::MediaStreamVideoTrack(resource_track); |
| video_encoder_ = pp::VideoEncoder(); |
| + video_profile_ = VideoProfileFromString( |
| + dict_message.Get("profile").AsString()); |
| ConfigureTrack(); |
| } else if (command == "stop") { |
| StopEncode(); |
| @@ -460,9 +509,34 @@ void VideoEncoderInstance::PostDataMessage(const void* buffer, uint32_t size) { |
| dictionary.Set(pp::Var("name"), pp::Var("data")); |
| - pp::VarArrayBuffer array_buffer(size); |
| - void* data_ptr = array_buffer.Map(); |
| - memcpy(data_ptr, buffer, size); |
| + pp::VarArrayBuffer array_buffer; |
| + uint8_t* data_ptr; |
| + uint32_t data_offset = 0; |
| + if (video_profile_ == PP_VIDEOPROFILE_VP8_ANY || |
| + video_profile_ == PP_VIDEOPROFILE_VP9_ANY) { |
| + uint32_t frame_offset = 0; |
| + if (encoded_frames_ == 1) { |
| + array_buffer = pp::VarArrayBuffer( |
| + size + ivf_writer_.GetFileHeaderSize() + |
| + ivf_writer_.GetFrameHeaderSize()); |
| + data_ptr = static_cast<uint8_t*>(array_buffer.Map()); |
| + frame_offset = ivf_writer_.WriteFileHeader( |
| + data_ptr, frame_size_.width(), frame_size_.height()); |
| + } else { |
| + array_buffer = pp::VarArrayBuffer( |
| + size + ivf_writer_.GetFrameHeaderSize()); |
| + data_ptr = static_cast<uint8_t*>(array_buffer.Map()); |
| + } |
| + data_offset = frame_offset + |
| + ivf_writer_.WriteFrameHeader(data_ptr + frame_offset, |
| + encoded_frames_, |
| + size); |
| + } else { |
| + array_buffer = pp::VarArrayBuffer(size); |
| + data_ptr = static_cast<uint8_t*>(array_buffer.Map()); |
| + } |
| + |
| + memcpy(data_ptr + data_offset, buffer, size); |
| array_buffer.Unmap(); |
| dictionary.Set(pp::Var("data"), array_buffer); |