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:
parent
91fedfbedf
commit
8906187c86
@ -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
|
||||
|
||||
@ -5518,4 +5518,10 @@ void PeerConnection::DestroyBaseChannel(cricket::BaseChannel* channel) {
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnection::ClearStatsCache() {
|
||||
if (stats_collector_) {
|
||||
stats_collector_->ClearCachedStatsReport();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user