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}
This commit is contained in:
Åsa Persson 2017-12-04 11:11:19 +01:00 committed by Commit Bot
parent 4e76ebb372
commit 71485ac0f3

View File

@ -25,6 +25,7 @@ const int kMaxFramesLost = kFrameRate * kMaxSecondsLost;
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 @@ class PictureIdObserver : public test::RtpRtcpObserver {
: 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 @@ class PictureIdObserver : public test::RtpRtcpObserver {
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 @@ class PictureIdObserver : public test::RtpRtcpObserver {
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 @@ class PictureIdObserver : public test::RtpRtcpObserver {
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 @@ class PictureIdObserver : public test::RtpRtcpObserver {
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 @@ class PictureIdObserver : public test::RtpRtcpObserver {
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 @@ class PictureIdTest : public test::CallTest,
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 @@ class VideoStreamFactory
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 @@ class VideoStreamFactory
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 @@ void PictureIdTest::SetupEncoder(VideoEncoder* encoder,
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, IncreasingAfterRecreateStreamVp8) {
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, IncreasingAfterRecreateStreamSimulcastEncoderAdapter) {
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});