Adding test for SingleNalUnit mode
Test enables single-nalu mode, sets limit for nalu lenght and verifies that encoder follows that limit. I found that QP jumps significantly when the mode is enabled. In result encoder might produce 4kbyte and 0.4kbyte frames back-to-back. But it seems that happens only to couple of frames in the beginning. This caused test to fail with default RC thresholds. To bypass this I increased frame size mismatch threshold from 20 to 30%. This should be Ok considering single-nalu mode is rare. BUG=webrtc:8070 Review-Url: https://codereview.webrtc.org/3014623002 Cr-Commit-Position: refs/heads/master@{#20023}
This commit is contained in:
@ -32,6 +32,9 @@ struct FrameStatistic {
|
||||
size_t encoded_frame_size_bytes = 0;
|
||||
webrtc::FrameType frame_type = kVideoFrameDelta;
|
||||
|
||||
// H264 specific.
|
||||
rtc::Optional<size_t> max_nalu_length;
|
||||
|
||||
// Decoding.
|
||||
int64_t decode_start_ns = 0;
|
||||
int decode_return_code = 0;
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@ -19,6 +20,7 @@
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "common_types.h" // NOLINT(build/include)
|
||||
#include "common_video/h264/h264_common.h"
|
||||
#include "modules/video_coding/codecs/vp8/simulcast_rate_allocator.h"
|
||||
#include "modules/video_coding/include/video_codec_initializer.h"
|
||||
#include "modules/video_coding/utility/default_video_bitrate_allocator.h"
|
||||
@ -108,6 +110,24 @@ void VerifyQpParser(const EncodedImage& encoded_frame,
|
||||
EXPECT_EQ(encoded_frame.qp_, qp) << "Encoder QP != parsed bitstream QP.";
|
||||
}
|
||||
|
||||
rtc::Optional<size_t> GetMaxNaluLength(const EncodedImage& encoded_frame,
|
||||
const TestConfig& config) {
|
||||
if (config.codec_settings.codecType != kVideoCodecH264)
|
||||
return rtc::Optional<size_t>();
|
||||
|
||||
std::vector<webrtc::H264::NaluIndex> nalu_indices =
|
||||
webrtc::H264::FindNaluIndices(encoded_frame._buffer,
|
||||
encoded_frame._length);
|
||||
|
||||
RTC_CHECK(!nalu_indices.empty());
|
||||
|
||||
size_t max_length = 0;
|
||||
for (const webrtc::H264::NaluIndex& index : nalu_indices)
|
||||
max_length = std::max(max_length, index.payload_size);
|
||||
|
||||
return rtc::Optional<size_t>(max_length);
|
||||
}
|
||||
|
||||
int GetElapsedTimeMicroseconds(int64_t start_ns, int64_t stop_ns) {
|
||||
int64_t diff_us = (stop_ns - start_ns) / rtc::kNumNanosecsPerMicrosec;
|
||||
RTC_DCHECK_GE(diff_us, std::numeric_limits<int>::min());
|
||||
@ -351,6 +371,8 @@ void VideoProcessor::FrameEncoded(webrtc::VideoCodecType codec,
|
||||
encoded_image._length / config_.networking_config.packet_size_in_bytes +
|
||||
1;
|
||||
|
||||
frame_stat->max_nalu_length = GetMaxNaluLength(encoded_image, config_);
|
||||
|
||||
// Simulate packet loss.
|
||||
bool exclude_this_frame = false;
|
||||
if (encoded_image._frameType == kVideoFrameKey) {
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#include "api/video/video_frame.h"
|
||||
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
|
||||
#include "modules/video_coding/codecs/test/packet_manipulator.h"
|
||||
#include "modules/video_coding/codecs/test/stats.h"
|
||||
#include "modules/video_coding/include/video_codec_interface.h"
|
||||
@ -100,6 +101,10 @@ struct TestConfig {
|
||||
// Should the hardware codecs be wrapped in software fallbacks?
|
||||
bool sw_fallback_encoder = false;
|
||||
bool sw_fallback_decoder = false;
|
||||
|
||||
// RTP H264 packetization mode.
|
||||
H264PacketizationMode packetization_mode =
|
||||
H264PacketizationMode::NonInterleaved;
|
||||
};
|
||||
|
||||
// Handles encoding/decoding of video using the VideoEncoder/VideoDecoder
|
||||
|
||||
@ -176,6 +176,7 @@ void VideoProcessorIntegrationTest::ProcessFramesAndMaybeVerify(
|
||||
const RateProfile& rate_profile,
|
||||
const std::vector<RateControlThresholds>* rc_thresholds,
|
||||
const QualityThresholds* quality_thresholds,
|
||||
const BitstreamThresholds* bs_thresholds,
|
||||
const VisualizationParams* visualization_params) {
|
||||
// The Android HW codec needs to be run on a task queue, so we simply always
|
||||
// run the test on a task queue.
|
||||
@ -246,6 +247,10 @@ void VideoProcessorIntegrationTest::ProcessFramesAndMaybeVerify(
|
||||
while (frame_number < num_frames) {
|
||||
UpdateRateControlMetrics(frame_number);
|
||||
|
||||
if (bs_thresholds) {
|
||||
VerifyBitstream(frame_number, *bs_thresholds);
|
||||
}
|
||||
|
||||
++frame_number;
|
||||
|
||||
if (frame_number ==
|
||||
@ -336,6 +341,13 @@ void VideoProcessorIntegrationTest::CreateEncoderAndDecoder() {
|
||||
case kVideoCodecH264:
|
||||
// TODO(brandtr): Generalize so that we support multiple profiles here.
|
||||
codec = cricket::VideoCodec(cricket::kH264CodecName);
|
||||
if (config_.packetization_mode == H264PacketizationMode::NonInterleaved) {
|
||||
codec.SetParam(cricket::kH264FmtpPacketizationMode, "1");
|
||||
} else {
|
||||
RTC_CHECK_EQ(config_.packetization_mode,
|
||||
H264PacketizationMode::SingleNalUnit);
|
||||
codec.SetParam(cricket::kH264FmtpPacketizationMode, "0");
|
||||
}
|
||||
encoder_.reset(encoder_factory->CreateVideoEncoder(codec));
|
||||
decoder_.reset(
|
||||
decoder_factory->CreateVideoDecoderWithParams(codec, decoder_params));
|
||||
@ -562,6 +574,14 @@ void VideoProcessorIntegrationTest::PrintRateControlMetrics(
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void VideoProcessorIntegrationTest::VerifyBitstream(
|
||||
int frame_number,
|
||||
const BitstreamThresholds& bs_thresholds) {
|
||||
RTC_CHECK_GE(frame_number, 0);
|
||||
const FrameStatistic* frame_stat = stats_.GetFrame(frame_number);
|
||||
EXPECT_LE(*(frame_stat->max_nalu_length), bs_thresholds.max_nalu_length);
|
||||
}
|
||||
|
||||
// Temporal layer index corresponding to frame number, for up to 3 layers.
|
||||
int VideoProcessorIntegrationTest::TemporalLayerIndexForFrame(
|
||||
int frame_number) const {
|
||||
|
||||
@ -71,6 +71,12 @@ struct QualityThresholds {
|
||||
double min_min_ssim;
|
||||
};
|
||||
|
||||
struct BitstreamThresholds {
|
||||
explicit BitstreamThresholds(size_t max_nalu_length)
|
||||
: max_nalu_length(max_nalu_length) {}
|
||||
size_t max_nalu_length;
|
||||
};
|
||||
|
||||
// Should video files be saved persistently to disk for post-run visualization?
|
||||
struct VisualizationParams {
|
||||
bool save_encoded_ivf;
|
||||
@ -122,6 +128,7 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
||||
const RateProfile& rate_profile,
|
||||
const std::vector<RateControlThresholds>* rc_thresholds,
|
||||
const QualityThresholds* quality_thresholds,
|
||||
const BitstreamThresholds* bs_thresholds,
|
||||
const VisualizationParams* visualization_params);
|
||||
|
||||
// Config.
|
||||
@ -192,6 +199,9 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
||||
const std::vector<int>& num_dropped_frames,
|
||||
const std::vector<int>& num_spatial_resizes) const;
|
||||
|
||||
void VerifyBitstream(int frame_number,
|
||||
const BitstreamThresholds& bs_thresholds);
|
||||
|
||||
// Codecs.
|
||||
std::unique_ptr<VideoEncoder> encoder_;
|
||||
std::unique_ptr<VideoDecoder> decoder_;
|
||||
|
||||
@ -70,7 +70,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, Process0PercentPacketLossVP9) {
|
||||
QualityThresholds quality_thresholds(37.0, 36.0, 0.93, 0.92);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP9: Run with 5% packet loss and fixed bitrate. Quality should be a bit
|
||||
@ -91,7 +91,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, Process5PercentPacketLossVP9) {
|
||||
QualityThresholds quality_thresholds(17.0, 14.0, 0.45, 0.36);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP9: Run with no packet loss, with varying bitrate (3 rate updates):
|
||||
@ -117,7 +117,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, ProcessNoLossChangeBitRateVP9) {
|
||||
QualityThresholds quality_thresholds(35.5, 30.0, 0.90, 0.85);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP9: Run with no packet loss, with an update (decrease) in frame rate.
|
||||
@ -147,7 +147,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx,
|
||||
QualityThresholds quality_thresholds(31.5, 18.0, 0.80, 0.43);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP9: Run with no packet loss and denoiser on. One key frame (first frame).
|
||||
@ -166,7 +166,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, ProcessNoLossDenoiserOnVP9) {
|
||||
QualityThresholds quality_thresholds(36.8, 35.8, 0.92, 0.91);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// Run with no packet loss, at low bitrate.
|
||||
@ -188,7 +188,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx,
|
||||
QualityThresholds quality_thresholds(24.0, 13.0, 0.65, 0.37);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// TODO(marpan): Add temporal layer test for VP9, once changes are in
|
||||
@ -214,7 +214,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, ProcessZeroPacketLoss) {
|
||||
QualityThresholds quality_thresholds(34.95, 33.0, 0.90, 0.89);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP8: Run with 5% packet loss and fixed bitrate. Quality should be a bit
|
||||
@ -235,7 +235,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, Process5PercentPacketLoss) {
|
||||
QualityThresholds quality_thresholds(20.0, 16.0, 0.60, 0.40);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP8: Run with 10% packet loss and fixed bitrate. Quality should be lower.
|
||||
@ -256,7 +256,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx, Process10PercentPacketLoss) {
|
||||
QualityThresholds quality_thresholds(19.0, 16.0, 0.50, 0.35);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
#endif // !defined(WEBRTC_IOS)
|
||||
@ -301,7 +301,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx,
|
||||
QualityThresholds quality_thresholds(34.0, 32.0, 0.85, 0.80);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP8: Run with no packet loss, with an update (decrease) in frame rate.
|
||||
@ -339,7 +339,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx,
|
||||
QualityThresholds quality_thresholds(31.0, 22.0, 0.80, 0.65);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// VP8: Run with no packet loss, with 3 temporal layers, with a rate update in
|
||||
@ -372,7 +372,7 @@ TEST_F(VideoProcessorIntegrationTestLibvpx,
|
||||
QualityThresholds quality_thresholds(32.5, 30.0, 0.85, 0.80);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@ -57,7 +57,7 @@ TEST_F(VideoProcessorIntegrationTestMediaCodec, ForemanCif500kbpsVp8) {
|
||||
QualityThresholds quality_thresholds(30.0, 14.0, 0.86, 0.39);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
TEST_F(VideoProcessorIntegrationTestMediaCodec,
|
||||
@ -89,7 +89,7 @@ TEST_F(VideoProcessorIntegrationTestMediaCodec,
|
||||
QualityThresholds quality_thresholds(33.0, 30.0, 0.90, 0.85);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
#endif // defined(WEBRTC_ANDROID)
|
||||
|
||||
@ -53,7 +53,7 @@ class VideoProcessorIntegrationTestOpenH264
|
||||
// these unittests appears to drop "packets" in a way that is not compatible
|
||||
// with H264. Therefore ProcessXPercentPacketLossH264, X != 0, unittests have
|
||||
// not been added.
|
||||
TEST_F(VideoProcessorIntegrationTestOpenH264, Process0PercentPacketLossH264) {
|
||||
TEST_F(VideoProcessorIntegrationTestOpenH264, Process0PercentPacketLoss) {
|
||||
SetCodecSettings(&config_, kVideoCodecH264, 1, false, false, true, false,
|
||||
kResilienceOn, kCifWidth, kCifHeight);
|
||||
|
||||
@ -68,7 +68,32 @@ TEST_F(VideoProcessorIntegrationTestOpenH264, Process0PercentPacketLossH264) {
|
||||
QualityThresholds quality_thresholds(35.0, 25.0, 0.93, 0.70);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
kNoVisualizationParams);
|
||||
nullptr, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
// H264: Enable SingleNalUnit packetization mode. Encoder should split
|
||||
// large frames into multiple slices and limit length of NAL units.
|
||||
TEST_F(VideoProcessorIntegrationTestOpenH264, ProcessNoLossSingleNalUnit) {
|
||||
config_.packetization_mode = H264PacketizationMode::SingleNalUnit;
|
||||
config_.networking_config.max_payload_size_in_bytes = 500;
|
||||
SetCodecSettings(&config_, kVideoCodecH264, 1, false, false, true, false,
|
||||
kResilienceOn, kCifWidth, kCifHeight);
|
||||
|
||||
RateProfile rate_profile;
|
||||
SetRateProfile(&rate_profile, 0, 500, 30, 0);
|
||||
rate_profile.frame_index_rate_update[1] = kNumFrames + 1;
|
||||
rate_profile.num_frames = kNumFrames;
|
||||
|
||||
std::vector<RateControlThresholds> rc_thresholds;
|
||||
AddRateControlThresholds(2, 60, 30, 10, 20, 0, 1, &rc_thresholds);
|
||||
|
||||
QualityThresholds quality_thresholds(35.0, 25.0, 0.93, 0.70);
|
||||
|
||||
BitstreamThresholds bs_thresholds(
|
||||
config_.networking_config.max_payload_size_in_bytes);
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
|
||||
&bs_thresholds, kNoVisualizationParams);
|
||||
}
|
||||
|
||||
#endif // defined(WEBRTC_USE_H264)
|
||||
|
||||
@ -77,7 +77,7 @@ class VideoProcessorIntegrationTestParameterized
|
||||
rate_profile.frame_index_rate_update[1] = kNumFrames + 1;
|
||||
rate_profile.num_frames = kNumFrames;
|
||||
|
||||
ProcessFramesAndMaybeVerify(rate_profile, nullptr, nullptr,
|
||||
ProcessFramesAndMaybeVerify(rate_profile, nullptr, nullptr, nullptr,
|
||||
&kVisualizationParams);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user