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}
diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn
index 2371d08..785533a 100644
--- a/modules/video_coding/BUILD.gn
+++ b/modules/video_coding/BUILD.gn
@@ -377,6 +377,7 @@
     }
 
     deps = [
+      ":codec_globals_headers",
       ":video_coding",
       ":video_coding_utility",
       ":webrtc_vp8",
diff --git a/modules/video_coding/codecs/test/stats.h b/modules/video_coding/codecs/test/stats.h
index 8022f27..fd6fa87 100644
--- a/modules/video_coding/codecs/test/stats.h
+++ b/modules/video_coding/codecs/test/stats.h
@@ -32,6 +32,9 @@
   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;
diff --git a/modules/video_coding/codecs/test/videoprocessor.cc b/modules/video_coding/codecs/test/videoprocessor.cc
index af5b4b3..cc453fa 100644
--- a/modules/video_coding/codecs/test/videoprocessor.cc
+++ b/modules/video_coding/codecs/test/videoprocessor.cc
@@ -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 @@
   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 @@
       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) {
diff --git a/modules/video_coding/codecs/test/videoprocessor.h b/modules/video_coding/codecs/test/videoprocessor.h
index 98b9140..cf96923 100644
--- a/modules/video_coding/codecs/test/videoprocessor.h
+++ b/modules/video_coding/codecs/test/videoprocessor.h
@@ -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 @@
   // 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
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc
index 99ecb10..7d9a81a 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest.cc
@@ -176,6 +176,7 @@
     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 @@
   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 @@
     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 @@
   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 {
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest.h b/modules/video_coding/codecs/test/videoprocessor_integrationtest.h
index 9831e48..d383915 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest.h
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest.h
@@ -71,6 +71,12 @@
   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 @@
       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 @@
       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_;
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc
index 9f3c1d3..3e43a76 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest_libvpx.cc
@@ -70,7 +70,7 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   QualityThresholds quality_thresholds(32.5, 30.0, 0.85, 0.80);
 
   ProcessFramesAndMaybeVerify(rate_profile, &rc_thresholds, &quality_thresholds,
-                              kNoVisualizationParams);
+                              nullptr, kNoVisualizationParams);
 }
 
 }  // namespace test
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest_mediacodec.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest_mediacodec.cc
index 293ac54..6e20244 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest_mediacodec.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest_mediacodec.cc
@@ -57,7 +57,7 @@
   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 @@
   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)
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest_openh264.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest_openh264.cc
index e6db027..df3e4d0 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest_openh264.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest_openh264.cc
@@ -53,7 +53,7 @@
 // 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 @@
   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)
diff --git a/modules/video_coding/codecs/test/videoprocessor_integrationtest_parameterized.cc b/modules/video_coding/codecs/test/videoprocessor_integrationtest_parameterized.cc
index 4bd5fec..8a182f9 100644
--- a/modules/video_coding/codecs/test/videoprocessor_integrationtest_parameterized.cc
+++ b/modules/video_coding/codecs/test/videoprocessor_integrationtest_parameterized.cc
@@ -77,7 +77,7 @@
     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);
   }