diff options
author | marpan@webrtc.org <marpan@webrtc.org> | 2014-11-01 06:10:48 +0000 |
---|---|---|
committer | marpan@webrtc.org <marpan@webrtc.org> | 2014-11-01 06:10:48 +0000 |
commit | 663738893094f5181b0ecaa9d97396b49fb92b3a (patch) | |
tree | 481320663dd399abe62a4b99f90d3d2e5feb8686 | |
parent | 0eb406686ab0fd732c357b69cfdae52da7bd9c98 (diff) | |
download | webrtc-663738893094f5181b0ecaa9d97396b49fb92b3a.tar.gz |
Add VP9 codec to VCM and vie_auto_test.
Include VP9 tests in videoprocessor_integrationtests.
Include end-to-end send/receiveVP9 test.
This is the same patch as https://code.google.com/p/webrtc/source/detail?r=7422, which was reverted when rolled into chrome (due to bss size increase). Relanding this again as we now have the clear to get this in:
see https://code.google.com/p/webrtc/issues/detail?id=3932
R=kjellander@webrtc.org, mflodman@webrtc.org, stefan@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/31829004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7588 4adac7df-926f-26a2-2b94-8c16560cd09d
28 files changed, 1146 insertions, 161 deletions
diff --git a/build/common.gypi b/build/common.gypi index e89f2f21..366e7e9f 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -42,6 +42,7 @@ 'modules_java_gyp_path%': '<(modules_java_gyp_path)', 'gen_core_neon_offsets_gyp%': '<(gen_core_neon_offsets_gyp)', 'webrtc_vp8_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp8', + 'webrtc_vp9_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp9', 'rbe_components_path%': '<(webrtc_root)/modules/remote_bitrate_estimator', 'include_opus%': 1, }, @@ -52,6 +53,7 @@ 'modules_java_gyp_path%': '<(modules_java_gyp_path)', 'gen_core_neon_offsets_gyp%': '<(gen_core_neon_offsets_gyp)', 'webrtc_vp8_dir%': '<(webrtc_vp8_dir)', + 'webrtc_vp9_dir%': '<(webrtc_vp9_dir)', 'include_opus%': '<(include_opus)', 'rtc_relative_path%': 1, 'rbe_components_path%': '<(rbe_components_path)', diff --git a/build/tsan_suppressions_webrtc.cc b/build/tsan_suppressions_webrtc.cc index 723e62a2..01658ed2 100644 --- a/build/tsan_suppressions_webrtc.cc +++ b/build/tsan_suppressions_webrtc.cc @@ -27,6 +27,7 @@ char kTSanDefaultSuppressions[] = "race:rtc::MessageQueue::Quit\n" "race:FileVideoCapturerTest::VideoCapturerListener::OnFrameCaptured\n" "race:vp8cx_remove_encoder_threads\n" +"race:third_party/libvpx/source/libvpx/vp9/common/vp9_scan.h\n" // Usage of trace callback and trace level is racy in libjingle_media_unittests. // https://code.google.com/p/webrtc/issues/detail?id=3372 diff --git a/common_types.h b/common_types.h index 8a20e5e0..7bcfd6d4 100644 --- a/common_types.h +++ b/common_types.h @@ -611,35 +611,45 @@ struct VideoCodecVP8 { } }; -// H264 specific. -struct VideoCodecH264 -{ - VideoCodecProfile profile; - bool frameDroppingOn; - int keyFrameInterval; - // These are NULL/0 if not externally negotiated. - const uint8_t* spsData; - size_t spsLen; - const uint8_t* ppsData; - size_t ppsLen; +// VP9 specific +struct VideoCodecVP9 { + VideoCodecComplexity complexity; + int resilience; + unsigned char numberOfTemporalLayers; + bool denoisingOn; + bool frameDroppingOn; + int keyFrameInterval; + bool adaptiveQpMode; }; -// Video codec types -enum VideoCodecType -{ - kVideoCodecVP8, - kVideoCodecH264, - kVideoCodecI420, - kVideoCodecRED, - kVideoCodecULPFEC, - kVideoCodecGeneric, - kVideoCodecUnknown +// H264 specific. +struct VideoCodecH264 { + VideoCodecProfile profile; + bool frameDroppingOn; + int keyFrameInterval; + // These are NULL/0 if not externally negotiated. + const uint8_t* spsData; + size_t spsLen; + const uint8_t* ppsData; + size_t ppsLen; }; -union VideoCodecUnion -{ - VideoCodecVP8 VP8; - VideoCodecH264 H264; +// Video codec types +enum VideoCodecType { + kVideoCodecVP8, + kVideoCodecVP9, + kVideoCodecH264, + kVideoCodecI420, + kVideoCodecRED, + kVideoCodecULPFEC, + kVideoCodecGeneric, + kVideoCodecUnknown +}; + +union VideoCodecUnion { + VideoCodecVP8 VP8; + VideoCodecVP9 VP9; + VideoCodecH264 H264; }; diff --git a/engine_configurations.h b/engine_configurations.h index a91532fc..5b093e5c 100644 --- a/engine_configurations.h +++ b/engine_configurations.h @@ -51,6 +51,7 @@ #define VIDEOCODEC_I420 #define VIDEOCODEC_VP8 +#define VIDEOCODEC_VP9 #define VIDEOCODEC_H264 // ============================================================================ diff --git a/modules/modules.gyp b/modules/modules.gyp index 6d0de1e2..3caf41f9 100644 --- a/modules/modules.gyp +++ b/modules/modules.gyp @@ -97,6 +97,7 @@ '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', '<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8', + '<(webrtc_root)/modules/video_coding/codecs/vp9/vp9.gyp:webrtc_vp9', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/test/test.gyp:frame_generator', '<(webrtc_root)/test/test.gyp:rtp_test_utils', @@ -326,6 +327,7 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(webrtc_root)/common_video/common_video.gyp:common_video', '<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8', + '<(webrtc_root)/modules/video_coding/codecs/vp9/vp9.gyp:webrtc_vp9', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/test/metrics.gyp:metrics', '<(webrtc_root)/test/test.gyp:test_support', diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index 07e7afc0..706e9d99 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -77,6 +77,7 @@ source_set("video_coding") { ":video_coding_utility", ":webrtc_i420", ":webrtc_vp8", + ":webrtc_vp9", "../../common_video", "../../system_wrappers", ] @@ -159,3 +160,35 @@ source_set("webrtc_vp8") { ] } } + +source_set("webrtc_vp9") { + sources = [ + "codecs/vp9/include/vp9.h", + "codecs/vp9/vp9_impl.cc", + "codecs/vp9/vp9_impl.h", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + + # TODO(kjellander): Remove once libvpx has changed it's libvpx_config to be + # in direct_dependent_configs. + configs += [ "//third_party/libvpx:libvpx_config" ] + + deps = [ + ":video_coding_utility", + "../../common_video", + "../../system_wrappers", + ] + if (rtc_build_libvpx) { + deps += [ + "//third_party/libvpx", + ] + } +} diff --git a/modules/video_coding/codecs/interface/video_codec_interface.h b/modules/video_coding/codecs/interface/video_codec_interface.h index 1c1d6ed6..da72febb 100644 --- a/modules/video_coding/codecs/interface/video_codec_interface.h +++ b/modules/video_coding/codecs/interface/video_codec_interface.h @@ -42,6 +42,19 @@ struct CodecSpecificInfoVP8 { int8_t keyIdx; // Negative value to skip keyIdx. }; +struct CodecSpecificInfoVP9 { + bool hasReceivedSLI; + uint8_t pictureIdSLI; + bool hasReceivedRPSI; + uint64_t pictureIdRPSI; + int16_t pictureId; // Negative value to skip pictureId. + bool nonReference; + uint8_t temporalIdx; + bool layerSync; + int tl0PicIdx; // Negative value to skip tl0PicIdx. + int8_t keyIdx; // Negative value to skip keyIdx. +}; + struct CodecSpecificInfoGeneric { uint8_t simulcast_idx; }; @@ -51,6 +64,7 @@ struct CodecSpecificInfoH264 {}; union CodecSpecificInfoUnion { CodecSpecificInfoGeneric generic; CodecSpecificInfoVP8 VP8; + CodecSpecificInfoVP9 VP9; CodecSpecificInfoH264 H264; }; diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc index bd4a563f..69f4d67a 100644 --- a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc +++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc @@ -16,6 +16,7 @@ #include "webrtc/modules/video_coding/codecs/test/packet_manipulator.h" #include "webrtc/modules/video_coding/codecs/test/videoprocessor.h" #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h" #include "webrtc/modules/video_coding/main/interface/video_coding.h" #include "webrtc/test/testsupport/fileutils.h" @@ -37,6 +38,7 @@ const int kBaseKeyFrameInterval = 3000; // Codec and network settings. struct CodecConfigPars { + VideoCodecType codec_type; float packet_loss; int num_temporal_layers; int key_frame_interval; @@ -136,6 +138,7 @@ class VideoProcessorIntegrationTest: public testing::Test { float start_bitrate_; // Codec and network settings. + VideoCodecType codec_type_; float packet_loss_; int num_temporal_layers_; int key_frame_interval_; @@ -149,8 +152,15 @@ class VideoProcessorIntegrationTest: public testing::Test { virtual ~VideoProcessorIntegrationTest() {} void SetUpCodecConfig() { - encoder_ = VP8Encoder::Create(); - decoder_ = VP8Decoder::Create(); + if (codec_type_ == kVideoCodecVP8) { + encoder_ = VP8Encoder::Create(); + decoder_ = VP8Decoder::Create(); + VideoCodingModule::Codec(kVideoCodecVP8, &codec_settings_); + } else if (codec_type_ == kVideoCodecVP9) { + encoder_ = VP9Encoder::Create(); + decoder_ = VP9Decoder::Create(); + VideoCodingModule::Codec(kVideoCodecVP9, &codec_settings_); + } // CIF is currently used for all tests below. // Setup the TestConfig struct for processing of a clip in CIF resolution. @@ -169,26 +179,42 @@ class VideoProcessorIntegrationTest: public testing::Test { config_.keyframe_interval = key_frame_interval_; config_.networking_config.packet_loss_probability = packet_loss_; - // Get a codec configuration struct and configure it. - VideoCodingModule::Codec(kVideoCodecVP8, &codec_settings_); + // Configure codec settings. config_.codec_settings = &codec_settings_; config_.codec_settings->startBitrate = start_bitrate_; config_.codec_settings->width = kCIFWidth; config_.codec_settings->height = kCIFHeight; - // These features may be set depending on the test. - config_.codec_settings->codecSpecific.VP8.errorConcealmentOn = - error_concealment_on_; - config_.codec_settings->codecSpecific.VP8.denoisingOn = - denoising_on_; - config_.codec_settings->codecSpecific.VP8.numberOfTemporalLayers = - num_temporal_layers_; - config_.codec_settings->codecSpecific.VP8.frameDroppingOn = - frame_dropper_on_; - config_.codec_settings->codecSpecific.VP8.automaticResizeOn = - spatial_resize_on_; - config_.codec_settings->codecSpecific.VP8.keyFrameInterval = - kBaseKeyFrameInterval; + // These features may be set depending on the test. + switch (config_.codec_settings->codecType) { + case kVideoCodecVP8: + config_.codec_settings->codecSpecific.VP8.errorConcealmentOn = + error_concealment_on_; + config_.codec_settings->codecSpecific.VP8.denoisingOn = + denoising_on_; + config_.codec_settings->codecSpecific.VP8.numberOfTemporalLayers = + num_temporal_layers_; + config_.codec_settings->codecSpecific.VP8.frameDroppingOn = + frame_dropper_on_; + config_.codec_settings->codecSpecific.VP8.automaticResizeOn = + spatial_resize_on_; + config_.codec_settings->codecSpecific.VP8.keyFrameInterval = + kBaseKeyFrameInterval; + break; + case kVideoCodecVP9: + config_.codec_settings->codecSpecific.VP9.denoisingOn = + denoising_on_; + config_.codec_settings->codecSpecific.VP9.numberOfTemporalLayers = + num_temporal_layers_; + config_.codec_settings->codecSpecific.VP9.frameDroppingOn = + frame_dropper_on_; + config_.codec_settings->codecSpecific.VP9.keyFrameInterval = + kBaseKeyFrameInterval; + break; + default: + assert(false); + break; + } frame_reader_ = new webrtc::test::FrameReaderImpl(config_.input_filename, config_.frame_length_in_bytes); @@ -405,6 +431,7 @@ class VideoProcessorIntegrationTest: public testing::Test { CodecConfigPars process, RateControlMetrics* rc_metrics) { // Codec/config settings. + codec_type_ = process.codec_type; start_bitrate_ = rate_profile.target_bit_rate[0]; packet_loss_ = process.packet_loss; key_frame_interval_ = process.key_frame_interval; @@ -514,6 +541,7 @@ void SetRateProfilePars(RateProfile* rate_profile, } void SetCodecParameters(CodecConfigPars* process_settings, + VideoCodecType codec_type, float packet_loss, int key_frame_interval, int num_temporal_layers, @@ -521,6 +549,7 @@ void SetCodecParameters(CodecConfigPars* process_settings, bool denoising_on, bool frame_dropper_on, bool spatial_resize_on) { + process_settings->codec_type = codec_type; process_settings->packet_loss = packet_loss; process_settings->key_frame_interval = key_frame_interval; process_settings->num_temporal_layers = num_temporal_layers, @@ -560,7 +589,126 @@ void SetRateControlMetrics(RateControlMetrics* rc_metrics, rc_metrics[update_index].num_spatial_resizes = num_spatial_resizes; } -// Run with no packet loss and fixed bitrate. Quality should be very high. +// VP9: Run with no packet loss and fixed bitrate. Quality should be very high. +// One key frame (first frame only) in sequence. Setting |key_frame_interval| +// to -1 below means no periodic key frames in test. +TEST_F(VideoProcessorIntegrationTest, Process0PercentPacketLossVP9) { + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 500, 30, 0); + rate_profile.frame_index_rate_update[1] = kNbrFramesShort + 1; + rate_profile.num_frames = kNbrFramesShort; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.0f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 37.5, 36.0, 0.94, 0.93); + // Metrics for rate control. + RateControlMetrics rc_metrics[1]; + SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + +// VP9: Run with 5% packet loss and fixed bitrate. Quality should be a bit +// lower. One key frame (first frame only) in sequence. +TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLossVP9) { + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 500, 30, 0); + rate_profile.frame_index_rate_update[1] = kNbrFramesShort + 1; + rate_profile.num_frames = kNbrFramesShort; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.05f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 17.0, 15.0, 0.45, 0.38); + // Metrics for rate control. + RateControlMetrics rc_metrics[1]; + SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + + +// VP9: Run with no packet loss, with varying bitrate (3 rate updates): +// low to high to medium. Check that quality and encoder response to the new +// target rate/per-frame bandwidth (for each rate update) is within limits. +// One key frame (first frame only) in sequence. +TEST_F(VideoProcessorIntegrationTest, ProcessNoLossChangeBitRateVP9) { + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 200, 30, 0); + SetRateProfilePars(&rate_profile, 1, 800, 30, 100); + SetRateProfilePars(&rate_profile, 2, 500, 30, 200); + rate_profile.frame_index_rate_update[3] = kNbrFramesLong + 1; + rate_profile.num_frames = kNbrFramesLong; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.0f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 36.0, 32.0, 0.90, 0.85); + // Metrics for rate control. + RateControlMetrics rc_metrics[3]; + SetRateControlMetrics(rc_metrics, 0, 0, 30, 20, 20, 20, 0); + SetRateControlMetrics(rc_metrics, 1, 2, 0, 20, 20, 60, 0); + SetRateControlMetrics(rc_metrics, 2, 0, 0, 20, 20, 30, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + +// VP9: Run with no packet loss, with an update (decrease) in frame rate. +// Lower frame rate means higher per-frame-bandwidth, so easier to encode. +// At the low bitrate in this test, this means better rate control after the +// update(s) to lower frame rate. So expect less frame drops, and max values +// for the rate control metrics can be lower. One key frame (first frame only). +// Note: quality after update should be higher but we currently compute quality +// metrics averaged over whole sequence run. +TEST_F(VideoProcessorIntegrationTest, + ProcessNoLossChangeFrameRateFrameDropVP9) { + config_.networking_config.packet_loss_probability = 0; + // Bitrate and frame rate profile. + RateProfile rate_profile; + SetRateProfilePars(&rate_profile, 0, 50, 24, 0); + SetRateProfilePars(&rate_profile, 1, 50, 15, 100); + SetRateProfilePars(&rate_profile, 2, 50, 10, 200); + rate_profile.frame_index_rate_update[3] = kNbrFramesLong + 1; + rate_profile.num_frames = kNbrFramesLong; + // Codec/network settings. + CodecConfigPars process_settings; + SetCodecParameters(&process_settings, kVideoCodecVP9, 0.0f, -1, 1, false, + false, true, false); + // Metrics for expected quality. + QualityMetrics quality_metrics; + SetQualityMetrics(&quality_metrics, 30.0, 18.0, 0.80, 0.40); + // Metrics for rate control. + RateControlMetrics rc_metrics[3]; + SetRateControlMetrics(rc_metrics, 0, 30, 30, 60, 15, 40, 0); + SetRateControlMetrics(rc_metrics, 1, 15, 0, 50, 10, 30, 0); + SetRateControlMetrics(rc_metrics, 2, 5, 0, 38, 10, 30, 0); + ProcessFramesAndVerify(quality_metrics, + rate_profile, + process_settings, + rc_metrics); +} + + +// TODO(marpan): Add temporal layer test for VP9, once changes are in +// vp9 wrapper for this. + +// VP8: Run with no packet loss and fixed bitrate. Quality should be very high. // One key frame (first frame only) in sequence. Setting |key_frame_interval| // to -1 below means no periodic key frames in test. TEST_F(VideoProcessorIntegrationTest, ProcessZeroPacketLoss) { @@ -571,7 +719,8 @@ TEST_F(VideoProcessorIntegrationTest, ProcessZeroPacketLoss) { rate_profile.num_frames = kNbrFramesShort; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 34.95, 33.0, 0.90, 0.89); @@ -584,8 +733,8 @@ TEST_F(VideoProcessorIntegrationTest, ProcessZeroPacketLoss) { rc_metrics); } -// Run with 5% packet loss and fixed bitrate. Quality should be a bit lower. -// One key frame (first frame only) in sequence. +// VP8: Run with 5% packet loss and fixed bitrate. Quality should be a bit +// lower. One key frame (first frame only) in sequence. TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLoss) { // Bitrate and frame rate profile. RateProfile rate_profile; @@ -594,7 +743,8 @@ TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLoss) { rate_profile.num_frames = kNbrFramesShort; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.05f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.05f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 20.0, 16.0, 0.60, 0.40); @@ -607,7 +757,7 @@ TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLoss) { rc_metrics); } -// Run with 10% packet loss and fixed bitrate. Quality should be even lower. +// VP8: Run with 10% packet loss and fixed bitrate. Quality should be lower. // One key frame (first frame only) in sequence. TEST_F(VideoProcessorIntegrationTest, Process10PercentPacketLoss) { // Bitrate and frame rate profile. @@ -617,7 +767,8 @@ TEST_F(VideoProcessorIntegrationTest, Process10PercentPacketLoss) { rate_profile.num_frames = kNbrFramesShort; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.1f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.1f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 19.0, 16.0, 0.50, 0.35); @@ -639,12 +790,12 @@ TEST_F(VideoProcessorIntegrationTest, Process10PercentPacketLoss) { // disabled on Android. Some quality parameter in the above test has been // adjusted to also pass for |cpu_speed| <= 12. -// Run with no packet loss, with varying bitrate (3 rate updates): +// VP8: Run with no packet loss, with varying bitrate (3 rate updates): // low to high to medium. Check that quality and encoder response to the new // target rate/per-frame bandwidth (for each rate update) is within limits. // One key frame (first frame only) in sequence. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossChangeBitRate)) { + DISABLED_ON_ANDROID(ProcessNoLossChangeBitRateVP8)) { // Bitrate and frame rate profile. RateProfile rate_profile; SetRateProfilePars(&rate_profile, 0, 200, 30, 0); @@ -654,7 +805,8 @@ TEST_F(VideoProcessorIntegrationTest, rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 34.0, 32.0, 0.85, 0.80); @@ -669,15 +821,15 @@ TEST_F(VideoProcessorIntegrationTest, rc_metrics); } -// Run with no packet loss, with an update (decrease) in frame rate. +// VP8: Run with no packet loss, with an update (decrease) in frame rate. // Lower frame rate means higher per-frame-bandwidth, so easier to encode. // At the bitrate in this test, this means better rate control after the // update(s) to lower frame rate. So expect less frame drops, and max values // for the rate control metrics can be lower. One key frame (first frame only). // Note: quality after update should be higher but we currently compute quality -// metrics avergaed over whole sequence run. +// metrics averaged over whole sequence run. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossChangeFrameRateFrameDrop)) { + DISABLED_ON_ANDROID(ProcessNoLossChangeFrameRateFrameDropVP8)) { config_.networking_config.packet_loss_probability = 0; // Bitrate and frame rate profile. RateProfile rate_profile; @@ -688,7 +840,8 @@ TEST_F(VideoProcessorIntegrationTest, rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 1, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 1, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 31.0, 22.0, 0.80, 0.65); @@ -706,7 +859,7 @@ TEST_F(VideoProcessorIntegrationTest, // Run with no packet loss, at low bitrate. During this time we should've // resized once. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossSpatialResizeFrameDrop)) { + DISABLED_ON_ANDROID(ProcessNoLossSpatialResizeFrameDropVP8)) { config_.networking_config.packet_loss_probability = 0; // Bitrate and frame rate profile. RateProfile rate_profile; @@ -715,8 +868,8 @@ TEST_F(VideoProcessorIntegrationTest, rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters( - &process_settings, 0.0f, kNbrFramesLong, 1, false, true, true, true); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, kNbrFramesLong, + 1, false, true, true, true); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 25.0, 15.0, 0.70, 0.40); @@ -729,13 +882,13 @@ TEST_F(VideoProcessorIntegrationTest, rc_metrics); } -// Run with no packet loss, with 3 temporal layers, with a rate update in the -// middle of the sequence. The max values for the frame size mismatch and +// VP8: Run with no packet loss, with 3 temporal layers, with a rate update in +// the middle of the sequence. The max values for the frame size mismatch and // encoding rate mismatch are applied to each layer. // No dropped frames in this test, and internal spatial resizer is off. // One key frame (first frame only) in sequence, so no spatial resizing. TEST_F(VideoProcessorIntegrationTest, - DISABLED_ON_ANDROID(ProcessNoLossTemporalLayers)) { + DISABLED_ON_ANDROID(ProcessNoLossTemporalLayersVP8)) { config_.networking_config.packet_loss_probability = 0; // Bitrate and frame rate profile. RateProfile rate_profile; @@ -745,7 +898,8 @@ TEST_F(VideoProcessorIntegrationTest, rate_profile.num_frames = kNbrFramesLong; // Codec/network settings. CodecConfigPars process_settings; - SetCodecParameters(&process_settings, 0.0f, -1, 3, false, true, true, false); + SetCodecParameters(&process_settings, kVideoCodecVP8, 0.0f, -1, 3, false, + true, true, false); // Metrics for expected quality. QualityMetrics quality_metrics; SetQualityMetrics(&quality_metrics, 32.5, 30.0, 0.85, 0.80); diff --git a/modules/video_coding/codecs/vp8/vp8_impl.h b/modules/video_coding/codecs/vp8/vp8_impl.h index 08ce3c91..fec53d53 100644 --- a/modules/video_coding/codecs/vp8/vp8_impl.h +++ b/modules/video_coding/codecs/vp8/vp8_impl.h @@ -35,73 +35,20 @@ class VP8EncoderImpl : public VP8Encoder { virtual ~VP8EncoderImpl(); - // Free encoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int Release(); - // Initialize the encoder with the information from the codecSettings - // - // Input: - // - codec_settings : Codec settings - // - number_of_cores : Number of cores available for the encoder - // - max_payload_size : The maximum size each payload is allowed - // to have. Usually MTU - overhead. - // - // Return value : Set bit rate if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERR_PARAMETER - // WEBRTC_VIDEO_CODEC_ERR_SIZE - // WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED - // WEBRTC_VIDEO_CODEC_MEMORY - // WEBRTC_VIDEO_CODEC_ERROR virtual int InitEncode(const VideoCodec* codec_settings, int number_of_cores, uint32_t max_payload_size); - // Encode an I420 image (as a part of a video stream). The encoded image - // will be returned to the user through the encode complete callback. - // - // Input: - // - input_image : Image to be encoded - // - frame_types : Frame type to be generated by the encoder. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERR_PARAMETER - // WEBRTC_VIDEO_CODEC_MEMORY - // WEBRTC_VIDEO_CODEC_ERROR - // WEBRTC_VIDEO_CODEC_TIMEOUT - virtual int Encode(const I420VideoFrame& input_image, const CodecSpecificInfo* codec_specific_info, const std::vector<VideoFrameType>* frame_types); - // Register an encode complete callback object. - // - // Input: - // - callback : Callback object which handles encoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int RegisterEncodeCompleteCallback(EncodedImageCallback* callback); - // Inform the encoder of the new packet loss rate and the round-trip time of - // the network. - // - // - packet_loss : Fraction lost - // (loss rate in percent = 100 * packetLoss / 255) - // - rtt : Round-trip time in milliseconds - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: WEBRTC_VIDEO_CODEC_ERROR - // virtual int SetChannelParameters(uint32_t packet_loss, int rtt); - // Inform the encoder about the new target bit rate. - // - // - new_bitrate_kbit : New target bit rate - // - frame_rate : The target frame rate - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate); private: @@ -150,61 +97,20 @@ class VP8DecoderImpl : public VP8Decoder { virtual ~VP8DecoderImpl(); - // Initialize the decoder. - // - // Return value : WEBRTC_VIDEO_CODEC_OK. - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERROR virtual int InitDecode(const VideoCodec* inst, int number_of_cores); - // Decode encoded image (as a part of a video stream). The decoded image - // will be returned to the user through the decode complete callback. - // - // Input: - // - input_image : Encoded image to be decoded - // - missing_frames : True if one or more frames have been lost - // since the previous decode call. - // - fragmentation : Specifies the start and length of each VP8 - // partition. - // - codec_specific_info : pointer to specific codec data - // - render_time_ms : Render time in Ms - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERROR - // WEBRTC_VIDEO_CODEC_ERR_PARAMETER virtual int Decode(const EncodedImage& input_image, bool missing_frames, const RTPFragmentationHeader* fragmentation, const CodecSpecificInfo* codec_specific_info, int64_t /*render_time_ms*/); - // Register a decode complete callback object. - // - // Input: - // - callback : Callback object which handles decoded images. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int RegisterDecodeCompleteCallback(DecodedImageCallback* callback); - // Free decoder memory. - // - // Return value : WEBRTC_VIDEO_CODEC_OK if OK - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_ERROR virtual int Release(); - // Reset decoder state and prepare for a new call. - // - // Return value : WEBRTC_VIDEO_CODEC_OK. - // <0 - Errors: - // WEBRTC_VIDEO_CODEC_UNINITIALIZED - // WEBRTC_VIDEO_CODEC_ERROR virtual int Reset(); - // Create a copy of the codec and its internal state. - // - // Return value : A copy of the instance if OK, NULL otherwise. virtual VideoDecoder* Copy(); private: diff --git a/modules/video_coding/codecs/vp9/include/vp9.h b/modules/video_coding/codecs/vp9/include/vp9.h new file mode 100644 index 00000000..cd77f72d --- /dev/null +++ b/modules/video_coding/codecs/vp9/include/vp9.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_ +#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_ + +#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" + +namespace webrtc { + +class VP9Encoder : public VideoEncoder { + public: + static VP9Encoder* Create(); + + virtual ~VP9Encoder() {} +}; + + +class VP9Decoder : public VideoDecoder { + public: + static VP9Decoder* Create(); + + virtual ~VP9Decoder() {} +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_H_ diff --git a/modules/video_coding/codecs/vp9/vp9.gyp b/modules/video_coding/codecs/vp9/vp9.gyp new file mode 100644 index 00000000..2bd46feb --- /dev/null +++ b/modules/video_coding/codecs/vp9/vp9.gyp @@ -0,0 +1,36 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'includes': [ + '../../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'webrtc_vp9', + 'type': 'static_library', + 'dependencies': [ + '<(webrtc_root)/common_video/common_video.gyp:common_video', + '<(webrtc_root)/modules/video_coding/utility/video_coding_utility.gyp:video_coding_utility', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'conditions': [ + ['build_libvpx==1', { + 'dependencies': [ + '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx', + ], + }], + ], + 'sources': [ + 'include/vp9.h', + 'vp9_impl.cc', + 'vp9_impl.h', + ], + }, + ], +} diff --git a/modules/video_coding/codecs/vp9/vp9_impl.cc b/modules/video_coding/codecs/vp9/vp9_impl.cc new file mode 100644 index 00000000..33f11a37 --- /dev/null +++ b/modules/video_coding/codecs/vp9/vp9_impl.cc @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#include "webrtc/modules/video_coding/codecs/vp9/vp9_impl.h" + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <vector> + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_decoder.h" +#include "vpx/vp8cx.h" +#include "vpx/vp8dx.h" + +#include "webrtc/common.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/system_wrappers/interface/tick_util.h" +#include "webrtc/system_wrappers/interface/trace_event.h" + +namespace webrtc { + +VP9Encoder* VP9Encoder::Create() { + return new VP9EncoderImpl(); +} + +VP9EncoderImpl::VP9EncoderImpl() + : encoded_image_(), + encoded_complete_callback_(NULL), + inited_(false), + timestamp_(0), + picture_id_(0), + cpu_speed_(3), + rc_max_intra_target_(0), + encoder_(NULL), + config_(NULL), + raw_(NULL) { + memset(&codec_, 0, sizeof(codec_)); + uint32_t seed = static_cast<uint32_t>(TickTime::MillisecondTimestamp()); + srand(seed); +} + +VP9EncoderImpl::~VP9EncoderImpl() { + Release(); +} + +int VP9EncoderImpl::Release() { + if (encoded_image_._buffer != NULL) { + delete [] encoded_image_._buffer; + encoded_image_._buffer = NULL; + } + if (encoder_ != NULL) { + if (vpx_codec_destroy(encoder_)) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + delete encoder_; + encoder_ = NULL; + } + if (config_ != NULL) { + delete config_; + config_ = NULL; + } + if (raw_ != NULL) { + vpx_img_free(raw_); + raw_ = NULL; + } + inited_ = false; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit, + uint32_t new_framerate) { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (encoder_->err) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + if (new_framerate < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + // Update bit rate + if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) { + new_bitrate_kbit = codec_.maxBitrate; + } + config_->rc_target_bitrate = new_bitrate_kbit; + codec_.maxFramerate = new_framerate; + // Update encoder context + if (vpx_codec_enc_config_set(encoder_, config_)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::InitEncode(const VideoCodec* inst, + int number_of_cores, + uint32_t /*max_payload_size*/) { + if (inst == NULL) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->maxFramerate < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + // Allow zero to represent an unspecified maxBitRate + if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->width < 1 || inst->height < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (number_of_cores < 1) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + int retVal = Release(); + if (retVal < 0) { + return retVal; + } + if (encoder_ == NULL) { + encoder_ = new vpx_codec_ctx_t; + } + if (config_ == NULL) { + config_ = new vpx_codec_enc_cfg_t; + } + timestamp_ = 0; + if (&codec_ != inst) { + codec_ = *inst; + } + // Random start 16 bits is enough. + picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF; + // Allocate memory for encoded image + if (encoded_image_._buffer != NULL) { + delete [] encoded_image_._buffer; + } + encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height); + encoded_image_._buffer = new uint8_t[encoded_image_._size]; + encoded_image_._completeFrame = true; + // Creating a wrapper to the image - setting image data to NULL. Actual + // pointer will be set in encode. Setting align to 1, as it is meaningless + // (actual memory is not allocated). + raw_ = vpx_img_wrap(NULL, VPX_IMG_FMT_I420, codec_.width, codec_.height, + 1, NULL); + // Populate encoder configuration with default values. + if (vpx_codec_enc_config_default(vpx_codec_vp9_cx(), config_, 0)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + config_->g_w = codec_.width; + config_->g_h = codec_.height; + config_->rc_target_bitrate = inst->startBitrate; // in kbit/s + config_->g_error_resilient = 1; + // Setting the time base of the codec. + config_->g_timebase.num = 1; + config_->g_timebase.den = 90000; + config_->g_lag_in_frames = 0; // 0- no frame lagging + config_->g_threads = 1; + // Rate control settings. + config_->rc_dropframe_thresh = inst->codecSpecific.VP9.frameDroppingOn ? + 30 : 0; + config_->rc_end_usage = VPX_CBR; + config_->g_pass = VPX_RC_ONE_PASS; + config_->rc_min_quantizer = 2; + config_->rc_max_quantizer = 56; + config_->rc_undershoot_pct = 50; + config_->rc_overshoot_pct = 50; + config_->rc_buf_initial_sz = 500; + config_->rc_buf_optimal_sz = 600; + config_->rc_buf_sz = 1000; + // Set the maximum target size of any key-frame. + rc_max_intra_target_ = MaxIntraTarget(config_->rc_buf_optimal_sz); + if (inst->codecSpecific.VP9.keyFrameInterval > 0) { + config_->kf_mode = VPX_KF_AUTO; + config_->kf_max_dist = inst->codecSpecific.VP9.keyFrameInterval; + } else { + config_->kf_mode = VPX_KF_DISABLED; + } + return InitAndSetControlSettings(inst); +} + +int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) { + if (vpx_codec_enc_init(encoder_, vpx_codec_vp9_cx(), config_, 0)) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + // Only positive speeds, currently: 0 - 7. + // O means slowest/best quality, 7 means fastest/lowest quality. + // TODO(marpan): Speeds 5-7 are speed settings for real-time mode, on desktop. + // Currently set to 5, update to 6 (for faster encoding) after some subjective + // quality tests. + cpu_speed_ = 5; + // Note: some of these codec controls still use "VP8" in the control name. + // TODO(marpan): Update this in the next/future libvpx version. + vpx_codec_control(encoder_, VP8E_SET_CPUUSED, cpu_speed_); + vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT, + rc_max_intra_target_); + vpx_codec_control(encoder_, VP9E_SET_AQ_MODE, + inst->codecSpecific.VP9.adaptiveQpMode ? 3 : 0); + // TODO(marpan): Enable in future libvpx roll: waiting for SSE2 optimization. +// #if !defined(WEBRTC_ARCH_ARM) + // vpx_codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, + // inst->codecSpecific.VP9.denoisingOn ? 1 : 0); +// #endif + inited_ = true; + return WEBRTC_VIDEO_CODEC_OK; +} + +uint32_t VP9EncoderImpl::MaxIntraTarget(uint32_t optimal_buffer_size) { + // Set max to the optimal buffer level (normalized by target BR), + // and scaled by a scale_par. + // Max target size = scale_par * optimal_buffer_size * targetBR[Kbps]. + // This value is presented in percentage of perFrameBw: + // perFrameBw = targetBR[Kbps] * 1000 / framerate. + // The target in % is as follows: + float scale_par = 0.5; + uint32_t target_pct = + optimal_buffer_size * scale_par * codec_.maxFramerate / 10; + // Don't go below 3 times the per frame bandwidth. + const uint32_t min_intra_size = 300; + return (target_pct < min_intra_size) ? min_intra_size: target_pct; +} + +int VP9EncoderImpl::Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector<VideoFrameType>* frame_types) { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (input_image.IsZeroSize()) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (encoded_complete_callback_ == NULL) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + VideoFrameType frame_type = kDeltaFrame; + // We only support one stream at the moment. + if (frame_types && frame_types->size() > 0) { + frame_type = (*frame_types)[0]; + } + // Image in vpx_image_t format. + // Input image is const. VPX's raw image is not defined as const. + raw_->planes[VPX_PLANE_Y] = const_cast<uint8_t*>(input_image.buffer(kYPlane)); + raw_->planes[VPX_PLANE_U] = const_cast<uint8_t*>(input_image.buffer(kUPlane)); + raw_->planes[VPX_PLANE_V] = const_cast<uint8_t*>(input_image.buffer(kVPlane)); + raw_->stride[VPX_PLANE_Y] = input_image.stride(kYPlane); + raw_->stride[VPX_PLANE_U] = input_image.stride(kUPlane); + raw_->stride[VPX_PLANE_V] = input_image.stride(kVPlane); + + int flags = 0; + bool send_keyframe = (frame_type == kKeyFrame); + if (send_keyframe) { + // Key frame request from caller. + flags = VPX_EFLAG_FORCE_KF; + } + assert(codec_.maxFramerate > 0); + uint32_t duration = 90000 / codec_.maxFramerate; + if (vpx_codec_encode(encoder_, raw_, timestamp_, duration, flags, + VPX_DL_REALTIME)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + timestamp_ += duration; + return GetEncodedPartitions(input_image); +} + +void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, + const vpx_codec_cx_pkt& pkt, + uint32_t timestamp) { + assert(codec_specific != NULL); + codec_specific->codecType = kVideoCodecVP9; + CodecSpecificInfoVP9 *vp9_info = &(codec_specific->codecSpecific.VP9); + vp9_info->pictureId = picture_id_; + vp9_info->keyIdx = kNoKeyIdx; + vp9_info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0; + // TODO(marpan): Temporal layers are supported in the current VP9 version, + // but for now use 1 temporal layer encoding. Will update this when temporal + // layer support for VP9 is added in webrtc. + vp9_info->temporalIdx = kNoTemporalIdx; + vp9_info->layerSync = false; + vp9_info->tl0PicIdx = kNoTl0PicIdx; + picture_id_ = (picture_id_ + 1) & 0x7FFF; +} + +int VP9EncoderImpl::GetEncodedPartitions(const I420VideoFrame& input_image) { + vpx_codec_iter_t iter = NULL; + encoded_image_._length = 0; + encoded_image_._frameType = kDeltaFrame; + RTPFragmentationHeader frag_info; + // Note: no data partitioning in VP9, so 1 partition only. We keep this + // fragmentation data for now, until VP9 packetizer is implemented. + frag_info.VerifyAndAllocateFragmentationHeader(1); + int part_idx = 0; + CodecSpecificInfo codec_specific; + const vpx_codec_cx_pkt_t *pkt = NULL; + while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) { + switch (pkt->kind) { + case VPX_CODEC_CX_FRAME_PKT: { + memcpy(&encoded_image_._buffer[encoded_image_._length], + pkt->data.frame.buf, + pkt->data.frame.sz); + frag_info.fragmentationOffset[part_idx] = encoded_image_._length; + frag_info.fragmentationLength[part_idx] = + static_cast<uint32_t>(pkt->data.frame.sz); + frag_info.fragmentationPlType[part_idx] = 0; + frag_info.fragmentationTimeDiff[part_idx] = 0; + encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz); + assert(encoded_image_._length <= encoded_image_._size); + break; + } + default: { + break; + } + } + // End of frame. + if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) { + // Check if encoded frame is a key frame. + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { + encoded_image_._frameType = kKeyFrame; + } + PopulateCodecSpecific(&codec_specific, *pkt, input_image.timestamp()); + break; + } + } + if (encoded_image_._length > 0) { + TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_._length); + encoded_image_._timeStamp = input_image.timestamp(); + encoded_image_.capture_time_ms_ = input_image.render_time_ms(); + encoded_image_._encodedHeight = raw_->d_h; + encoded_image_._encodedWidth = raw_->d_w; + encoded_complete_callback_->Encoded(encoded_image_, &codec_specific, + &frag_info); + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::SetChannelParameters(uint32_t packet_loss, int rtt) { + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9EncoderImpl::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + encoded_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +VP9Decoder* VP9Decoder::Create() { + return new VP9DecoderImpl(); +} + +VP9DecoderImpl::VP9DecoderImpl() + : decode_complete_callback_(NULL), + inited_(false), + decoder_(NULL), + key_frame_required_(true) { + memset(&codec_, 0, sizeof(codec_)); +} + +VP9DecoderImpl::~VP9DecoderImpl() { + inited_ = true; // in order to do the actual release + Release(); +} + +int VP9DecoderImpl::Reset() { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + InitDecode(&codec_, 1); + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::InitDecode(const VideoCodec* inst, int number_of_cores) { + if (inst == NULL) { + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + int ret_val = Release(); + if (ret_val < 0) { + return ret_val; + } + if (decoder_ == NULL) { + decoder_ = new vpx_dec_ctx_t; + } + vpx_codec_dec_cfg_t cfg; + // Setting number of threads to a constant value (1) + cfg.threads = 1; + cfg.h = cfg.w = 0; // set after decode + vpx_codec_flags_t flags = 0; + if (vpx_codec_dec_init(decoder_, vpx_codec_vp9_dx(), &cfg, flags)) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + if (&codec_ != inst) { + // Save VideoCodec instance for later; mainly for duplicating the decoder. + codec_ = *inst; + } + inited_ = true; + // Always start with a complete key frame. + key_frame_required_ = true; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::Decode(const EncodedImage& input_image, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t /*render_time_ms*/) { + if (!inited_) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (decode_complete_callback_ == NULL) { + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + // Always start with a complete key frame. + if (key_frame_required_) { + if (input_image._frameType != kKeyFrame) + return WEBRTC_VIDEO_CODEC_ERROR; + // We have a key frame - is it complete? + if (input_image._completeFrame) { + key_frame_required_ = false; + } else { + return WEBRTC_VIDEO_CODEC_ERROR; + } + } + vpx_codec_iter_t iter = NULL; + vpx_image_t* img; + uint8_t* buffer = input_image._buffer; + if (input_image._length == 0) { + buffer = NULL; // Triggers full frame concealment. + } + if (vpx_codec_decode(decoder_, + buffer, + input_image._length, + 0, + VPX_DL_REALTIME)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + img = vpx_codec_get_frame(decoder_, &iter); + int ret = ReturnFrame(img, input_image._timeStamp); + if (ret != 0) { + return ret; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::ReturnFrame(const vpx_image_t* img, uint32_t timestamp) { + if (img == NULL) { + // Decoder OK and NULL image => No show frame. + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + int half_height = (img->d_h + 1) / 2; + int size_y = img->stride[VPX_PLANE_Y] * img->d_h; + int size_u = img->stride[VPX_PLANE_U] * half_height; + int size_v = img->stride[VPX_PLANE_V] * half_height; + decoded_image_.CreateFrame(size_y, img->planes[VPX_PLANE_Y], + size_u, img->planes[VPX_PLANE_U], + size_v, img->planes[VPX_PLANE_V], + img->d_w, img->d_h, + img->stride[VPX_PLANE_Y], + img->stride[VPX_PLANE_U], + img->stride[VPX_PLANE_V]); + decoded_image_.set_timestamp(timestamp); + int ret = decode_complete_callback_->Decoded(decoded_image_); + if (ret != 0) + return ret; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) { + decode_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int VP9DecoderImpl::Release() { + if (decoder_ != NULL) { + if (vpx_codec_destroy(decoder_)) { + return WEBRTC_VIDEO_CODEC_MEMORY; + } + delete decoder_; + decoder_ = NULL; + } + inited_ = false; + return WEBRTC_VIDEO_CODEC_OK; +} +} // namespace webrtc diff --git a/modules/video_coding/codecs/vp9/vp9_impl.h b/modules/video_coding/codecs/vp9/vp9_impl.h new file mode 100644 index 00000000..94788db5 --- /dev/null +++ b/modules/video_coding/codecs/vp9/vp9_impl.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_ + +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" + +// VPX forward declaration +typedef struct vpx_codec_ctx vpx_codec_ctx_t; +typedef struct vpx_codec_ctx vpx_dec_ctx_t; +typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t; +typedef struct vpx_image vpx_image_t; +typedef struct vpx_ref_frame vpx_ref_frame_t; +struct vpx_codec_cx_pkt; + +namespace webrtc { + +class VP9EncoderImpl : public VP9Encoder { + public: + VP9EncoderImpl(); + + virtual ~VP9EncoderImpl(); + + virtual int Release() OVERRIDE; + + virtual int InitEncode(const VideoCodec* codec_settings, + int number_of_cores, + uint32_t max_payload_size) OVERRIDE; + + virtual int Encode(const I420VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector<VideoFrameType>* frame_types) OVERRIDE; + + virtual int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) + OVERRIDE; + + virtual int SetChannelParameters(uint32_t packet_loss, int rtt) OVERRIDE; + + virtual int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) OVERRIDE; + + private: + // Call encoder initialize function and set control settings. + int InitAndSetControlSettings(const VideoCodec* inst); + + void PopulateCodecSpecific(CodecSpecificInfo* codec_specific, + const vpx_codec_cx_pkt& pkt, + uint32_t timestamp); + + int GetEncodedPartitions(const I420VideoFrame& input_image); + + // Determine maximum target for Intra frames + // + // Input: + // - optimal_buffer_size : Optimal buffer size + // Return Value : Max target size for Intra frames represented as + // percentage of the per frame bandwidth + uint32_t MaxIntraTarget(uint32_t optimal_buffer_size); + + EncodedImage encoded_image_; + EncodedImageCallback* encoded_complete_callback_; + VideoCodec codec_; + bool inited_; + int64_t timestamp_; + uint16_t picture_id_; + int cpu_speed_; + uint32_t rc_max_intra_target_; + vpx_codec_ctx_t* encoder_; + vpx_codec_enc_cfg_t* config_; + vpx_image_t* raw_; +}; + + +class VP9DecoderImpl : public VP9Decoder { + public: + VP9DecoderImpl(); + + virtual ~VP9DecoderImpl(); + + virtual int InitDecode(const VideoCodec* inst, int number_of_cores) OVERRIDE; + + virtual int Decode(const EncodedImage& input_image, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t /*render_time_ms*/) OVERRIDE; + + virtual int RegisterDecodeCompleteCallback(DecodedImageCallback* callback) + OVERRIDE; + + virtual int Release() OVERRIDE; + + virtual int Reset() OVERRIDE; + + private: + int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp); + + I420VideoFrame decoded_image_; + DecodedImageCallback* decode_complete_callback_; + bool inited_; + vpx_dec_ctx_t* decoder_; + VideoCodec codec_; + bool key_frame_required_; +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_ diff --git a/modules/video_coding/main/interface/video_coding_defines.h b/modules/video_coding/main/interface/video_coding_defines.h index 0919c892..efdc41b4 100644 --- a/modules/video_coding/main/interface/video_coding_defines.h +++ b/modules/video_coding/main/interface/video_coding_defines.h @@ -39,6 +39,7 @@ namespace webrtc { #define VCM_RED_PAYLOAD_TYPE 96 #define VCM_ULPFEC_PAYLOAD_TYPE 97 #define VCM_VP8_PAYLOAD_TYPE 100 +#define VCM_VP9_PAYLOAD_TYPE 101 #define VCM_I420_PAYLOAD_TYPE 124 #define VCM_H264_PAYLOAD_TYPE 127 diff --git a/modules/video_coding/main/source/codec_database.cc b/modules/video_coding/main/source/codec_database.cc index e99cc528..2fc92461 100644 --- a/modules/video_coding/main/source/codec_database.cc +++ b/modules/video_coding/main/source/codec_database.cc @@ -19,6 +19,9 @@ #ifdef VIDEOCODEC_VP8 #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" #endif +#ifdef VIDEOCODEC_VP9 +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" +#endif #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/system_wrappers/interface/logging.h" @@ -39,6 +42,20 @@ VideoCodecVP8 VideoEncoder::GetDefaultVp8Settings() { return vp8_settings; } +VideoCodecVP9 VideoEncoder::GetDefaultVp9Settings() { + VideoCodecVP9 vp9_settings; + memset(&vp9_settings, 0, sizeof(vp9_settings)); + + vp9_settings.resilience = 1; + vp9_settings.numberOfTemporalLayers = 1; + vp9_settings.denoisingOn = false; + vp9_settings.frameDroppingOn = true; + vp9_settings.keyFrameInterval = 3000; + vp9_settings.adaptiveQpMode = true; + + return vp9_settings; +} + VideoCodecH264 VideoEncoder::GetDefaultH264Settings() { VideoCodecH264 h264_settings; memset(&h264_settings, 0, sizeof(h264_settings)); @@ -126,6 +143,24 @@ bool VCMCodecDataBase::Codec(int list_id, return true; } #endif +#ifdef VIDEOCODEC_VP9 + case VCM_VP9_IDX: { + strncpy(settings->plName, "VP9", 4); + settings->codecType = kVideoCodecVP9; + // 96 to 127 dynamic payload types for video codecs. + settings->plType = VCM_VP9_PAYLOAD_TYPE; + settings->startBitrate = 100; + settings->minBitrate = VCM_MIN_BITRATE; + settings->maxBitrate = 0; + settings->maxFramerate = VCM_DEFAULT_FRAME_RATE; + settings->width = VCM_DEFAULT_CODEC_WIDTH; + settings->height = VCM_DEFAULT_CODEC_HEIGHT; + settings->numberOfSimulcastStreams = 0; + settings->qpMax = 56; + settings->codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings(); + return true; + } +#endif #ifdef VIDEOCODEC_H264 case VCM_H264_IDX: { strncpy(settings->plName, "H264", 5); @@ -362,6 +397,13 @@ bool VCMCodecDataBase::RequiresEncoderReset(const VideoCodec& new_send_codec) { return true; } break; + case kVideoCodecVP9: + if (memcmp(&new_send_codec.codecSpecific.VP9, + &send_codec_.codecSpecific.VP9, + sizeof(new_send_codec.codecSpecific.VP9)) != 0) { + return true; + } + break; case kVideoCodecH264: if (memcmp(&new_send_codec.codecSpecific.H264, &send_codec_.codecSpecific.H264, @@ -635,6 +677,10 @@ VCMGenericEncoder* VCMCodecDataBase::CreateEncoder( case kVideoCodecVP8: return new VCMGenericEncoder(*(VP8Encoder::Create())); #endif +#ifdef VIDEOCODEC_VP9 + case kVideoCodecVP9: + return new VCMGenericEncoder(*(VP9Encoder::Create())); +#endif #ifdef VIDEOCODEC_I420 case kVideoCodecI420: return new VCMGenericEncoder(*(new I420Encoder)); @@ -662,6 +708,10 @@ VCMGenericDecoder* VCMCodecDataBase::CreateDecoder(VideoCodecType type) const { case kVideoCodecVP8: return new VCMGenericDecoder(*(VP8Decoder::Create())); #endif +#ifdef VIDEOCODEC_VP9 + case kVideoCodecVP9: + return new VCMGenericDecoder(*(VP9Decoder::Create())); +#endif #ifdef VIDEOCODEC_I420 case kVideoCodecI420: return new VCMGenericDecoder(*(new I420Decoder)); diff --git a/modules/video_coding/main/source/internal_defines.h b/modules/video_coding/main/source/internal_defines.h index ef42c628..adc940f2 100644 --- a/modules/video_coding/main/source/internal_defines.h +++ b/modules/video_coding/main/source/internal_defines.h @@ -39,10 +39,15 @@ inline uint32_t MaskWord64ToUWord32(int64_t w64) #else #define VCM_VP8_IDX VCM_NO_CODEC_IDX #endif +#ifdef VIDEOCODEC_VP9 + #define VCM_VP9_IDX (VCM_VP8_IDX + 1) +#else + #define VCM_VP9_IDX VCM_VP8_IDX +#endif #ifdef VIDEOCODEC_H264 - #define VCM_H264_IDX (VCM_VP8_IDX + 1) + #define VCM_H264_IDX (VCM_VP9_IDX + 1) #else - #define VCM_H264_IDX VCM_VP8_IDX + #define VCM_H264_IDX VCM_VP9_IDX #endif #ifdef VIDEOCODEC_I420 #define VCM_I420_IDX (VCM_H264_IDX + 1) diff --git a/modules/video_coding/main/source/video_coding.gypi b/modules/video_coding/main/source/video_coding.gypi index f19a5855..02c5a5c0 100644 --- a/modules/video_coding/main/source/video_coding.gypi +++ b/modules/video_coding/main/source/video_coding.gypi @@ -17,6 +17,7 @@ '<(webrtc_root)/modules/video_coding/utility/video_coding_utility.gyp:video_coding_utility', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_vp8_dir)/vp8.gyp:webrtc_vp8', + '<(webrtc_vp9_dir)/vp9.gyp:webrtc_vp9', ], 'sources': [ # interfaces diff --git a/modules/video_coding/main/test/normal_test.cc b/modules/video_coding/main/test/normal_test.cc index f23682bd..815c3acc 100644 --- a/modules/video_coding/main/test/normal_test.cc +++ b/modules/video_coding/main/test/normal_test.cc @@ -101,6 +101,9 @@ VCMNTEncodeCompleteCallback::SendData( rtpInfo.type.Video.codecHeader.VP8.pictureId = videoHdr->codecHeader.VP8.pictureId; break; + case kVideoCodecVP9: + // Leave for now, until we add kRtpVideoVp9 to RTP. + break; default: assert(false); return -1; diff --git a/modules/video_coding/main/test/test_callbacks.cc b/modules/video_coding/main/test/test_callbacks.cc index 710a06ea..d68f9949 100644 --- a/modules/video_coding/main/test/test_callbacks.cc +++ b/modules/video_coding/main/test/test_callbacks.cc @@ -82,6 +82,9 @@ VCMEncodeCompleteCallback::SendData( rtpInfo.type.Video.codecHeader.VP8.pictureId = videoHdr->codecHeader.VP8.pictureId; break; + case webrtc::kRtpVideoGeneric: + // Leave for now, until we add kRtpVideoVp9 to RTP. + break; default: assert(false); return -1; diff --git a/modules/video_coding/main/test/test_util.cc b/modules/video_coding/main/test/test_util.cc index 09ad9916..d2b8f8c7 100644 --- a/modules/video_coding/main/test/test_util.cc +++ b/modules/video_coding/main/test/test_util.cc @@ -151,6 +151,7 @@ webrtc::RtpVideoCodecTypes ConvertCodecType(const char* plname) { if (strncmp(plname,"VP8" , 3) == 0) { return webrtc::kRtpVideoVp8; } else { - return webrtc::kRtpVideoNone; // Default value + // Default value. + return webrtc::kRtpVideoGeneric; } } diff --git a/modules/video_coding/main/test/tester_main.cc b/modules/video_coding/main/test/tester_main.cc index bf17ab27..874fa9e7 100644 --- a/modules/video_coding/main/test/tester_main.cc +++ b/modules/video_coding/main/test/tester_main.cc @@ -63,6 +63,8 @@ int ParseArguments(CmdArgs& args) { args.codecName = FLAGS_codec; if (args.codecName == "VP8") { args.codecType = kVideoCodecVP8; + } else if (args.codecName == "VP9") { + args.codecType = kVideoCodecVP9; } else if (args.codecName == "I420") { args.codecType = kVideoCodecI420; } else { diff --git a/video/call.cc b/video/call.cc index ff70fda5..fd41d75f 100644 --- a/video/call.cc +++ b/video/call.cc @@ -20,6 +20,7 @@ #include "webrtc/config.h" #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" #include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -46,6 +47,8 @@ VideoEncoder* VideoEncoder::Create(VideoEncoder::EncoderType codec_type) { switch (codec_type) { case kVp8: return VP8Encoder::Create(); + case kVp9: + return VP9Encoder::Create(); } assert(false); return NULL; diff --git a/video/end_to_end_tests.cc b/video/end_to_end_tests.cc index f9a11e44..06cb187c 100644 --- a/video/end_to_end_tests.cc +++ b/video/end_to_end_tests.cc @@ -19,6 +19,9 @@ #include "webrtc/call.h" #include "webrtc/frame_callback.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" +#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" +#include "webrtc/modules/video_coding/main/interface/video_coding_defines.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/event_wrapper.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" @@ -222,6 +225,57 @@ TEST_F(EndToEndTest, TransmitsFirstFrame) { DestroyStreams(); } +// TODO(marpan): Re-enable this test on the next libvpx roll. +TEST_F(EndToEndTest, DISABLED_SendsAndReceivesVP9) { + class VP9Observer : public test::EndToEndTest, public VideoRenderer { + public: + VP9Observer() + : EndToEndTest(2 * kDefaultTimeoutMs), + encoder_(VideoEncoder::Create(VideoEncoder::kVp9)), + decoder_(VP9Decoder::Create()), + frame_counter_(0) {} + + virtual void PerformTest() OVERRIDE { + EXPECT_EQ(kEventSignaled, Wait()) + << "Timed out while waiting for enough frames to be decoded."; + } + + virtual void ModifyConfigs( + VideoSendStream::Config* send_config, + std::vector<VideoReceiveStream::Config>* receive_configs, + VideoEncoderConfig* encoder_config) OVERRIDE { + send_config->encoder_settings.encoder = encoder_.get(); + send_config->encoder_settings.payload_name = "VP9"; + send_config->encoder_settings.payload_type = VCM_VP9_PAYLOAD_TYPE; + encoder_config->streams[0].min_bitrate_bps = 50000; + encoder_config->streams[0].target_bitrate_bps = + encoder_config->streams[0].max_bitrate_bps = 2000000; + + (*receive_configs)[0].renderer = this; + (*receive_configs)[0].decoders.resize(1); + (*receive_configs)[0].decoders[0].payload_type = + send_config->encoder_settings.payload_type; + (*receive_configs)[0].decoders[0].payload_name = + send_config->encoder_settings.payload_name; + (*receive_configs)[0].decoders[0].decoder = decoder_.get(); + } + + virtual void RenderFrame(const I420VideoFrame& video_frame, + int time_to_render_ms) OVERRIDE { + const int kRequiredFrames = 500; + if (++frame_counter_ == kRequiredFrames) + observation_complete_->Set(); + } + + private: + scoped_ptr<webrtc::VideoEncoder> encoder_; + scoped_ptr<webrtc::VideoDecoder> decoder_; + int frame_counter_; + } test; + + RunBaseTest(&test); +} + TEST_F(EndToEndTest, SendsAndReceivesH264) { class H264Observer : public test::EndToEndTest, public VideoRenderer { public: diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc index d6ebc2b8..28231b00 100644 --- a/video/video_send_stream.cc +++ b/video/video_send_stream.cc @@ -301,6 +301,8 @@ bool VideoSendStream::ReconfigureVideoEncoder( memset(&video_codec, 0, sizeof(video_codec)); if (config_.encoder_settings.payload_name == "VP8") { video_codec.codecType = kVideoCodecVP8; + } else if (config_.encoder_settings.payload_name == "VP9") { + video_codec.codecType = kVideoCodecVP9; } else if (config_.encoder_settings.payload_name == "H264") { video_codec.codecType = kVideoCodecH264; } else { @@ -323,6 +325,8 @@ bool VideoSendStream::ReconfigureVideoEncoder( if (video_codec.codecType == kVideoCodecVP8) { video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings(); + } else if (video_codec.codecType == kVideoCodecVP9) { + video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings(); } else if (video_codec.codecType == kVideoCodecH264) { video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings(); } diff --git a/video_encoder.h b/video_encoder.h index cbdf1ef0..2bf52f3c 100644 --- a/video_encoder.h +++ b/video_encoder.h @@ -40,28 +40,84 @@ class VideoEncoder { public: enum EncoderType { kVp8, + kVp9, }; static VideoEncoder* Create(EncoderType codec_type); static VideoCodecVP8 GetDefaultVp8Settings(); + static VideoCodecVP9 GetDefaultVp9Settings(); static VideoCodecH264 GetDefaultH264Settings(); virtual ~VideoEncoder() {} + // Initialize the encoder with the information from the codecSettings + // + // Input: + // - codec_settings : Codec settings + // - number_of_cores : Number of cores available for the encoder + // - max_payload_size : The maximum size each payload is allowed + // to have. Usually MTU - overhead. + // + // Return value : Set bit rate if OK + // <0 - Errors: + // WEBRTC_VIDEO_CODEC_ERR_PARAMETER + // WEBRTC_VIDEO_CODEC_ERR_SIZE + // WEBRTC_VIDEO_CODEC_LEVEL_EXCEEDED + // WEBRTC_VIDEO_CODEC_MEMORY + // WEBRTC_VIDEO_CODEC_ERROR virtual int32_t InitEncode(const VideoCodec* codec_settings, int32_t number_of_cores, uint32_t max_payload_size) = 0; + + // Register an encode complete callback object. + // + // Input: + // - callback : Callback object which handles encoded images. + // + // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int32_t RegisterEncodeCompleteCallback( EncodedImageCallback* callback) = 0; - virtual int32_t Release() = 0; + // Free encoder memory. + // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. + virtual int32_t Release() = 0; + // Encode an I420 image (as a part of a video stream). The encoded image + // will be returned to the user through the encode complete callback. + // + // Input: + // - frame : Image to be encoded + // - frame_types : Frame type to be generated by the encoder. + // + // Return value : WEBRTC_VIDEO_CODEC_OK if OK + // <0 - Errors: + // WEBRTC_VIDEO_CODEC_ERR_PARAMETER + // WEBRTC_VIDEO_CODEC_MEMORY + // WEBRTC_VIDEO_CODEC_ERROR + // WEBRTC_VIDEO_CODEC_TIMEOUT virtual int32_t Encode(const I420VideoFrame& frame, const CodecSpecificInfo* codec_specific_info, const std::vector<VideoFrameType>* frame_types) = 0; + // Inform the encoder of the new packet loss rate and the round-trip time of + // the network. + // + // Input: + // - packet_loss : Fraction lost + // (loss rate in percent = 100 * packetLoss / 255) + // - rtt : Round-trip time in milliseconds + // Return value : WEBRTC_VIDEO_CODEC_OK if OK + // <0 - Errors: WEBRTC_VIDEO_CODEC_ERROR virtual int32_t SetChannelParameters(uint32_t packet_loss, int rtt) = 0; + + // Inform the encoder about the new target bit rate. + // + // Input: + // - bitrate : New target bit rate + // - framerate : The target frame rate + // + // Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise. virtual int32_t SetRates(uint32_t bitrate, uint32_t framerate) = 0; virtual int32_t SetPeriodicKeyFrames(bool enable) { return -1; } diff --git a/video_engine/test/auto_test/source/vie_autotest.cc b/video_engine/test/auto_test/source/vie_autotest.cc index fb1a46f2..41fafaef 100644 --- a/video_engine/test/auto_test/source/vie_autotest.cc +++ b/video_engine/test/auto_test/source/vie_autotest.cc @@ -98,6 +98,9 @@ void ViEAutoTest::PrintVideoCodec(const webrtc::VideoCodec videoCodec) case webrtc::kVideoCodecVP8: ViETest::Log("\tcodecType: VP8"); break; + case webrtc::kVideoCodecVP9: + ViETest::Log("\tcodecType: VP9"); + break; case webrtc::kVideoCodecI420: ViETest::Log("\tcodecType: I420"); break; diff --git a/video_engine/vie_codec_impl.cc b/video_engine/vie_codec_impl.cc index f939a667..0ef039db 100644 --- a/video_engine/vie_codec_impl.cc +++ b/video_engine/vie_codec_impl.cc @@ -640,6 +640,8 @@ bool ViECodecImpl::CodecValid(const VideoCodec& video_codec) { return false; } else if ((video_codec.codecType == kVideoCodecVP8 && strncmp(video_codec.plName, "VP8", 4) == 0) || + (video_codec.codecType == kVideoCodecVP9 && + strncmp(video_codec.plName, "VP9", 4) == 0) || (video_codec.codecType == kVideoCodecI420 && strncmp(video_codec.plName, "I420", 4) == 0) || (video_codec.codecType == kVideoCodecH264 && @@ -36,6 +36,7 @@ 'video_engine/video_engine.gyp:*', 'voice_engine/voice_engine.gyp:*', '<(webrtc_vp8_dir)/vp8.gyp:*', + '<(webrtc_vp9_dir)/vp9.gyp:*', ], }, 'targets': [ |