Test picture sequence id when VideoSendStream is recreated.
Bug: webrtc:7475 Change-Id: I613b20b9da2c264b05c6a604d3f9754878857a0f Reviewed-on: https://chromium-review.googlesource.com/527076 Commit-Queue: Sebastian Jansson <srte@webrtc.org> Reviewed-by: Åsa Persson <asapersson@webrtc.org> Reviewed-by: Rasmus Brandt <brandtr@webrtc.org> Cr-Commit-Position: refs/heads/master@{#18722}
This commit is contained in:
committed by
Commit Bot
parent
ab04559c7f
commit
e92f93fc9a
@ -241,6 +241,7 @@ if (rtc_include_tests) {
|
|||||||
"end_to_end_tests.cc",
|
"end_to_end_tests.cc",
|
||||||
"overuse_frame_detector_unittest.cc",
|
"overuse_frame_detector_unittest.cc",
|
||||||
"payload_router_unittest.cc",
|
"payload_router_unittest.cc",
|
||||||
|
"picture_id_tests.cc",
|
||||||
"quality_threshold_unittest.cc",
|
"quality_threshold_unittest.cc",
|
||||||
"receive_statistics_proxy_unittest.cc",
|
"receive_statistics_proxy_unittest.cc",
|
||||||
"report_block_stats_unittest.cc",
|
"report_block_stats_unittest.cc",
|
||||||
|
|||||||
@ -139,7 +139,6 @@ class EndToEndTest : public test::CallTest {
|
|||||||
void RespectsRtcpMode(RtcpMode rtcp_mode);
|
void RespectsRtcpMode(RtcpMode rtcp_mode);
|
||||||
void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
|
void TestSendsSetSsrcs(size_t num_ssrcs, bool send_single_ssrc_first);
|
||||||
void TestRtpStatePreservation(bool use_rtx, bool provoke_rtcpsr_before_rtp);
|
void TestRtpStatePreservation(bool use_rtx, bool provoke_rtcpsr_before_rtp);
|
||||||
void TestPictureIdStatePreservation(VideoEncoder* encoder);
|
|
||||||
void VerifyHistogramStats(bool use_rtx, bool use_red, bool screenshare);
|
void VerifyHistogramStats(bool use_rtx, bool use_red, bool screenshare);
|
||||||
void VerifyNewVideoSendStreamsRespectNetworkState(
|
void VerifyNewVideoSendStreamsRespectNetworkState(
|
||||||
MediaType network_to_bring_up,
|
MediaType network_to_bring_up,
|
||||||
@ -3940,236 +3939,6 @@ TEST_F(EndToEndTest, RestartingSendStreamKeepsRtpAndRtcpTimestampsSynced) {
|
|||||||
TestRtpStatePreservation(true, true);
|
TestRtpStatePreservation(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EndToEndTest::TestPictureIdStatePreservation(VideoEncoder* encoder) {
|
|
||||||
const size_t kFrameMaxWidth = 1280;
|
|
||||||
const size_t kFrameMaxHeight = 720;
|
|
||||||
const size_t kFrameRate = 30;
|
|
||||||
|
|
||||||
// Use a special stream factory in this test to ensure that all simulcast
|
|
||||||
// streams are being sent.
|
|
||||||
class VideoStreamFactory
|
|
||||||
: public VideoEncoderConfig::VideoStreamFactoryInterface {
|
|
||||||
public:
|
|
||||||
VideoStreamFactory() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<VideoStream> CreateEncoderStreams(
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
const VideoEncoderConfig& encoder_config) override {
|
|
||||||
std::vector<VideoStream> streams =
|
|
||||||
test::CreateVideoStreams(width, height, encoder_config);
|
|
||||||
|
|
||||||
const size_t kBitrate = 100000;
|
|
||||||
|
|
||||||
if (encoder_config.number_of_streams > 1) {
|
|
||||||
RTC_DCHECK_EQ(3, encoder_config.number_of_streams);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
|
|
||||||
streams[i].min_bitrate_bps = kBitrate;
|
|
||||||
streams[i].target_bitrate_bps = kBitrate;
|
|
||||||
streams[i].max_bitrate_bps = kBitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// test::CreateVideoStreams does not return frame sizes for the lower
|
|
||||||
// streams that are accepted by VP8Impl::InitEncode.
|
|
||||||
// TODO(brandtr): Fix the problem in test::CreateVideoStreams, rather
|
|
||||||
// than overriding the values here.
|
|
||||||
streams[1].width = streams[2].width / 2;
|
|
||||||
streams[1].height = streams[2].height / 2;
|
|
||||||
streams[0].width = streams[1].width / 2;
|
|
||||||
streams[0].height = streams[1].height / 2;
|
|
||||||
} else {
|
|
||||||
// Use the same total bitrates when sending a single stream to avoid
|
|
||||||
// lowering the bitrate estimate and requiring a subsequent rampup.
|
|
||||||
streams[0].min_bitrate_bps = 3 * kBitrate;
|
|
||||||
streams[0].target_bitrate_bps = 3 * kBitrate;
|
|
||||||
streams[0].max_bitrate_bps = 3 * kBitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return streams;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class PictureIdObserver : public test::RtpRtcpObserver {
|
|
||||||
public:
|
|
||||||
PictureIdObserver()
|
|
||||||
: test::RtpRtcpObserver(kDefaultTimeoutMs), num_ssrcs_to_observe_(1) {}
|
|
||||||
|
|
||||||
void ResetExpectations(size_t num_expected_ssrcs) {
|
|
||||||
rtc::CritScope lock(&crit_);
|
|
||||||
// Do not clear the timestamp and picture_id, to ensure that we check
|
|
||||||
// consistency between reinits and recreations.
|
|
||||||
num_packets_sent_.clear();
|
|
||||||
num_ssrcs_to_observe_ = num_expected_ssrcs;
|
|
||||||
ssrc_observed_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Action OnSendRtp(const uint8_t* packet, size_t length) override {
|
|
||||||
rtc::CritScope lock(&crit_);
|
|
||||||
|
|
||||||
// RTP header.
|
|
||||||
RTPHeader header;
|
|
||||||
EXPECT_TRUE(parser_->Parse(packet, length, &header));
|
|
||||||
const uint32_t timestamp = header.timestamp;
|
|
||||||
const uint32_t ssrc = header.ssrc;
|
|
||||||
|
|
||||||
const bool known_ssrc =
|
|
||||||
(ssrc == kVideoSendSsrcs[0] || ssrc == kVideoSendSsrcs[1] ||
|
|
||||||
ssrc == kVideoSendSsrcs[2]);
|
|
||||||
EXPECT_TRUE(known_ssrc) << "Unknown SSRC sent.";
|
|
||||||
|
|
||||||
const bool is_padding =
|
|
||||||
(length == header.headerLength + header.paddingLength);
|
|
||||||
if (is_padding) {
|
|
||||||
return SEND_PACKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
// VP8 header.
|
|
||||||
std::unique_ptr<RtpDepacketizer> depacketizer(
|
|
||||||
RtpDepacketizer::Create(kRtpVideoVp8));
|
|
||||||
RtpDepacketizer::ParsedPayload parsed_payload;
|
|
||||||
EXPECT_TRUE(depacketizer->Parse(
|
|
||||||
&parsed_payload, &packet[header.headerLength],
|
|
||||||
length - header.headerLength - header.paddingLength));
|
|
||||||
const uint16_t picture_id =
|
|
||||||
parsed_payload.type.Video.codecHeader.VP8.pictureId;
|
|
||||||
|
|
||||||
// If this is the first packet, we have nothing to compare to.
|
|
||||||
if (last_observed_timestamp_.find(ssrc) ==
|
|
||||||
last_observed_timestamp_.end()) {
|
|
||||||
last_observed_timestamp_[ssrc] = timestamp;
|
|
||||||
last_observed_picture_id_[ssrc] = picture_id;
|
|
||||||
++num_packets_sent_[ssrc];
|
|
||||||
|
|
||||||
return SEND_PACKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify continuity and monotonicity of picture_id sequence.
|
|
||||||
if (last_observed_timestamp_[ssrc] == timestamp) {
|
|
||||||
// Packet belongs to same frame as before.
|
|
||||||
EXPECT_EQ(last_observed_picture_id_[ssrc], picture_id);
|
|
||||||
} else {
|
|
||||||
// Packet is a new frame.
|
|
||||||
EXPECT_EQ((last_observed_picture_id_[ssrc] + 1) % (1 << 15),
|
|
||||||
picture_id);
|
|
||||||
}
|
|
||||||
last_observed_timestamp_[ssrc] = timestamp;
|
|
||||||
last_observed_picture_id_[ssrc] = picture_id;
|
|
||||||
|
|
||||||
// Pass the test when enough media packets have been received
|
|
||||||
// on all streams.
|
|
||||||
if (++num_packets_sent_[ssrc] >= 10 && !ssrc_observed_[ssrc]) {
|
|
||||||
ssrc_observed_[ssrc] = true;
|
|
||||||
if (--num_ssrcs_to_observe_ == 0) {
|
|
||||||
observation_complete_.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SEND_PACKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc::CriticalSection crit_;
|
|
||||||
std::map<uint32_t, uint32_t> last_observed_timestamp_ GUARDED_BY(crit_);
|
|
||||||
std::map<uint32_t, uint16_t> last_observed_picture_id_ GUARDED_BY(crit_);
|
|
||||||
std::map<uint32_t, size_t> num_packets_sent_ GUARDED_BY(crit_);
|
|
||||||
size_t num_ssrcs_to_observe_ GUARDED_BY(crit_);
|
|
||||||
std::map<uint32_t, bool> ssrc_observed_ GUARDED_BY(crit_);
|
|
||||||
} observer;
|
|
||||||
|
|
||||||
Call::Config config(event_log_.get());
|
|
||||||
CreateCalls(config, config);
|
|
||||||
|
|
||||||
test::PacketTransport send_transport(
|
|
||||||
sender_call_.get(), &observer, test::PacketTransport::kSender,
|
|
||||||
payload_type_map_, FakeNetworkPipe::Config());
|
|
||||||
test::PacketTransport receive_transport(
|
|
||||||
nullptr, &observer, test::PacketTransport::kReceiver, payload_type_map_,
|
|
||||||
FakeNetworkPipe::Config());
|
|
||||||
send_transport.SetReceiver(receiver_call_->Receiver());
|
|
||||||
receive_transport.SetReceiver(sender_call_->Receiver());
|
|
||||||
|
|
||||||
CreateSendConfig(kNumSsrcs, 0, 0, &send_transport);
|
|
||||||
video_send_config_.encoder_settings.encoder = encoder;
|
|
||||||
video_send_config_.encoder_settings.payload_name = "VP8";
|
|
||||||
video_encoder_config_.video_stream_factory =
|
|
||||||
new rtc::RefCountedObject<VideoStreamFactory>();
|
|
||||||
video_encoder_config_.number_of_streams = 1;
|
|
||||||
CreateMatchingReceiveConfigs(&receive_transport);
|
|
||||||
|
|
||||||
CreateVideoStreams();
|
|
||||||
CreateFrameGeneratorCapturer(kFrameRate, kFrameMaxWidth, kFrameMaxHeight);
|
|
||||||
|
|
||||||
auto reinit_encoder_and_test = [this, &observer](int num_expected_ssrcs) {
|
|
||||||
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_.Copy());
|
|
||||||
observer.ResetExpectations(num_expected_ssrcs);
|
|
||||||
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for packets.";
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO(brandtr): Add tests where we recreate the whole VideoSendStream. This
|
|
||||||
// requires synchronizing the frame generator output with the packetization
|
|
||||||
// output, to not have any timing-dependent gaps in the picture_id sequence.
|
|
||||||
|
|
||||||
// Initial test with a single stream.
|
|
||||||
Start();
|
|
||||||
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for packets.";
|
|
||||||
|
|
||||||
// Reinit the encoder and make sure the picture_id sequence is continuous.
|
|
||||||
reinit_encoder_and_test(1);
|
|
||||||
|
|
||||||
// Go up to three streams.
|
|
||||||
video_encoder_config_.number_of_streams = 3;
|
|
||||||
reinit_encoder_and_test(3);
|
|
||||||
reinit_encoder_and_test(3);
|
|
||||||
|
|
||||||
// Go back to one stream.
|
|
||||||
video_encoder_config_.number_of_streams = 1;
|
|
||||||
reinit_encoder_and_test(1);
|
|
||||||
reinit_encoder_and_test(1);
|
|
||||||
|
|
||||||
send_transport.StopSending();
|
|
||||||
receive_transport.StopSending();
|
|
||||||
|
|
||||||
Stop();
|
|
||||||
DestroyStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(EndToEndTest, PictureIdStateRetainedAfterReinitingVp8) {
|
|
||||||
std::unique_ptr<VideoEncoder> encoder(VP8Encoder::Create());
|
|
||||||
TestPictureIdStatePreservation(encoder.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(EndToEndTest,
|
|
||||||
PictureIdStateRetainedAfterReinitingSimulcastEncoderAdapter) {
|
|
||||||
class VideoEncoderFactoryAdapter : public webrtc::VideoEncoderFactory {
|
|
||||||
public:
|
|
||||||
explicit VideoEncoderFactoryAdapter(
|
|
||||||
cricket::WebRtcVideoEncoderFactory* factory)
|
|
||||||
: factory_(factory) {}
|
|
||||||
virtual ~VideoEncoderFactoryAdapter() {}
|
|
||||||
|
|
||||||
// Implements webrtc::VideoEncoderFactory.
|
|
||||||
webrtc::VideoEncoder* Create() override {
|
|
||||||
return factory_->CreateVideoEncoder(
|
|
||||||
cricket::VideoCodec(cricket::kVp8CodecName));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Destroy(webrtc::VideoEncoder* encoder) override {
|
|
||||||
return factory_->DestroyVideoEncoder(encoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
cricket::WebRtcVideoEncoderFactory* const factory_;
|
|
||||||
};
|
|
||||||
|
|
||||||
cricket::InternalEncoderFactory internal_encoder_factory;
|
|
||||||
SimulcastEncoderAdapter simulcast_encoder_adapter(
|
|
||||||
new VideoEncoderFactoryAdapter(&internal_encoder_factory));
|
|
||||||
|
|
||||||
TestPictureIdStatePreservation(&simulcast_encoder_adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This test is flaky on linux_memcheck. Disable on all linux bots until
|
// This test is flaky on linux_memcheck. Disable on all linux bots until
|
||||||
// flakyness has been fixed.
|
// flakyness has been fixed.
|
||||||
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7737
|
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7737
|
||||||
|
|||||||
349
webrtc/video/picture_id_tests.cc
Normal file
349
webrtc/video/picture_id_tests.cc
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 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/media/engine/internalencoderfactory.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
|
||||||
|
#include "webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.h"
|
||||||
|
#include "webrtc/modules/video_coding/sequence_number_util.h"
|
||||||
|
#include "webrtc/test/call_test.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
const int kFrameMaxWidth = 1280;
|
||||||
|
const int kFrameMaxHeight = 720;
|
||||||
|
const int kFrameRate = 30;
|
||||||
|
const int kMaxSecondsLost = 5;
|
||||||
|
const int kMaxFramesLost = kFrameRate * kMaxSecondsLost;
|
||||||
|
const int kMinPacketsToObserve = 10;
|
||||||
|
const int kEncoderBitrateBps = 100000;
|
||||||
|
const uint32_t kPictureIdWraparound = (1 << 15);
|
||||||
|
|
||||||
|
class PictureIdObserver : public test::RtpRtcpObserver {
|
||||||
|
public:
|
||||||
|
PictureIdObserver()
|
||||||
|
: test::RtpRtcpObserver(test::CallTest::kDefaultTimeoutMs),
|
||||||
|
max_expected_picture_id_gap_(0),
|
||||||
|
num_ssrcs_to_observe_(1) {}
|
||||||
|
|
||||||
|
void SetExpectedSsrcs(size_t num_expected_ssrcs) {
|
||||||
|
rtc::CritScope lock(&crit_);
|
||||||
|
num_ssrcs_to_observe_ = num_expected_ssrcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetObservedSsrcs() {
|
||||||
|
rtc::CritScope lock(&crit_);
|
||||||
|
// Do not clear the timestamp and picture_id, to ensure that we check
|
||||||
|
// consistency between reinits and recreations.
|
||||||
|
num_packets_sent_.clear();
|
||||||
|
observed_ssrcs_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMaxExpectedPictureIdGap(int max_expected_picture_id_gap) {
|
||||||
|
rtc::CritScope lock(&crit_);
|
||||||
|
max_expected_picture_id_gap_ = max_expected_picture_id_gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Action OnSendRtp(const uint8_t* packet, size_t length) override {
|
||||||
|
rtc::CritScope lock(&crit_);
|
||||||
|
|
||||||
|
// RTP header.
|
||||||
|
RTPHeader header;
|
||||||
|
EXPECT_TRUE(parser_->Parse(packet, length, &header));
|
||||||
|
const uint32_t timestamp = header.timestamp;
|
||||||
|
const uint32_t ssrc = header.ssrc;
|
||||||
|
|
||||||
|
const bool known_ssrc = (ssrc == test::CallTest::kVideoSendSsrcs[0] ||
|
||||||
|
ssrc == test::CallTest::kVideoSendSsrcs[1] ||
|
||||||
|
ssrc == test::CallTest::kVideoSendSsrcs[2]);
|
||||||
|
EXPECT_TRUE(known_ssrc) << "Unknown SSRC sent.";
|
||||||
|
|
||||||
|
const bool is_padding =
|
||||||
|
(length == header.headerLength + header.paddingLength);
|
||||||
|
if (is_padding) {
|
||||||
|
return SEND_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VP8 header.
|
||||||
|
std::unique_ptr<RtpDepacketizer> depacketizer(
|
||||||
|
RtpDepacketizer::Create(kRtpVideoVp8));
|
||||||
|
RtpDepacketizer::ParsedPayload parsed_payload;
|
||||||
|
EXPECT_TRUE(depacketizer->Parse(
|
||||||
|
&parsed_payload, &packet[header.headerLength],
|
||||||
|
length - header.headerLength - header.paddingLength));
|
||||||
|
const uint16_t picture_id =
|
||||||
|
parsed_payload.type.Video.codecHeader.VP8.pictureId;
|
||||||
|
|
||||||
|
// If this is the first packet, we have nothing to compare to.
|
||||||
|
if (last_observed_timestamp_.find(ssrc) == last_observed_timestamp_.end()) {
|
||||||
|
last_observed_timestamp_[ssrc] = timestamp;
|
||||||
|
last_observed_picture_id_[ssrc] = picture_id;
|
||||||
|
++num_packets_sent_[ssrc];
|
||||||
|
|
||||||
|
return SEND_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify continuity and monotonicity of picture_id sequence.
|
||||||
|
if (last_observed_timestamp_[ssrc] == timestamp) {
|
||||||
|
// Packet belongs to same frame as before.
|
||||||
|
EXPECT_EQ(last_observed_picture_id_[ssrc], picture_id);
|
||||||
|
} else {
|
||||||
|
// Packet is a new frame.
|
||||||
|
|
||||||
|
// Picture id should be increasing.
|
||||||
|
const bool picture_id_is_increasing =
|
||||||
|
AheadOf<uint16_t, kPictureIdWraparound>(
|
||||||
|
picture_id, last_observed_picture_id_[ssrc]);
|
||||||
|
EXPECT_TRUE(picture_id_is_increasing);
|
||||||
|
|
||||||
|
// Picture id should not increase more than expected.
|
||||||
|
const int picture_id_diff = ForwardDiff<uint16_t, kPictureIdWraparound>(
|
||||||
|
last_observed_picture_id_[ssrc], picture_id);
|
||||||
|
EXPECT_LE(picture_id_diff - 1, max_expected_picture_id_gap_);
|
||||||
|
}
|
||||||
|
last_observed_timestamp_[ssrc] = timestamp;
|
||||||
|
last_observed_picture_id_[ssrc] = picture_id;
|
||||||
|
|
||||||
|
// Pass the test when enough media packets have been received
|
||||||
|
// on all streams.
|
||||||
|
if (++num_packets_sent_[ssrc] >= kMinPacketsToObserve &&
|
||||||
|
observed_ssrcs_.find(ssrc) == observed_ssrcs_.end()) {
|
||||||
|
observed_ssrcs_.insert(ssrc);
|
||||||
|
if (observed_ssrcs_.size() == num_ssrcs_to_observe_) {
|
||||||
|
observation_complete_.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SEND_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc::CriticalSection crit_;
|
||||||
|
std::map<uint32_t, uint32_t> last_observed_timestamp_ GUARDED_BY(crit_);
|
||||||
|
std::map<uint32_t, uint16_t> last_observed_picture_id_ GUARDED_BY(crit_);
|
||||||
|
std::map<uint32_t, size_t> num_packets_sent_ GUARDED_BY(crit_);
|
||||||
|
int max_expected_picture_id_gap_ GUARDED_BY(crit_);
|
||||||
|
size_t num_ssrcs_to_observe_ GUARDED_BY(crit_);
|
||||||
|
std::set<uint32_t> observed_ssrcs_ GUARDED_BY(crit_);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PictureIdTest : public test::CallTest {
|
||||||
|
public:
|
||||||
|
PictureIdTest() {}
|
||||||
|
|
||||||
|
virtual ~PictureIdTest() {
|
||||||
|
EXPECT_EQ(nullptr, video_send_stream_);
|
||||||
|
EXPECT_TRUE(video_receive_streams_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupEncoder(VideoEncoder* encoder);
|
||||||
|
void TestPictureIdContinuousAfterReconfigure(
|
||||||
|
const std::vector<int>& ssrc_counts);
|
||||||
|
void TestPictureIdIncreaseAfterRecreateStreams(
|
||||||
|
const std::vector<int>& ssrc_counts);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PictureIdObserver observer;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use a special stream factory to ensure that all simulcast streams are being
|
||||||
|
// sent.
|
||||||
|
class VideoStreamFactory
|
||||||
|
: public VideoEncoderConfig::VideoStreamFactoryInterface {
|
||||||
|
public:
|
||||||
|
VideoStreamFactory() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<VideoStream> CreateEncoderStreams(
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const VideoEncoderConfig& encoder_config) override {
|
||||||
|
std::vector<VideoStream> streams =
|
||||||
|
test::CreateVideoStreams(width, height, encoder_config);
|
||||||
|
|
||||||
|
if (encoder_config.number_of_streams > 1) {
|
||||||
|
RTC_DCHECK_EQ(3, encoder_config.number_of_streams);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
|
||||||
|
streams[i].min_bitrate_bps = kEncoderBitrateBps;
|
||||||
|
streams[i].target_bitrate_bps = kEncoderBitrateBps;
|
||||||
|
streams[i].max_bitrate_bps = kEncoderBitrateBps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test::CreateVideoStreams does not return frame sizes for the lower
|
||||||
|
// streams that are accepted by VP8Impl::InitEncode.
|
||||||
|
// TODO(brandtr): Fix the problem in test::CreateVideoStreams, rather
|
||||||
|
// than overriding the values here.
|
||||||
|
streams[1].width = streams[2].width / 2;
|
||||||
|
streams[1].height = streams[2].height / 2;
|
||||||
|
streams[0].width = streams[1].width / 2;
|
||||||
|
streams[0].height = streams[1].height / 2;
|
||||||
|
} else {
|
||||||
|
// Use the same total bitrates when sending a single stream to avoid
|
||||||
|
// lowering the bitrate estimate and requiring a subsequent rampup.
|
||||||
|
streams[0].min_bitrate_bps = 3 * kEncoderBitrateBps;
|
||||||
|
streams[0].target_bitrate_bps = 3 * kEncoderBitrateBps;
|
||||||
|
streams[0].max_bitrate_bps = 3 * kEncoderBitrateBps;
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void PictureIdTest::SetupEncoder(VideoEncoder* encoder) {
|
||||||
|
Call::Config config(event_log_.get());
|
||||||
|
CreateCalls(config, config);
|
||||||
|
|
||||||
|
send_transport_.reset(new test::PacketTransport(
|
||||||
|
sender_call_.get(), &observer, test::PacketTransport::kSender,
|
||||||
|
payload_type_map_, FakeNetworkPipe::Config()));
|
||||||
|
|
||||||
|
CreateSendConfig(kNumSsrcs, 0, 0, send_transport_.get());
|
||||||
|
video_send_config_.encoder_settings.encoder = encoder;
|
||||||
|
video_send_config_.encoder_settings.payload_name = "VP8";
|
||||||
|
video_encoder_config_.video_stream_factory =
|
||||||
|
new rtc::RefCountedObject<VideoStreamFactory>();
|
||||||
|
video_encoder_config_.number_of_streams = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureIdTest::TestPictureIdContinuousAfterReconfigure(
|
||||||
|
const std::vector<int>& ssrc_counts) {
|
||||||
|
CreateVideoStreams();
|
||||||
|
CreateFrameGeneratorCapturer(kFrameRate, kFrameMaxWidth, kFrameMaxHeight);
|
||||||
|
|
||||||
|
// Initial test with a single stream.
|
||||||
|
Start();
|
||||||
|
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for packets.";
|
||||||
|
|
||||||
|
// Reconfigure VideoEncoder and test picture id increase.
|
||||||
|
// Expect continously increasing picture id, equivalent to no gaps.
|
||||||
|
observer.SetMaxExpectedPictureIdGap(0);
|
||||||
|
for (int ssrc_count : ssrc_counts) {
|
||||||
|
video_encoder_config_.number_of_streams = ssrc_count;
|
||||||
|
observer.SetExpectedSsrcs(ssrc_count);
|
||||||
|
observer.ResetObservedSsrcs();
|
||||||
|
// Make sure the picture_id sequence is continuous on reinit and recreate.
|
||||||
|
video_send_stream_->ReconfigureVideoEncoder(video_encoder_config_.Copy());
|
||||||
|
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for packets.";
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
DestroyStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PictureIdTest::TestPictureIdIncreaseAfterRecreateStreams(
|
||||||
|
const std::vector<int>& ssrc_counts) {
|
||||||
|
CreateVideoStreams();
|
||||||
|
CreateFrameGeneratorCapturer(kFrameRate, kFrameMaxWidth, kFrameMaxHeight);
|
||||||
|
|
||||||
|
// Initial test with a single stream.
|
||||||
|
Start();
|
||||||
|
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for packets.";
|
||||||
|
|
||||||
|
// Recreate VideoSendStream and test picture id increase.
|
||||||
|
// When the VideoSendStream is destroyed, any frames still in queue is lost
|
||||||
|
// with it, therefore it is expected that some frames might be lost.
|
||||||
|
observer.SetMaxExpectedPictureIdGap(kMaxFramesLost);
|
||||||
|
for (int ssrc_count : ssrc_counts) {
|
||||||
|
video_encoder_config_.number_of_streams = ssrc_count;
|
||||||
|
|
||||||
|
frame_generator_capturer_->Stop();
|
||||||
|
sender_call_->DestroyVideoSendStream(video_send_stream_);
|
||||||
|
|
||||||
|
observer.SetExpectedSsrcs(ssrc_count);
|
||||||
|
observer.ResetObservedSsrcs();
|
||||||
|
|
||||||
|
video_send_stream_ = sender_call_->CreateVideoSendStream(
|
||||||
|
video_send_config_.Copy(), video_encoder_config_.Copy());
|
||||||
|
video_send_stream_->Start();
|
||||||
|
CreateFrameGeneratorCapturer(kFrameRate, kFrameMaxWidth, kFrameMaxHeight);
|
||||||
|
frame_generator_capturer_->Start();
|
||||||
|
EXPECT_TRUE(observer.Wait()) << "Timed out waiting for packets.";
|
||||||
|
}
|
||||||
|
|
||||||
|
Stop();
|
||||||
|
DestroyStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PictureIdTest, PictureIdContinuousAfterReconfigureVp8) {
|
||||||
|
std::unique_ptr<VideoEncoder> encoder(VP8Encoder::Create());
|
||||||
|
SetupEncoder(encoder.get());
|
||||||
|
TestPictureIdContinuousAfterReconfigure({1, 3, 3, 1, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PictureIdTest, PictureIdIncreasingAfterRecreateStreamVp8) {
|
||||||
|
std::unique_ptr<VideoEncoder> encoder(VP8Encoder::Create());
|
||||||
|
SetupEncoder(encoder.get());
|
||||||
|
TestPictureIdIncreaseAfterRecreateStreams({1, 3, 3, 1, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PictureIdTest, PictureIdIncreasingAfterStreamCountChangeVp8) {
|
||||||
|
std::unique_ptr<VideoEncoder> encoder(VP8Encoder::Create());
|
||||||
|
// Make sure that that the picture id is not reset if the stream count goes
|
||||||
|
// down and then up.
|
||||||
|
std::vector<int> ssrc_counts = {3, 1, 3};
|
||||||
|
SetupEncoder(encoder.get());
|
||||||
|
TestPictureIdContinuousAfterReconfigure(ssrc_counts);
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoEncoderFactoryAdapter : public webrtc::VideoEncoderFactory {
|
||||||
|
public:
|
||||||
|
explicit VideoEncoderFactoryAdapter(
|
||||||
|
cricket::WebRtcVideoEncoderFactory* factory)
|
||||||
|
: factory_(factory) {}
|
||||||
|
virtual ~VideoEncoderFactoryAdapter() {}
|
||||||
|
|
||||||
|
// Implements webrtc::VideoEncoderFactory.
|
||||||
|
webrtc::VideoEncoder* Create() override {
|
||||||
|
return factory_->CreateVideoEncoder(
|
||||||
|
cricket::VideoCodec(cricket::kVp8CodecName));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Destroy(webrtc::VideoEncoder* encoder) override {
|
||||||
|
return factory_->DestroyVideoEncoder(encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
cricket::WebRtcVideoEncoderFactory* const factory_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(PictureIdTest,
|
||||||
|
PictureIdContinuousAfterReconfigureSimulcastEncoderAdapter) {
|
||||||
|
cricket::InternalEncoderFactory internal_encoder_factory;
|
||||||
|
SimulcastEncoderAdapter simulcast_encoder_adapter(
|
||||||
|
new VideoEncoderFactoryAdapter(&internal_encoder_factory));
|
||||||
|
SetupEncoder(&simulcast_encoder_adapter);
|
||||||
|
TestPictureIdContinuousAfterReconfigure({1, 3, 3, 1, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PictureIdTest,
|
||||||
|
PictureIdIncreasingAfterRecreateStreamSimulcastEncoderAdapter) {
|
||||||
|
cricket::InternalEncoderFactory internal_encoder_factory;
|
||||||
|
SimulcastEncoderAdapter simulcast_encoder_adapter(
|
||||||
|
new VideoEncoderFactoryAdapter(&internal_encoder_factory));
|
||||||
|
SetupEncoder(&simulcast_encoder_adapter);
|
||||||
|
TestPictureIdIncreaseAfterRecreateStreams({1, 3, 3, 1, 1});
|
||||||
|
}
|
||||||
|
|
||||||
|
// When using the simulcast encoder adapter, the picture id is randomly set
|
||||||
|
// when the ssrc count is reduced and then increased. This means that we are
|
||||||
|
// not spec compliant in that particular case.
|
||||||
|
TEST_F(
|
||||||
|
PictureIdTest,
|
||||||
|
DISABLED_PictureIdIncreasingAfterStreamCountChangeSimulcastEncoderAdapter) {
|
||||||
|
cricket::InternalEncoderFactory internal_encoder_factory;
|
||||||
|
SimulcastEncoderAdapter simulcast_encoder_adapter(
|
||||||
|
new VideoEncoderFactoryAdapter(&internal_encoder_factory));
|
||||||
|
// Make sure that that the picture id is not reset if the stream count goes
|
||||||
|
// down and then up.
|
||||||
|
std::vector<int> ssrc_counts = {3, 1, 3};
|
||||||
|
SetupEncoder(&simulcast_encoder_adapter);
|
||||||
|
TestPictureIdContinuousAfterReconfigure(ssrc_counts);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
Reference in New Issue
Block a user