Default streams: don't block media even if on different transceiver.
This fixes some edge cases where early media could cause default stream that block the actual signaled media from beind delivered. Bug: webrtc:11477 Change-Id: I8b26df63a690861bd19f083102d1395e882f8733 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/183120 Commit-Queue: Taylor <deadbeef@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Rasmus Brandt <brandtr@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32030}
This commit is contained in:
committed by
Commit Bot
parent
0ade98316c
commit
c03a187391
@ -14,12 +14,16 @@ if (rtc_include_tests) {
|
||||
sources = [
|
||||
"peer_scenario_quality_test.cc",
|
||||
"remote_estimate_test.cc",
|
||||
"unsignaled_stream_test.cc",
|
||||
]
|
||||
deps = [
|
||||
"..:peer_scenario",
|
||||
"../../:field_trial",
|
||||
"../../:rtp_test_utils",
|
||||
"../../:test_support",
|
||||
"../../../media:rtc_media_base",
|
||||
"../../../modules/rtp_rtcp:rtp_rtcp",
|
||||
"../../../modules/rtp_rtcp:rtp_rtcp_format",
|
||||
"../../../pc:rtc_pc_base",
|
||||
]
|
||||
}
|
||||
|
||||
157
test/peer_scenario/tests/unsignaled_stream_test.cc
Normal file
157
test/peer_scenario/tests/unsignaled_stream_test.cc
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2020 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 "media/base/stream_params.h"
|
||||
#include "modules/rtp_rtcp/source/byte_io.h"
|
||||
|
||||
#include "pc/media_session.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/peer_scenario/peer_scenario.h"
|
||||
#include "test/rtp_header_parser.h"
|
||||
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
class FrameObserver : public rtc::VideoSinkInterface<VideoFrame> {
|
||||
public:
|
||||
FrameObserver() : frame_observed_(false) {}
|
||||
void OnFrame(const VideoFrame&) override { frame_observed_ = true; }
|
||||
|
||||
std::atomic<bool> frame_observed_;
|
||||
};
|
||||
|
||||
uint32_t get_ssrc(SessionDescriptionInterface* offer, size_t track_index) {
|
||||
EXPECT_LT(track_index, offer->description()->contents().size());
|
||||
return offer->description()
|
||||
->contents()[track_index]
|
||||
.media_description()
|
||||
->streams()[0]
|
||||
.ssrcs[0];
|
||||
}
|
||||
|
||||
void set_ssrc(SessionDescriptionInterface* offer, size_t index, uint32_t ssrc) {
|
||||
EXPECT_LT(index, offer->description()->contents().size());
|
||||
cricket::StreamParams& new_stream_params = offer->description()
|
||||
->contents()[index]
|
||||
.media_description()
|
||||
->mutable_streams()[0];
|
||||
new_stream_params.ssrcs[0] = ssrc;
|
||||
new_stream_params.ssrc_groups[0].ssrcs[0] = ssrc;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UnsignaledStreamTest, ReplacesUnsignaledStreamOnCompletedSignaling) {
|
||||
// This test covers a scenario that might occur if a remote client starts
|
||||
// sending media packets before negotiation has completed. These packets will
|
||||
// trigger an unsignalled default stream to be created, and connects that to
|
||||
// a default video sink.
|
||||
// In some edge cases using unified plan, the default stream is create in a
|
||||
// different transceiver to where the media SSRC will actually be used.
|
||||
// This test verifies that the default stream is removed properly, and that
|
||||
// packets are demuxed and video frames reach the desired sink.
|
||||
|
||||
// Defined before PeerScenario so it gets destructed after, to avoid use after
|
||||
// free.
|
||||
PeerScenario s(*test_info_);
|
||||
|
||||
PeerScenarioClient::Config config = PeerScenarioClient::Config();
|
||||
// Disable encryption so that we can inject a fake early media packet without
|
||||
// triggering srtp failures.
|
||||
config.disable_encryption = true;
|
||||
auto* caller = s.CreateClient(config);
|
||||
auto* callee = s.CreateClient(config);
|
||||
|
||||
auto send_node = s.net()->NodeBuilder().Build().node;
|
||||
auto ret_node = s.net()->NodeBuilder().Build().node;
|
||||
|
||||
s.net()->CreateRoute(caller->endpoint(), {send_node}, callee->endpoint());
|
||||
s.net()->CreateRoute(callee->endpoint(), {ret_node}, caller->endpoint());
|
||||
|
||||
auto signaling = s.ConnectSignaling(caller, callee, {send_node}, {ret_node});
|
||||
PeerScenarioClient::VideoSendTrackConfig video_conf;
|
||||
video_conf.generator.squares_video->framerate = 15;
|
||||
|
||||
auto first_track = caller->CreateVideo("VIDEO", video_conf);
|
||||
FrameObserver first_sink;
|
||||
callee->AddVideoReceiveSink(first_track.track->id(), &first_sink);
|
||||
|
||||
signaling.StartIceSignaling();
|
||||
std::atomic<bool> offer_exchange_done(false);
|
||||
std::atomic<bool> got_unsignaled_packet(false);
|
||||
|
||||
// We will capture the media ssrc of the first added stream, and preemptively
|
||||
// inject a new media packet using a different ssrc.
|
||||
// This will create "default stream" for the second ssrc and connected it to
|
||||
// the default video sink (not set in this test).
|
||||
uint32_t first_ssrc = 0;
|
||||
uint32_t second_ssrc = 0;
|
||||
|
||||
signaling.NegotiateSdp(
|
||||
/* munge_sdp = */ {},
|
||||
/* modify_sdp = */
|
||||
[&](SessionDescriptionInterface* offer) {
|
||||
first_ssrc = get_ssrc(offer, 0);
|
||||
second_ssrc = first_ssrc + 1;
|
||||
|
||||
send_node->router()->SetWatcher([&](const EmulatedIpPacket& packet) {
|
||||
if (packet.size() > 1 && packet.cdata()[0] >> 6 == 2 &&
|
||||
!RtpHeaderParser::IsRtcp(packet.data.cdata(),
|
||||
packet.data.size())) {
|
||||
if (ByteReader<uint32_t>::ReadBigEndian(&(packet.cdata()[8])) ==
|
||||
first_ssrc &&
|
||||
!got_unsignaled_packet) {
|
||||
rtc::CopyOnWriteBuffer updated_buffer = packet.data;
|
||||
ByteWriter<uint32_t>::WriteBigEndian(&updated_buffer.data()[8],
|
||||
second_ssrc);
|
||||
EmulatedIpPacket updated_packet(
|
||||
packet.from, packet.to, updated_buffer, packet.arrival_time);
|
||||
send_node->OnPacketReceived(std::move(updated_packet));
|
||||
got_unsignaled_packet = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
[&](const SessionDescriptionInterface& answer) {
|
||||
EXPECT_EQ(answer.description()->contents().size(), 1u);
|
||||
offer_exchange_done = true;
|
||||
});
|
||||
EXPECT_TRUE(s.WaitAndProcess(&offer_exchange_done));
|
||||
EXPECT_TRUE(s.WaitAndProcess(&got_unsignaled_packet));
|
||||
EXPECT_TRUE(s.WaitAndProcess(&first_sink.frame_observed_));
|
||||
|
||||
auto second_track = caller->CreateVideo("VIDEO2", video_conf);
|
||||
FrameObserver second_sink;
|
||||
callee->AddVideoReceiveSink(second_track.track->id(), &second_sink);
|
||||
|
||||
// Create a second video stream, munge the sdp to force it to use our fake
|
||||
// early media ssrc.
|
||||
offer_exchange_done = false;
|
||||
signaling.NegotiateSdp(
|
||||
/* munge_sdp = */
|
||||
[&](SessionDescriptionInterface* offer) {
|
||||
set_ssrc(offer, 1, second_ssrc);
|
||||
},
|
||||
/* modify_sdp = */ {},
|
||||
[&](const SessionDescriptionInterface& answer) {
|
||||
EXPECT_EQ(answer.description()->contents().size(), 2u);
|
||||
offer_exchange_done = true;
|
||||
});
|
||||
EXPECT_TRUE(s.WaitAndProcess(&offer_exchange_done));
|
||||
EXPECT_TRUE(s.WaitAndProcess(&second_sink.frame_observed_));
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
Reference in New Issue
Block a user