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}
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index b99df99..94f2db1 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -5518,4 +5518,10 @@
}
}
+void PeerConnection::ClearStatsCache() {
+ if (stats_collector_) {
+ stats_collector_->ClearCachedStatsReport();
+ }
+}
+
} // namespace webrtc
diff --git a/pc/peerconnection.h b/pc/peerconnection.h
index b9a86d8..66061f1 100644
--- a/pc/peerconnection.h
+++ b/pc/peerconnection.h
@@ -137,6 +137,7 @@
webrtc::MediaStreamTrackInterface* track,
StatsOutputLevel level) override;
void GetStats(RTCStatsCollectorCallback* callback) override;
+ void ClearStatsCache() override;
SignalingState signaling_state() override;
diff --git a/pc/peerconnectioninterface_unittest.cc b/pc/peerconnectioninterface_unittest.cc
index 6bff276..56c12e9 100644
--- a/pc/peerconnectioninterface_unittest.cc
+++ b/pc/peerconnectioninterface_unittest.cc
@@ -846,6 +846,15 @@
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 @@
// 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 @@
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 @@
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;
diff --git a/pc/rtcstatscollector.cc b/pc/rtcstatscollector.cc
index 24444f3..e777a26 100644
--- a/pc/rtcstatscollector.cc
+++ b/pc/rtcstatscollector.cc
@@ -207,7 +207,7 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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(
diff --git a/pc/rtcstatscollector_unittest.cc b/pc/rtcstatscollector_unittest.cc
index 7e4ad7f..2149695 100644
--- a/pc/rtcstatscollector_unittest.cc
+++ b/pc/rtcstatscollector_unittest.cc
@@ -286,6 +286,7 @@
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 @@
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 @@
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 @@
"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 @@
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()
diff --git a/pc/trackmediainfomap.cc b/pc/trackmediainfomap.cc
index 2a7fe9c..3fbe3fa 100644
--- a/pc/trackmediainfomap.cc
+++ b/pc/trackmediainfomap.cc
@@ -135,6 +135,11 @@
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 @@
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 @@
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 @@
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 @@
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);
diff --git a/pc/trackmediainfomap.h b/pc/trackmediainfomap.h
index acc0b14..fd9a98e 100644
--- a/pc/trackmediainfomap.h
+++ b/pc/trackmediainfomap.h
@@ -58,6 +58,13 @@
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 @@
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
diff --git a/pc/trackmediainfomap_unittest.cc b/pc/trackmediainfomap_unittest.cc
index 65e75c4..c05abe1 100644
--- a/pc/trackmediainfomap_unittest.cc
+++ b/pc/trackmediainfomap_unittest.cc
@@ -386,6 +386,20 @@
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.