Pivot generation of stats to iterate senders/receivers

This will allow stats to be generated when AddTrack() is used.
It also exposes a ClearStatsCache() call on the PC to allow enforcement
of cache lifetime restrictions.

Bug: webrtc:8616
Change-Id: If47b967ce9e40fa768303e6f5f54fe74db2cc7a4
Reviewed-on: https://webrtc-review.googlesource.com/34360
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21468}
This commit is contained in:
Harald Alvestrand 2018-01-02 14:08:34 +01:00 committed by Commit Bot
parent 91fedfbedf
commit 8906187c86
9 changed files with 372 additions and 83 deletions

View File

@ -734,6 +734,10 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
// break third party projects. As soon as they have been updated this should
// be changed to "= 0;".
virtual void GetStats(RTCStatsCollectorCallback* callback) {}
// Clear cached stats in the rtcstatscollector.
// Exposed for testing while waiting for automatic cache clear to work.
// https://bugs.webrtc.org/8693
virtual void ClearStatsCache() {}
// Create a data channel with the provided config, or default config if none
// is provided. Note that an offer/answer negotiation is still necessary

View File

@ -5518,4 +5518,10 @@ void PeerConnection::DestroyBaseChannel(cricket::BaseChannel* channel) {
}
}
void PeerConnection::ClearStatsCache() {
if (stats_collector_) {
stats_collector_->ClearCachedStatsReport();
}
}
} // namespace webrtc

View File

@ -137,6 +137,7 @@ class PeerConnection : public PeerConnectionInterface,
webrtc::MediaStreamTrackInterface* track,
StatsOutputLevel level) override;
void GetStats(RTCStatsCollectorCallback* callback) override;
void ClearStatsCache() override;
SignalingState signaling_state() override;

View File

@ -846,6 +846,15 @@ class PeerConnectionInterfaceTest : public testing::Test {
return observer->called();
}
// Call the standards-compliant GetStats function.
bool DoGetRTCStats() {
rtc::scoped_refptr<webrtc::MockRTCStatsCollectorCallback> callback(
new rtc::RefCountedObject<webrtc::MockRTCStatsCollectorCallback>());
pc_->GetStats(callback);
EXPECT_TRUE_WAIT(callback->called(), kTimeout);
return callback->called();
}
void InitiateCall() {
CreatePeerConnectionWithoutDtls();
// Create a local stream with audio&video tracks.
@ -1466,7 +1475,6 @@ TEST_F(PeerConnectionInterfaceTest, AddTrackRemoveTrack) {
// expecting a random stream ID to be generated.
TEST_F(PeerConnectionInterfaceTest, AddTrackWithoutStream) {
CreatePeerConnectionWithoutDtls();
// Create a dummy stream, so tracks share a stream label.
rtc::scoped_refptr<AudioTrackInterface> audio_track(
pc_factory_->CreateAudioTrack("audio_track", nullptr));
rtc::scoped_refptr<VideoTrackInterface> video_track(
@ -1487,6 +1495,24 @@ TEST_F(PeerConnectionInterfaceTest, AddTrackWithoutStream) {
EXPECT_NE(video_sender->stream_ids(), audio_sender->stream_ids());
}
// Test that we can call GetStats() after AddTrack but before connecting
// the PeerConnection to a peer.
TEST_F(PeerConnectionInterfaceTest, AddTrackBeforeConnecting) {
CreatePeerConnectionWithoutDtls();
rtc::scoped_refptr<AudioTrackInterface> audio_track(
pc_factory_->CreateAudioTrack("audio_track", nullptr));
rtc::scoped_refptr<VideoTrackInterface> video_track(
pc_factory_->CreateVideoTrack(
"video_track", pc_factory_->CreateVideoSource(
std::unique_ptr<cricket::VideoCapturer>(
new cricket::FakeVideoCapturer()))));
auto audio_sender =
pc_->AddTrack(audio_track, std::vector<MediaStreamInterface*>());
auto video_sender =
pc_->AddTrack(video_track, std::vector<MediaStreamInterface*>());
EXPECT_TRUE(DoGetStats(nullptr));
}
TEST_F(PeerConnectionInterfaceTest, CreateOfferReceiveAnswer) {
InitiateCall();
WaitAndVerifyOnAddStream(kStreamLabel1);
@ -1735,6 +1761,19 @@ TEST_F(PeerConnectionInterfaceTest, GetStatsForInvalidTrack) {
EXPECT_FALSE(DoGetStats(unknown_audio_track));
}
TEST_F(PeerConnectionInterfaceTest, GetRTCStatsBeforeAndAfterCalling) {
CreatePeerConnectionWithoutDtls();
EXPECT_TRUE(DoGetRTCStats());
// Clearing stats cache is needed now, but should be temporary.
// https://bugs.chromium.org/p/webrtc/issues/detail?id=8693
pc_->ClearStatsCache();
AddAudioVideoStream(kStreamLabel1, "audio_track", "video_track");
EXPECT_TRUE(DoGetRTCStats());
pc_->ClearStatsCache();
CreateOfferReceiveAnswer();
EXPECT_TRUE(DoGetRTCStats());
}
// This test setup two RTP data channels in loop back.
TEST_F(PeerConnectionInterfaceTest, TestDataChannel) {
FakeConstraints constraints;

View File

@ -207,7 +207,7 @@ void SetInboundRTPStreamStatsFromMediaReceiverInfo(
RTCInboundRTPStreamStats* inbound_stats) {
RTC_DCHECK(inbound_stats);
inbound_stats->ssrc = media_receiver_info.ssrc();
// TODO(hbos): Support the remote case. crbug.com/657855
// TODO(hbos): Support the remote case. https://crbug.com/657855
inbound_stats->is_remote = false;
inbound_stats->packets_received =
static_cast<uint32_t>(media_receiver_info.packets_rcvd);
@ -265,7 +265,7 @@ void SetOutboundRTPStreamStatsFromMediaSenderInfo(
RTCOutboundRTPStreamStats* outbound_stats) {
RTC_DCHECK(outbound_stats);
outbound_stats->ssrc = media_sender_info.ssrc();
// TODO(hbos): Support the remote case. crbug.com/657856
// TODO(hbos): Support the remote case. https://crbug.com/657856
outbound_stats->is_remote = false;
outbound_stats->packets_sent =
static_cast<uint32_t>(media_sender_info.packets_sent);
@ -460,7 +460,7 @@ ProduceMediaStreamTrackStatsFromVideoSenderInfo(
video_track_stats->frame_height = static_cast<uint32_t>(
video_sender_info.send_frame_height);
// TODO(hbos): Will reduce this by frames dropped due to congestion control
// when available. crbug.com/659137
// when available. https://crbug.com/659137
video_track_stats->frames_sent = video_sender_info.frames_encoded;
return video_track_stats;
}
@ -492,7 +492,7 @@ ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
// TODO(hbos): When we support receiving simulcast, this should be the total
// number of frames correctly decoded, independent of which SSRC it was
// received from. Since we don't support that, this is correct and is the same
// value as "RTCInboundRTPStreamStats.framesDecoded". crbug.com/659137
// value as "RTCInboundRTPStreamStats.framesDecoded". https://crbug.com/659137
video_track_stats->frames_decoded = video_receiver_info.frames_decoded;
RTC_DCHECK_GE(video_receiver_info.frames_received,
video_receiver_info.frames_rendered);
@ -501,20 +501,99 @@ ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
return video_track_stats;
}
void ProduceMediaStreamAndTrackStats(
void ProduceSenderMediaTrackStats(
int64_t timestamp_us,
const TrackMediaInfoMap& track_media_info_map,
std::vector<rtc::scoped_refptr<RtpSenderInterface>> senders,
RTCStatsReport* report) {
// This function iterates over the senders to generate outgoing track stats.
// TODO(hbos): Return stats of detached tracks. We have to perform stats
// gathering at the time of detachment to get accurate stats and timestamps.
// https://crbug.com/659137
for (auto const sender : senders) {
// Don't report on tracks before starting to send.
// https://bugs.webrtc.org/8673
if (sender->ssrc() == 0)
continue;
if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
AudioTrackInterface* track =
static_cast<AudioTrackInterface*>(sender->track().get());
if (!track)
continue;
auto voice_sender_info =
track_media_info_map.GetVoiceSenderInfoBySsrc(sender->ssrc());
RTC_CHECK(voice_sender_info)
<< "No voice sender info for sender with ssrc " << sender->ssrc();
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats =
ProduceMediaStreamTrackStatsFromVoiceSenderInfo(timestamp_us, *track,
*voice_sender_info);
report->AddStats(std::move(audio_track_stats));
} else if (sender->media_type() == cricket::MEDIA_TYPE_VIDEO) {
VideoTrackInterface* track =
static_cast<VideoTrackInterface*>(sender->track().get());
if (!track)
continue;
auto video_sender_info =
track_media_info_map.GetVideoSenderInfoBySsrc(sender->ssrc());
RTC_CHECK(video_sender_info)
<< "No video sender info for sender with ssrc " << sender->ssrc();
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats =
ProduceMediaStreamTrackStatsFromVideoSenderInfo(timestamp_us, *track,
*video_sender_info);
report->AddStats(std::move(video_track_stats));
} else {
RTC_NOTREACHED();
}
}
}
void ProduceReceiverMediaTrackStats(
int64_t timestamp_us,
const TrackMediaInfoMap& track_media_info_map,
std::vector<rtc::scoped_refptr<RtpReceiverInterface>> receivers,
RTCStatsReport* report) {
// This function iterates over the receivers to find the remote tracks.
for (auto const receiver : receivers) {
if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
AudioTrackInterface* track =
static_cast<AudioTrackInterface*>(receiver->track().get());
const cricket::VoiceReceiverInfo* voice_receiver_info =
track_media_info_map.GetVoiceReceiverInfo(*track);
if (!voice_receiver_info) {
continue;
}
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats =
ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(
timestamp_us, *track, *voice_receiver_info);
report->AddStats(std::move(audio_track_stats));
} else if (receiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
VideoTrackInterface* track =
static_cast<VideoTrackInterface*>(receiver->track().get());
const cricket::VideoReceiverInfo* video_receiver_info =
track_media_info_map.GetVideoReceiverInfo(*track);
if (!video_receiver_info) {
continue;
}
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats =
ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
timestamp_us, *track, *video_receiver_info);
report->AddStats(std::move(video_track_stats));
} else {
RTC_NOTREACHED();
}
}
}
void ProduceMediaStreamStats(
int64_t timestamp_us,
const TrackMediaInfoMap& track_media_info_map,
rtc::scoped_refptr<StreamCollectionInterface> streams,
bool is_local,
RTCStatsReport* report) {
// TODO(hbos): When "AddTrack" is implemented we should iterate tracks to
// find which streams exist, not iterate streams to find tracks.
// crbug.com/659137
// TODO(hbos): Return stats of detached tracks. We have to perform stats
// gathering at the time of detachment to get accurate stats and timestamps.
// crbug.com/659137
if (!streams)
return;
// Collect info about streams and which tracks in them are attached to PC.
for (size_t i = 0; i < streams->count(); ++i) {
MediaStreamInterface* stream = streams->at(i);
@ -524,70 +603,64 @@ void ProduceMediaStreamAndTrackStats(
stream->label(), timestamp_us));
stream_stats->stream_identifier = stream->label();
stream_stats->track_ids = std::vector<std::string>();
// The track stats are per-attachment to the connection. There can be one
// for receiving (remote) tracks and multiple attachments for sending
// (local) tracks.
// Record the IDs of tracks that are currently connected.
// Note: Iterating over streams may be iffy with AddTrack.
// TODO(hta): Revisit in conjunction with https://bugs.webrtc.org/8674
if (is_local) {
// Local Audio Tracks
for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track :
stream->GetAudioTracks()) {
const std::vector<cricket::VoiceSenderInfo*>* voice_sender_infos =
for (auto audio_track : stream->GetAudioTracks()) {
auto sender_infos =
track_media_info_map.GetVoiceSenderInfos(*audio_track);
if (!voice_sender_infos) {
// There is no map entry on unconnected tracks.
// https://bugs.webrtc.org/8673
if (!sender_infos)
continue;
}
for (const auto& voice_sender_info : *voice_sender_infos) {
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats =
ProduceMediaStreamTrackStatsFromVoiceSenderInfo(
timestamp_us, *audio_track, *voice_sender_info);
stream_stats->track_ids->push_back(audio_track_stats->id());
report->AddStats(std::move(audio_track_stats));
for (const auto& sender_info : *sender_infos) {
// In the WebRTC implementation, SSRC 0 means unconnected,
// and should not occur in the map.
// https://bugs.webrtc.org/8694
RTC_DCHECK_NE(0, sender_info->ssrc());
stream_stats->track_ids->push_back(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
is_local, MediaStreamTrackInterface::kAudioKind,
audio_track->id(), sender_info->ssrc()));
}
}
// Local Video Tracks
for (const rtc::scoped_refptr<VideoTrackInterface>& video_track :
stream->GetVideoTracks()) {
const std::vector<cricket::VideoSenderInfo*>* video_sender_infos =
for (auto video_track : stream->GetVideoTracks()) {
auto sender_infos =
track_media_info_map.GetVideoSenderInfos(*video_track);
if (!video_sender_infos) {
// There is no map entry on unconnected tracks.
// https://bugs.webrtc.org/8673
if (!sender_infos)
continue;
}
for (const auto& video_sender_info : *video_sender_infos) {
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats =
ProduceMediaStreamTrackStatsFromVideoSenderInfo(
timestamp_us, *video_track, *video_sender_info);
stream_stats->track_ids->push_back(video_track_stats->id());
report->AddStats(std::move(video_track_stats));
for (const auto& sender_info : *sender_infos) {
// SSRC must not be zero. https://bugs.webrtc.org/8694
RTC_DCHECK_NE(0, sender_info->ssrc());
stream_stats->track_ids->push_back(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
is_local, MediaStreamTrackInterface::kVideoKind,
video_track->id(), sender_info->ssrc()));
}
}
} else {
// Remote Audio Tracks
for (const rtc::scoped_refptr<AudioTrackInterface>& audio_track :
stream->GetAudioTracks()) {
const cricket::VoiceReceiverInfo* voice_receiver_info =
for (auto audio_track : stream->GetAudioTracks()) {
auto receiver_info =
track_media_info_map.GetVoiceReceiverInfo(*audio_track);
if (!voice_receiver_info) {
continue;
if (receiver_info) {
stream_stats->track_ids->push_back(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
is_local, MediaStreamTrackInterface::kAudioKind,
audio_track->id(), receiver_info->ssrc()));
}
std::unique_ptr<RTCMediaStreamTrackStats> audio_track_stats =
ProduceMediaStreamTrackStatsFromVoiceReceiverInfo(
timestamp_us, *audio_track, *voice_receiver_info);
stream_stats->track_ids->push_back(audio_track_stats->id());
report->AddStats(std::move(audio_track_stats));
}
// Remote Video Tracks
for (const rtc::scoped_refptr<VideoTrackInterface>& video_track :
stream->GetVideoTracks()) {
const cricket::VideoReceiverInfo* video_receiver_info =
for (auto video_track : stream->GetVideoTracks()) {
auto receiver_info =
track_media_info_map.GetVideoReceiverInfo(*video_track);
if (!video_receiver_info) {
continue;
if (receiver_info) {
stream_stats->track_ids->push_back(
RTCMediaStreamTrackStatsIDFromTrackKindIDAndSsrc(
is_local, MediaStreamTrackInterface::kVideoKind,
video_track->id(), receiver_info->ssrc()));
}
std::unique_ptr<RTCMediaStreamTrackStats> video_track_stats =
ProduceMediaStreamTrackStatsFromVideoReceiverInfo(
timestamp_us, *video_track, *video_receiver_info);
stream_stats->track_ids->push_back(video_track_stats->id());
report->AddStats(std::move(video_track_stats));
}
}
report->AddStats(std::move(stream_stats));
@ -897,7 +970,7 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_n(
// TODO(hbos): There could be other candidates that are not paired with
// anything. We don't have a complete list. Local candidates come from
// Port objects, and prflx candidates (both local and remote) are only
// stored in candidate pairs. crbug.com/632723
// stored in candidate pairs. https://crbug.com/632723
candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats(
timestamp_us, info.local_candidate, true, transport_id, report);
candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats(
@ -908,7 +981,7 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_n(
candidate_pair_stats->nominated = info.nominated;
// TODO(hbos): This writable is different than the spec. It goes to
// false after a certain amount of time without a response passes.
// crbug.com/633550
// https://crbug.com/633550
candidate_pair_stats->writable = info.writable;
candidate_pair_stats->bytes_sent =
static_cast<uint64_t>(info.sent_total_bytes);
@ -960,16 +1033,15 @@ void RTCStatsCollector::ProduceMediaStreamAndTrackStats_s(
int64_t timestamp_us, RTCStatsReport* report) const {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(track_media_info_map_);
ProduceMediaStreamAndTrackStats(timestamp_us,
*track_media_info_map_,
pc_->local_streams(),
true,
report);
ProduceMediaStreamAndTrackStats(timestamp_us,
*track_media_info_map_,
pc_->remote_streams(),
false,
report);
// TODO(bugs.webrtc.org/8674): Use the stream list updated by AddTrack
ProduceMediaStreamStats(timestamp_us, *track_media_info_map_,
pc_->local_streams(), true, report);
ProduceMediaStreamStats(timestamp_us, *track_media_info_map_,
pc_->remote_streams(), false, report);
ProduceSenderMediaTrackStats(timestamp_us, *track_media_info_map_,
pc_->GetSenders(), report);
ProduceReceiverMediaTrackStats(timestamp_us, *track_media_info_map_,
pc_->GetReceivers(), report);
}
void RTCStatsCollector::ProducePeerConnectionStats_s(

View File

@ -286,6 +286,7 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver {
std::unique_ptr<cricket::MediaEngineInterface>(media_engine_))),
pc_(pc_factory_) {
// Default return values for mocks.
EXPECT_CALL(pc_, GetCallStats()).WillRepeatedly(Return(Call::Stats()));
EXPECT_CALL(pc_, local_streams()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(pc_, remote_streams()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(pc_, GetSenders()).WillRepeatedly(Return(
@ -320,25 +321,32 @@ class RTCStatsCollectorTestHelper : public SetSessionDescriptionObserver {
void SetupLocalTrackAndSender(cricket::MediaType media_type,
const std::string& track_id,
uint32_t ssrc) {
uint32_t ssrc,
bool add_stream) {
rtc::scoped_refptr<StreamCollection> local_streams =
StreamCollection::Create();
EXPECT_CALL(pc_, local_streams())
.WillRepeatedly(Return(local_streams));
rtc::scoped_refptr<MediaStream> local_stream =
MediaStream::Create("LocalStreamLabel");
local_streams->AddStream(local_stream);
rtc::scoped_refptr<MediaStream> local_stream;
if (add_stream) {
local_stream = MediaStream::Create("LocalStreamLabel");
local_streams->AddStream(local_stream);
}
rtc::scoped_refptr<MediaStreamTrackInterface> track;
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
track = CreateFakeTrack(media_type, track_id,
MediaStreamTrackInterface::kLive);
local_stream->AddTrack(static_cast<AudioTrackInterface*>(track.get()));
if (add_stream) {
local_stream->AddTrack(static_cast<AudioTrackInterface*>(track.get()));
}
} else {
track = CreateFakeTrack(media_type, track_id,
MediaStreamTrackInterface::kLive);
local_stream->AddTrack(static_cast<VideoTrackInterface*>(track.get()));
if (add_stream) {
local_stream->AddTrack(static_cast<VideoTrackInterface*>(track.get()));
}
}
rtc::scoped_refptr<MockRtpSender> sender = CreateMockSender(track, ssrc);
@ -1987,8 +1995,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) {
kDefaultRtcpMuxRequired, kDefaultSrtpRequired);
voice_channel.set_transport_name_for_testing("TransportName");
test_->SetupLocalTrackAndSender(
cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", 1);
test_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO,
"LocalAudioTrackID", 1, true);
cricket::VoiceMediaInfo voice_media_info;
@ -2064,8 +2072,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) {
"VideoContentName", kDefaultRtcpMuxRequired, kDefaultSrtpRequired);
video_channel.set_transport_name_for_testing("TransportName");
test_->SetupLocalTrackAndSender(
cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", 1);
test_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_VIDEO,
"LocalVideoTrackID", 1, true);
cricket::VideoMediaInfo video_media_info;
@ -2315,6 +2323,104 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) {
report->Get(expected_rtcp_transport.id())->cast_to<RTCTransportStats>());
}
TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) {
// Emulates the case where AddTrack is used without an associated MediaStream
auto* voice_media_channel = new MockVoiceMediaChannel();
cricket::VoiceChannel voice_channel(
test_->worker_thread(), test_->network_thread(),
test_->signaling_thread(), test_->media_engine(),
rtc::WrapUnique(voice_media_channel), "VoiceContentName",
kDefaultRtcpMuxRequired, kDefaultSrtpRequired);
voice_channel.set_transport_name_for_testing("TransportName");
test_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO,
"LocalAudioTrackID", 1, false);
cricket::VoiceMediaInfo voice_media_info;
voice_media_info.senders.push_back(cricket::VoiceSenderInfo());
voice_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo());
voice_media_info.senders[0].local_stats[0].ssrc = 1;
voice_media_info.senders[0].packets_sent = 2;
voice_media_info.senders[0].bytes_sent = 3;
voice_media_info.senders[0].codec_payload_type = 42;
RtpCodecParameters codec_parameters;
codec_parameters.payload_type = 42;
codec_parameters.kind = cricket::MEDIA_TYPE_AUDIO;
codec_parameters.name = "dummy";
codec_parameters.clock_rate = 0;
voice_media_info.send_codecs.insert(
std::make_pair(codec_parameters.payload_type, codec_parameters));
EXPECT_CALL(*voice_media_channel, GetStats(_))
.WillOnce(DoAll(SetArgPointee<0>(voice_media_info), Return(true)));
SessionStats session_stats;
session_stats.transport_stats["TransportName"].transport_name =
"TransportName";
// Make sure the associated |RTCTransportStats| is created.
cricket::TransportChannelStats channel_stats;
channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP;
session_stats.transport_stats["TransportName"].channel_stats.push_back(
channel_stats);
EXPECT_CALL(test_->pc(), GetSessionStats(_))
.WillRepeatedly(Invoke([&session_stats](const ChannelNamePairs&) {
return std::unique_ptr<SessionStats>(new SessionStats(session_stats));
}));
EXPECT_CALL(test_->pc(), voice_channel())
.WillRepeatedly(Return(&voice_channel));
rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
RTCOutboundRTPStreamStats expected_audio("RTCOutboundRTPAudioStream_1",
report->timestamp_us());
expected_audio.ssrc = 1;
expected_audio.is_remote = false;
expected_audio.media_type = "audio";
expected_audio.track_id =
"RTCMediaStreamTrack_local_audio_LocalAudioTrackID_1";
expected_audio.transport_id =
"RTCTransport_TransportName_" +
rtc::ToString<>(cricket::ICE_CANDIDATE_COMPONENT_RTP);
expected_audio.codec_id = "RTCCodec_OutboundAudio_42";
expected_audio.packets_sent = 2;
expected_audio.bytes_sent = 3;
ASSERT_TRUE(report->Get(expected_audio.id()));
EXPECT_EQ(
report->Get(expected_audio.id())->cast_to<RTCOutboundRTPStreamStats>(),
expected_audio);
ASSERT_TRUE(report->Get(expected_audio.id()));
EXPECT_EQ(
report->Get(expected_audio.id())->cast_to<RTCOutboundRTPStreamStats>(),
expected_audio);
EXPECT_TRUE(report->Get(*expected_audio.track_id));
EXPECT_TRUE(report->Get(*expected_audio.transport_id));
EXPECT_TRUE(report->Get(*expected_audio.codec_id));
}
// When the PC has not had SetLocalDescription done, tracks all have
// SSRC 0, meaning "unconnected".
// We do not report stats on those tracks. https://bugs.webrtc.org/8673
TEST_F(RTCStatsCollectorTest, StatsNotReportedOnZeroSsrc) {
rtc::scoped_refptr<MediaStreamTrackInterface> track =
CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "audioTrack",
MediaStreamTrackInterface::kLive);
rtc::scoped_refptr<MockRtpSender> sender = CreateMockSender(track, 0);
EXPECT_CALL(test_->pc(), GetSenders())
.WillRepeatedly(
Return(std::vector<rtc::scoped_refptr<RtpSenderInterface>>(
{rtc::scoped_refptr<RtpSenderInterface>(sender.get())})));
rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
std::vector<const RTCMediaStreamTrackStats*> track_stats =
report->GetStatsOfType<RTCMediaStreamTrackStats>();
EXPECT_EQ(0, track_stats.size());
}
class RTCStatsCollectorTestWithFakeCollector : public testing::Test {
public:
RTCStatsCollectorTestWithFakeCollector()

View File

@ -135,6 +135,11 @@ TrackMediaInfoMap::TrackMediaInfoMap(
audio_track_by_sender_info_[&sender_info] = associated_track;
voice_infos_by_local_track_[associated_track].push_back(&sender_info);
}
if (sender_info.ssrc() == 0)
continue; // Unconnected SSRC. bugs.webrtc.org/8673
RTC_CHECK(voice_info_by_sender_ssrc_.count(sender_info.ssrc()) == 0)
<< "Duplicate voice sender SSRC: " << sender_info.ssrc();
voice_info_by_sender_ssrc_[sender_info.ssrc()] = &sender_info;
}
for (auto& receiver_info : voice_media_info_->receivers) {
AudioTrackInterface* associated_track =
@ -150,6 +155,9 @@ TrackMediaInfoMap::TrackMediaInfoMap(
audio_track_by_receiver_info_[&receiver_info] = unsignaled_audio_track;
voice_info_by_remote_track_[unsignaled_audio_track] = &receiver_info;
}
RTC_CHECK(voice_info_by_receiver_ssrc_.count(receiver_info.ssrc()) == 0)
<< "Duplicate voice receiver SSRC: " << receiver_info.ssrc();
voice_info_by_receiver_ssrc_[receiver_info.ssrc()] = &receiver_info;
}
}
if (video_media_info_) {
@ -162,6 +170,11 @@ TrackMediaInfoMap::TrackMediaInfoMap(
video_track_by_sender_info_[&sender_info] = associated_track;
video_infos_by_local_track_[associated_track].push_back(&sender_info);
}
if (sender_info.ssrc() == 0)
continue; // Unconnected SSRC. bugs.webrtc.org/8673
RTC_DCHECK(video_info_by_sender_ssrc_.count(sender_info.ssrc()) == 0)
<< "Duplicate video sender SSRC: " << sender_info.ssrc();
video_info_by_sender_ssrc_[sender_info.ssrc()] = &sender_info;
}
for (auto& receiver_info : video_media_info_->receivers) {
VideoTrackInterface* associated_track =
@ -177,6 +190,9 @@ TrackMediaInfoMap::TrackMediaInfoMap(
video_track_by_receiver_info_[&receiver_info] = unsignaled_video_track;
video_info_by_remote_track_[unsignaled_video_track] = &receiver_info;
}
RTC_DCHECK(video_info_by_receiver_ssrc_.count(receiver_info.ssrc()) == 0)
<< "Duplicate video receiver SSRC: " << receiver_info.ssrc();
video_info_by_receiver_ssrc_[receiver_info.ssrc()] = &receiver_info;
}
}
}
@ -203,6 +219,25 @@ const cricket::VideoReceiverInfo* TrackMediaInfoMap::GetVideoReceiverInfo(
return FindValueOrNull(video_info_by_remote_track_, &remote_video_track);
}
const cricket::VoiceSenderInfo* TrackMediaInfoMap::GetVoiceSenderInfoBySsrc(
uint32_t ssrc) const {
return FindValueOrNull(voice_info_by_sender_ssrc_, ssrc);
}
const cricket::VoiceReceiverInfo* TrackMediaInfoMap::GetVoiceReceiverInfoBySsrc(
uint32_t ssrc) const {
return FindValueOrNull(voice_info_by_receiver_ssrc_, ssrc);
}
const cricket::VideoSenderInfo* TrackMediaInfoMap::GetVideoSenderInfoBySsrc(
uint32_t ssrc) const {
return FindValueOrNull(video_info_by_sender_ssrc_, ssrc);
}
const cricket::VideoReceiverInfo* TrackMediaInfoMap::GetVideoReceiverInfoBySsrc(
uint32_t ssrc) const {
return FindValueOrNull(video_info_by_receiver_ssrc_, ssrc);
}
rtc::scoped_refptr<AudioTrackInterface> TrackMediaInfoMap::GetAudioTrack(
const cricket::VoiceSenderInfo& voice_sender_info) const {
return FindValueOrNull(audio_track_by_sender_info_, &voice_sender_info);

View File

@ -58,6 +58,13 @@ class TrackMediaInfoMap {
const cricket::VideoReceiverInfo* GetVideoReceiverInfo(
const VideoTrackInterface& remote_video_track) const;
const cricket::VoiceSenderInfo* GetVoiceSenderInfoBySsrc(uint32_t ssrc) const;
const cricket::VoiceReceiverInfo* GetVoiceReceiverInfoBySsrc(
uint32_t ssrc) const;
const cricket::VideoSenderInfo* GetVideoSenderInfoBySsrc(uint32_t ssrc) const;
const cricket::VideoReceiverInfo* GetVideoReceiverInfoBySsrc(
uint32_t ssrc) const;
rtc::scoped_refptr<AudioTrackInterface> GetAudioTrack(
const cricket::VoiceSenderInfo& voice_sender_info) const;
rtc::scoped_refptr<AudioTrackInterface> GetAudioTrack(
@ -95,6 +102,11 @@ class TrackMediaInfoMap {
std::map<const cricket::VideoReceiverInfo*,
rtc::scoped_refptr<VideoTrackInterface>>
video_track_by_receiver_info_;
// These maps map SSRCs to the corresponding voice or video info objects.
std::map<uint32_t, cricket::VoiceSenderInfo*> voice_info_by_sender_ssrc_;
std::map<uint32_t, cricket::VoiceReceiverInfo*> voice_info_by_receiver_ssrc_;
std::map<uint32_t, cricket::VideoSenderInfo*> video_info_by_sender_ssrc_;
std::map<uint32_t, cricket::VideoReceiverInfo*> video_info_by_receiver_ssrc_;
};
} // namespace webrtc

View File

@ -386,6 +386,20 @@ TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithSsrcNotUnique) {
remote_video_track_.get());
}
TEST_F(TrackMediaInfoMapTest, SsrcLookupFunction) {
AddRtpSenderWithSsrcs({1}, local_audio_track_);
AddRtpReceiverWithSsrcs({2}, remote_audio_track_);
AddRtpSenderWithSsrcs({3}, local_video_track_);
AddRtpReceiverWithSsrcs({4}, remote_video_track_);
CreateMap();
EXPECT_TRUE(map_->GetVoiceSenderInfoBySsrc(1));
EXPECT_TRUE(map_->GetVoiceReceiverInfoBySsrc(2));
EXPECT_TRUE(map_->GetVideoSenderInfoBySsrc(3));
EXPECT_TRUE(map_->GetVideoReceiverInfoBySsrc(4));
EXPECT_FALSE(map_->GetVoiceSenderInfoBySsrc(2));
EXPECT_FALSE(map_->GetVoiceSenderInfoBySsrc(1024));
}
// Death tests.
// Disabled on Android because death tests misbehave on Android, see
// base/test/gtest_util.h.