PictureIdTest: Add tests for temporal layer 0 picture index, tl0_pic_idx.

Bug: none
Change-Id: I98215b705161ff601f9df5104acd5c5c531e1008
Reviewed-on: https://webrtc-review.googlesource.com/27981
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21030}
diff --git a/video/picture_id_tests.cc b/video/picture_id_tests.cc
index 1aced65..ee46355 100644
--- a/video/picture_id_tests.cc
+++ b/video/picture_id_tests.cc
@@ -25,6 +25,7 @@
 const int kMinPacketsToObserve = 10;
 const int kEncoderBitrateBps = 100000;
 const uint32_t kPictureIdWraparound = (1 << 15);
+const size_t kNumTemporalLayers[] = {1, 2, 3};
 
 const char kVp8ForcedFallbackEncoderEnabled[] =
     "WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled/";
@@ -48,6 +49,7 @@
       : test::RtpRtcpObserver(test::CallTest::kDefaultTimeoutMs),
         codec_type_(codec_type),
         max_expected_picture_id_gap_(0),
+        max_expected_tl0_idx_gap_(0),
         num_ssrcs_to_observe_(1) {}
 
   void SetExpectedSsrcs(size_t num_expected_ssrcs) {
@@ -66,20 +68,23 @@
   void SetMaxExpectedPictureIdGap(int max_expected_picture_id_gap) {
     rtc::CritScope lock(&crit_);
     max_expected_picture_id_gap_ = max_expected_picture_id_gap;
+    // Expect smaller gap for |tl0_pic_idx| (running index for temporal_idx 0).
+    max_expected_tl0_idx_gap_ = max_expected_picture_id_gap_ / 2;
   }
 
  private:
-  // TODO(asapersson): Add test for tl0_pic_idx.
   struct ParsedPacket {
     uint32_t timestamp;
     uint32_t ssrc;
-    uint16_t picture_id;
+    int16_t picture_id;
+    int16_t tl0_pic_idx;
+    uint8_t temporal_idx;
     FrameType frame_type;
   };
 
   bool ParsePayload(const uint8_t* packet,
                     size_t length,
-                    ParsedPacket* parsed) {
+                    ParsedPacket* parsed) const {
     RTPHeader header;
     EXPECT_TRUE(parser_->Parse(packet, length, &header));
     EXPECT_TRUE(header.ssrc == test::CallTest::kVideoSendSsrcs[0] ||
@@ -93,8 +98,8 @@
       return false;  // Padding packet.
     }
 
-    parsed->ssrc = header.ssrc;
     parsed->timestamp = header.timestamp;
+    parsed->ssrc = header.ssrc;
 
     std::unique_ptr<RtpDepacketizer> depacketizer(
         RtpDepacketizer::Create(codec_type_));
@@ -106,10 +111,18 @@
       case kRtpVideoVp8:
         parsed->picture_id =
             parsed_payload.type.Video.codecHeader.VP8.pictureId;
+        parsed->tl0_pic_idx =
+            parsed_payload.type.Video.codecHeader.VP8.tl0PicIdx;
+        parsed->temporal_idx =
+            parsed_payload.type.Video.codecHeader.VP8.temporalIdx;
         break;
       case kRtpVideoVp9:
         parsed->picture_id =
             parsed_payload.type.Video.codecHeader.VP9.picture_id;
+        parsed->tl0_pic_idx =
+            parsed_payload.type.Video.codecHeader.VP9.tl0_pic_idx;
+        parsed->temporal_idx =
+            parsed_payload.type.Video.codecHeader.VP9.temporal_idx;
         break;
       default:
         RTC_NOTREACHED();
@@ -120,43 +133,69 @@
     return true;
   }
 
+  // Verify continuity and monotonicity of picture_id sequence.
+  void VerifyPictureId(const ParsedPacket& current,
+                       const ParsedPacket& last) const
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
+    if (current.timestamp == last.timestamp) {
+      EXPECT_EQ(last.picture_id, current.picture_id);
+      return;  // Same frame.
+    }
+
+    // Packet belongs to a new frame.
+    // Picture id should be increasing.
+    EXPECT_TRUE((AheadOf<uint16_t, kPictureIdWraparound>(current.picture_id,
+                                                         last.picture_id)));
+
+    // Expect continuously increasing picture id.
+    int diff = ForwardDiff<uint16_t, kPictureIdWraparound>(last.picture_id,
+                                                           current.picture_id);
+    if (diff > 1) {
+      // If the VideoSendStream is destroyed, any frames still in queue is lost.
+      // Gaps only possible for first frame after a recreation, i.e. key frames.
+      EXPECT_EQ(kVideoFrameKey, current.frame_type);
+      EXPECT_LE(diff - 1, max_expected_picture_id_gap_);
+    }
+  }
+
+  void VerifyTl0Idx(const ParsedPacket& current, const ParsedPacket& last) const
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
+    if (current.tl0_pic_idx == kNoTl0PicIdx ||
+        current.temporal_idx == kNoTemporalIdx) {
+      return;  // No temporal layers.
+    }
+
+    if (current.timestamp == last.timestamp || current.temporal_idx != 0) {
+      EXPECT_EQ(last.tl0_pic_idx, current.tl0_pic_idx);
+      return;
+    }
+
+    // New frame with |temporal_idx| 0.
+    // |tl0_pic_idx| should be increasing.
+    EXPECT_TRUE(AheadOf<uint8_t>(current.tl0_pic_idx, last.tl0_pic_idx));
+
+    // Expect continuously increasing idx.
+    int diff = ForwardDiff<uint8_t>(last.tl0_pic_idx, current.tl0_pic_idx);
+    if (diff > 1) {
+      // If the VideoSendStream is destroyed, any frames still in queue is lost.
+      // Gaps only possible for first frame after a recreation, i.e. key frames.
+      EXPECT_EQ(kVideoFrameKey, current.frame_type);
+      EXPECT_LE(diff - 1, max_expected_tl0_idx_gap_);
+    }
+  }
+
   Action OnSendRtp(const uint8_t* packet, size_t length) override {
     rtc::CritScope lock(&crit_);
 
     ParsedPacket parsed;
-    if (!ParsePayload(packet, length, &parsed)) {
+    if (!ParsePayload(packet, length, &parsed))
       return SEND_PACKET;
-    }
 
     uint32_t ssrc = parsed.ssrc;
     if (last_observed_packet_.find(ssrc) != last_observed_packet_.end()) {
-      // Verify continuity and monotonicity of picture_id sequence.
       // Compare to last packet.
-      if (last_observed_packet_[ssrc].timestamp == parsed.timestamp) {
-        // Packet belongs to same frame as before.
-        EXPECT_EQ(last_observed_packet_[ssrc].picture_id, parsed.picture_id);
-      } else {
-        // Packet belongs to a new frame.
-
-        // Picture id should be increasing.
-        EXPECT_TRUE((AheadOf<uint16_t, kPictureIdWraparound>(
-            parsed.picture_id, last_observed_packet_[ssrc].picture_id)));
-
-        // Picture id should not increase more than expected.
-        const int picture_id_diff = ForwardDiff<uint16_t, kPictureIdWraparound>(
-            last_observed_packet_[ssrc].picture_id, parsed.picture_id);
-
-        // For delta frames, expect continuously increasing picture id.
-        if (parsed.frame_type != kVideoFrameKey) {
-          EXPECT_EQ(picture_id_diff, 1);
-        }
-        // Any frames still in queue is lost when a VideoSendStream is
-        // destroyed. The first frame after recreation should be a key frame.
-        if (picture_id_diff > 1) {
-          EXPECT_EQ(kVideoFrameKey, parsed.frame_type);
-          EXPECT_LE(picture_id_diff - 1, max_expected_picture_id_gap_);
-        }
-      }
+      VerifyPictureId(parsed, last_observed_packet_[ssrc]);
+      VerifyTl0Idx(parsed, last_observed_packet_[ssrc]);
     }
 
     last_observed_packet_[ssrc] = parsed;
@@ -178,14 +217,18 @@
   std::map<uint32_t, ParsedPacket> last_observed_packet_ RTC_GUARDED_BY(crit_);
   std::map<uint32_t, size_t> num_packets_sent_ RTC_GUARDED_BY(crit_);
   int max_expected_picture_id_gap_ RTC_GUARDED_BY(crit_);
+  int max_expected_tl0_idx_gap_ RTC_GUARDED_BY(crit_);
   size_t num_ssrcs_to_observe_ RTC_GUARDED_BY(crit_);
   std::set<uint32_t> observed_ssrcs_ RTC_GUARDED_BY(crit_);
 };
 
 class PictureIdTest : public test::CallTest,
-                      public ::testing::WithParamInterface<std::string> {
+                      public ::testing::WithParamInterface<
+                          ::testing::tuple<std::string, size_t>> {
  public:
-  PictureIdTest() : scoped_field_trial_(GetParam()) {}
+  PictureIdTest()
+      : scoped_field_trial_(::testing::get<0>(GetParam())),
+        num_temporal_layers_(::testing::get<1>(GetParam())) {}
 
   virtual ~PictureIdTest() {
     EXPECT_EQ(nullptr, video_send_stream_);
@@ -208,20 +251,23 @@
 
  private:
   test::ScopedFieldTrials scoped_field_trial_;
+  const size_t num_temporal_layers_;
   std::unique_ptr<PictureIdObserver> observer_;
 };
 
-INSTANTIATE_TEST_CASE_P(TestWithForcedFallbackEncoderEnabled,
-                        PictureIdTest,
-                        ::testing::Values(kVp8ForcedFallbackEncoderEnabled,
-                                          ""));
+INSTANTIATE_TEST_CASE_P(
+    TestWithForcedFallbackEncoderEnabled,
+    PictureIdTest,
+    ::testing::Combine(::testing::Values(kVp8ForcedFallbackEncoderEnabled, ""),
+                       ::testing::ValuesIn(kNumTemporalLayers)));
 
 // Use a special stream factory to ensure that all simulcast streams are being
 // sent.
 class VideoStreamFactory
     : public VideoEncoderConfig::VideoStreamFactoryInterface {
  public:
-  VideoStreamFactory() = default;
+  explicit VideoStreamFactory(size_t num_temporal_layers)
+      : num_of_temporal_layers_(num_temporal_layers) {}
 
  private:
   std::vector<VideoStream> CreateEncoderStreams(
@@ -238,6 +284,8 @@
         streams[i].min_bitrate_bps = kEncoderBitrateBps;
         streams[i].target_bitrate_bps = kEncoderBitrateBps;
         streams[i].max_bitrate_bps = kEncoderBitrateBps;
+        streams[i].temporal_layer_thresholds_bps.resize(
+            num_of_temporal_layers_ - 1);
       }
 
       // test::CreateVideoStreams does not return frame sizes for the lower
@@ -254,10 +302,14 @@
       streams[0].min_bitrate_bps = 3 * kEncoderBitrateBps;
       streams[0].target_bitrate_bps = 3 * kEncoderBitrateBps;
       streams[0].max_bitrate_bps = 3 * kEncoderBitrateBps;
+      streams[0].temporal_layer_thresholds_bps.resize(num_of_temporal_layers_ -
+                                                      1);
     }
 
     return streams;
   }
+
+  const size_t num_of_temporal_layers_;
 };
 
 void PictureIdTest::SetupEncoder(VideoEncoder* encoder,
@@ -278,7 +330,7 @@
     video_send_config_.encoder_settings.encoder = encoder;
     video_send_config_.encoder_settings.payload_name = payload_name;
     video_encoder_config_.video_stream_factory =
-        new rtc::RefCountedObject<VideoStreamFactory>();
+        new rtc::RefCountedObject<VideoStreamFactory>(num_temporal_layers_);
     video_encoder_config_.number_of_streams = 1;
   });
 }
@@ -375,7 +427,7 @@
 
 TEST_P(PictureIdTest, ContinuousAfterStreamCountChangeVp8) {
   std::unique_ptr<VideoEncoder> encoder(VP8Encoder::Create());
-  // Make sure that that the picture id is not reset if the stream count goes
+  // Make sure that the picture id is not reset if the stream count goes
   // down and then up.
   SetupEncoder(encoder.get(), "VP8");
   TestPictureIdContinuousAfterReconfigure({3, 1, 3});
@@ -401,11 +453,12 @@
 TEST_P(PictureIdTest, ContinuousAfterStreamCountChangeSimulcastEncoderAdapter) {
   // If forced fallback is enabled, the picture id is set in the PayloadRouter
   // and the sequence should be continuous.
-  if (GetParam() == kVp8ForcedFallbackEncoderEnabled) {
+  if (::testing::get<0>(GetParam()) == kVp8ForcedFallbackEncoderEnabled &&
+      ::testing::get<1>(GetParam()) == 1) {  // No temporal layers.
     InternalEncoderFactory internal_encoder_factory;
     SimulcastEncoderAdapter simulcast_encoder_adapter(
         &internal_encoder_factory);
-    // Make sure that that the picture id is not reset if the stream count goes
+    // Make sure that the picture id is not reset if the stream count goes
     // down and then up.
     SetupEncoder(&simulcast_encoder_adapter, "VP8");
     TestPictureIdContinuousAfterReconfigure({3, 1, 3});