diff --git a/api/stats/rtc_stats.h b/api/stats/rtc_stats.h index bb23490ce4..4361163576 100644 --- a/api/stats/rtc_stats.h +++ b/api/stats/rtc_stats.h @@ -179,6 +179,24 @@ class RTC_EXPORT RTCStats { return local_var_members_vec; \ } +// A version of WEBRTC_RTCSTATS_IMPL() where "..." is omitted, used to avoid a +// compile error on windows. This is used if the stats dictionary does not +// declare any members of its own (but perhaps its parent dictionary does). +#define WEBRTC_RTCSTATS_IMPL_NO_MEMBERS(this_class, parent_class, type_str) \ + const char this_class::kType[] = type_str; \ + \ + std::unique_ptr this_class::copy() const { \ + return std::unique_ptr(new this_class(*this)); \ + } \ + \ + const char* this_class::type() const { return this_class::kType; } \ + \ + std::vector \ + this_class::MembersOfThisObjectAndAncestors( \ + size_t local_var_additional_capacity) const { \ + return parent_class::MembersOfThisObjectAndAncestors(0); \ + } + // Non-standard stats members can be exposed to the JavaScript API in Chrome // e.g. through origin trials. The group ID can be used by the blink layer to // determine if a stats member should be exposed or not. Multiple non-standard diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 2e6cd3b9bb..cbcc8fa37d 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -279,6 +279,7 @@ class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats { ~RTCMediaStreamTrackStats() override; RTCStatsMember track_identifier; + RTCStatsMember media_source_id; RTCStatsMember remote_source; RTCStatsMember ended; // TODO(hbos): |RTCStatsCollector| does not return stats for detached tracks. @@ -450,6 +451,7 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { RTCOutboundRTPStreamStats(const RTCOutboundRTPStreamStats& other); ~RTCOutboundRTPStreamStats() override; + RTCStatsMember media_source_id; RTCStatsMember packets_sent; RTCStatsMember retransmitted_packets_sent; RTCStatsMember bytes_sent; @@ -466,6 +468,50 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { RTCStatsMember content_type; }; +// https://w3c.github.io/webrtc-stats/#dom-rtcmediasourcestats +class RTC_EXPORT RTCMediaSourceStats : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCMediaSourceStats(const RTCMediaSourceStats& other); + ~RTCMediaSourceStats() override; + + RTCStatsMember track_identifier; + RTCStatsMember kind; + + protected: + RTCMediaSourceStats(const std::string& id, int64_t timestamp_us); + RTCMediaSourceStats(std::string&& id, int64_t timestamp_us); +}; + +// https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats +class RTC_EXPORT RTCAudioSourceStats final : public RTCMediaSourceStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCAudioSourceStats(const std::string& id, int64_t timestamp_us); + RTCAudioSourceStats(std::string&& id, int64_t timestamp_us); + RTCAudioSourceStats(const RTCAudioSourceStats& other); + ~RTCAudioSourceStats() override; +}; + +// https://w3c.github.io/webrtc-stats/#dom-rtcvideosourcestats +class RTC_EXPORT RTCVideoSourceStats final : public RTCMediaSourceStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCVideoSourceStats(const std::string& id, int64_t timestamp_us); + RTCVideoSourceStats(std::string&& id, int64_t timestamp_us); + RTCVideoSourceStats(const RTCVideoSourceStats& other); + ~RTCVideoSourceStats() override; + + RTCStatsMember width; + RTCStatsMember height; + // TODO(hbos): Implement this metric. + RTCStatsMember frames; + RTCStatsMember frames_per_second; +}; + // https://w3c.github.io/webrtc-stats/#transportstats-dict* class RTC_EXPORT RTCTransportStats final : public RTCStats { public: diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 3e11a92061..9f82abe663 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -34,6 +34,7 @@ namespace webrtc { namespace { +// TODO(https://crbug.com/webrtc/10656): Consider making IDs less predictable. std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { return "RTCCertificate_" + fingerprint; } @@ -91,6 +92,16 @@ std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { return sb.str(); } +std::string RTCMediaSourceStatsIDFromKindAndAttachment( + cricket::MediaType media_type, + int attachment_id) { + char buf[1024]; + rtc::SimpleStringBuilder sb(buf); + sb << "RTC" << (media_type == cricket::MEDIA_TYPE_AUDIO ? "Audio" : "Video") + << "Source_" << attachment_id; + return sb.str(); +} + const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { if (type == cricket::LOCAL_PORT_TYPE) return RTCIceCandidateType::kHost; @@ -439,6 +450,9 @@ ProduceMediaStreamTrackStatsFromVoiceSenderInfo( timestamp_us, RTCMediaStreamTrackKind::kAudio)); SetMediaStreamTrackStatsFromMediaStreamTrackInterface( audio_track, audio_track_stats.get()); + audio_track_stats->media_source_id = + RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_AUDIO, + attachment_id); audio_track_stats->remote_source = false; audio_track_stats->detached = false; if (voice_sender_info.audio_level >= 0) { @@ -524,11 +538,13 @@ ProduceMediaStreamTrackStatsFromVideoSenderInfo( std::unique_ptr video_track_stats( new RTCMediaStreamTrackStats( RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(kSender, - attachment_id), timestamp_us, RTCMediaStreamTrackKind::kVideo)); SetMediaStreamTrackStatsFromMediaStreamTrackInterface( video_track, video_track_stats.get()); + video_track_stats->media_source_id = + RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_VIDEO, + attachment_id); video_track_stats->remote_source = false; video_track_stats->detached = false; video_track_stats->frame_width = static_cast( @@ -930,6 +946,7 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThreadImpl( ProduceDataChannelStats_s(timestamp_us, partial_report); ProduceMediaStreamStats_s(timestamp_us, partial_report); ProduceMediaStreamTrackStats_s(timestamp_us, partial_report); + ProduceMediaSourceStats_s(timestamp_us, partial_report); ProducePeerConnectionStats_s(timestamp_us, partial_report); } @@ -1265,6 +1282,64 @@ void RTCStatsCollector::ProduceMediaStreamTrackStats_s( } } +void RTCStatsCollector::ProduceMediaSourceStats_s( + int64_t timestamp_us, + RTCStatsReport* report) const { + RTC_DCHECK(signaling_thread_->IsCurrent()); + for (const RtpTransceiverStatsInfo& transceiver_stats_info : + transceiver_stats_infos_) { + const auto& track_media_info_map = + transceiver_stats_info.track_media_info_map; + for (const auto& sender : transceiver_stats_info.transceiver->senders()) { + const auto& sender_internal = sender->internal(); + const auto& track = sender_internal->track(); + if (!track) + continue; + // TODO(hbos): The same track could be attached to multiple senders which + // should result in multiple senders referencing the same media source + // stats. When all media source related metrics are moved to the track's + // source (e.g. input frame rate is moved from cricket::VideoSenderInfo to + // VideoTrackSourceInterface::Stats), don't create separate media source + // stats objects on a per-attachment basis. + std::unique_ptr media_source_stats; + if (track->kind() == MediaStreamTrackInterface::kAudioKind) { + media_source_stats = absl::make_unique( + RTCMediaSourceStatsIDFromKindAndAttachment( + cricket::MEDIA_TYPE_AUDIO, sender_internal->AttachmentId()), + timestamp_us); + } else { + RTC_DCHECK_EQ(MediaStreamTrackInterface::kVideoKind, track->kind()); + auto video_source_stats = absl::make_unique( + RTCMediaSourceStatsIDFromKindAndAttachment( + cricket::MEDIA_TYPE_VIDEO, sender_internal->AttachmentId()), + timestamp_us); + auto* video_track = static_cast(track.get()); + auto* video_source = video_track->GetSource(); + VideoTrackSourceInterface::Stats source_stats; + if (video_source && video_source->GetStats(&source_stats)) { + video_source_stats->width = source_stats.input_width; + video_source_stats->height = source_stats.input_height; + } + // TODO(hbos): Source stats should not depend on whether or not we are + // connected/have an SSRC assigned. Related to + // https://crbug.com/webrtc/8694 (using ssrc 0 to indicate "none"). + if (sender_internal->ssrc() != 0) { + auto* sender_info = track_media_info_map->GetVideoSenderInfoBySsrc( + sender_internal->ssrc()); + if (sender_info) { + video_source_stats->frames_per_second = + sender_info->framerate_input; + } + } + media_source_stats = std::move(video_source_stats); + } + media_source_stats->track_identifier = track->id(); + media_source_stats->kind = track->kind(); + report->AddStats(std::move(media_source_stats)); + } + } +} + void RTCStatsCollector::ProducePeerConnectionStats_s( int64_t timestamp_us, RTCStatsReport* report) const { RTC_DCHECK(signaling_thread_->IsCurrent()); @@ -1340,10 +1415,14 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( rtc::scoped_refptr audio_track = track_media_info_map.GetAudioTrack(voice_sender_info); if (audio_track) { + int attachment_id = + track_media_info_map.GetAttachmentIdByTrack(audio_track).value(); outbound_audio->track_id = - RTCMediaStreamTrackStatsIDFromDirectionAndAttachment( - kSender, - track_media_info_map.GetAttachmentIdByTrack(audio_track).value()); + RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(kSender, + attachment_id); + outbound_audio->media_source_id = + RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_AUDIO, + attachment_id); } outbound_audio->transport_id = transport_id; report->AddStats(std::move(outbound_audio)); @@ -1397,10 +1476,14 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( rtc::scoped_refptr video_track = track_media_info_map.GetVideoTrack(video_sender_info); if (video_track) { + int attachment_id = + track_media_info_map.GetAttachmentIdByTrack(video_track).value(); outbound_video->track_id = - RTCMediaStreamTrackStatsIDFromDirectionAndAttachment( - kSender, - track_media_info_map.GetAttachmentIdByTrack(video_track).value()); + RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(kSender, + attachment_id); + outbound_video->media_source_id = + RTCMediaSourceStatsIDFromKindAndAttachment(cricket::MEDIA_TYPE_VIDEO, + attachment_id); } outbound_video->transport_id = transport_id; report->AddStats(std::move(outbound_video)); diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h index 4837fc0abe..82501db038 100644 --- a/pc/rtc_stats_collector.h +++ b/pc/rtc_stats_collector.h @@ -182,6 +182,10 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface, // Produces |RTCMediaStreamTrackStats|. void ProduceMediaStreamTrackStats_s(int64_t timestamp_us, RTCStatsReport* report) const; + // Produces RTCMediaSourceStats, including RTCAudioSourceStats and + // RTCVideoSourceStats. + void ProduceMediaSourceStats_s(int64_t timestamp_us, + RTCStatsReport* report) const; // Produces |RTCPeerConnectionStats|. void ProducePeerConnectionStats_s(int64_t timestamp_us, RTCStatsReport* report) const; diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 78530dfd63..6cd63196a7 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -89,6 +89,14 @@ void PrintTo(const RTCOutboundRTPStreamStats& stats, ::std::ostream* os) { *os << stats.ToJson(); } +void PrintTo(const RTCAudioSourceStats& stats, ::std::ostream* os) { + *os << stats.ToJson(); +} + +void PrintTo(const RTCVideoSourceStats& stats, ::std::ostream* os) { + *os << stats.ToJson(); +} + void PrintTo(const RTCTransportStats& stats, ::std::ostream* os) { *os << stats.ToJson(); } @@ -195,19 +203,62 @@ class FakeAudioTrackForStats : public MediaStreamTrack { } }; +class FakeVideoTrackSourceForStats : public VideoTrackSourceInterface { + public: + static rtc::scoped_refptr Create( + int input_width, + int input_height) { + return rtc::scoped_refptr( + new rtc::RefCountedObject(input_width, + input_height)); + } + + FakeVideoTrackSourceForStats(int input_width, int input_height) + : input_width_(input_width), input_height_(input_height) {} + ~FakeVideoTrackSourceForStats() override {} + + // VideoTrackSourceInterface + bool is_screencast() const override { return false; } + absl::optional needs_denoising() const override { return false; } + bool GetStats(VideoTrackSourceInterface::Stats* stats) override { + stats->input_width = input_width_; + stats->input_height = input_height_; + return true; + } + // MediaSourceInterface (part of VideoTrackSourceInterface) + MediaSourceInterface::SourceState state() const override { + return MediaSourceInterface::SourceState::kLive; + } + bool remote() const override { return false; } + // NotifierInterface (part of MediaSourceInterface) + void RegisterObserver(ObserverInterface* observer) override {} + void UnregisterObserver(ObserverInterface* observer) override {} + // rtc::VideoSourceInterface (part of VideoTrackSourceInterface) + void AddOrUpdateSink(rtc::VideoSinkInterface* sink, + const rtc::VideoSinkWants& wants) override {} + void RemoveSink(rtc::VideoSinkInterface* sink) override {} + + private: + int input_width_; + int input_height_; +}; + class FakeVideoTrackForStats : public MediaStreamTrack { public: static rtc::scoped_refptr Create( const std::string& id, - MediaStreamTrackInterface::TrackState state) { + MediaStreamTrackInterface::TrackState state, + rtc::scoped_refptr source) { rtc::scoped_refptr video_track( - new rtc::RefCountedObject(id)); + new rtc::RefCountedObject(id, + std::move(source))); video_track->set_state(state); return video_track; } - explicit FakeVideoTrackForStats(const std::string& id) - : MediaStreamTrack(id) {} + FakeVideoTrackForStats(const std::string& id, + rtc::scoped_refptr source) + : MediaStreamTrack(id), source_(source) {} std::string kind() const override { return MediaStreamTrackInterface::kVideoKind; @@ -217,7 +268,12 @@ class FakeVideoTrackForStats : public MediaStreamTrack { const rtc::VideoSinkWants& wants) override {} void RemoveSink(rtc::VideoSinkInterface* sink) override {} - VideoTrackSourceInterface* GetSource() const override { return nullptr; } + VideoTrackSourceInterface* GetSource() const override { + return source_.get(); + } + + private: + rtc::scoped_refptr source_; }; rtc::scoped_refptr CreateFakeTrack( @@ -228,24 +284,26 @@ rtc::scoped_refptr CreateFakeTrack( return FakeAudioTrackForStats::Create(track_id, track_state); } else { RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); - return FakeVideoTrackForStats::Create(track_id, track_state); + return FakeVideoTrackForStats::Create(track_id, track_state, nullptr); } } rtc::scoped_refptr CreateMockSender( - const rtc::scoped_refptr& track, + cricket::MediaType media_type, + rtc::scoped_refptr track, uint32_t ssrc, int attachment_id, std::vector local_stream_ids) { + RTC_DCHECK(!track || + (track->kind() == MediaStreamTrackInterface::kAudioKind && + media_type == cricket::MEDIA_TYPE_AUDIO) || + (track->kind() == MediaStreamTrackInterface::kVideoKind && + media_type == cricket::MEDIA_TYPE_VIDEO)); rtc::scoped_refptr sender( new rtc::RefCountedObject()); EXPECT_CALL(*sender, track()).WillRepeatedly(Return(track)); EXPECT_CALL(*sender, ssrc()).WillRepeatedly(Return(ssrc)); - EXPECT_CALL(*sender, media_type()) - .WillRepeatedly( - Return(track->kind() == MediaStreamTrackInterface::kAudioKind - ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO)); + EXPECT_CALL(*sender, media_type()).WillRepeatedly(Return(media_type)); EXPECT_CALL(*sender, GetParameters()).WillRepeatedly(Invoke([ssrc]() { RtpParameters params; params.encodings.push_back(RtpEncodingParameters()); @@ -325,7 +383,8 @@ class RTCStatsCollectorWrapper { cricket::MediaType media_type, const std::string& track_id, uint32_t ssrc, - bool add_stream) { + bool add_stream, + int attachment_id) { rtc::scoped_refptr local_stream; if (add_stream) { local_stream = MediaStream::Create("LocalStreamId"); @@ -348,7 +407,7 @@ class RTCStatsCollectorWrapper { } rtc::scoped_refptr sender = - CreateMockSender(track, ssrc, 50, {}); + CreateMockSender(media_type, track, ssrc, attachment_id, {}); pc_->AddSender(sender); return sender; } @@ -388,6 +447,7 @@ class RTCStatsCollectorWrapper { // |[Voice/Video][Sender/Receiver]Info| and their SSRCs. Local tracks can be // associated with multiple |[Voice/Video]SenderInfo|s, remote tracks can only // be associated with one |[Voice/Video]ReceiverInfo|. + // Senders get assigned attachment ID "ssrc + 10". void CreateMockRtpSendersReceiversAndChannels( std::initializer_list< std::pair> @@ -407,7 +467,6 @@ class RTCStatsCollectorWrapper { cricket::VideoMediaInfo video_media_info; // Local audio tracks and voice sender infos - int attachment_id = 147; for (auto& pair : local_audio_track_info_pairs) { MediaStreamTrackInterface* local_audio_track = pair.first; const cricket::VoiceSenderInfo& voice_sender_info = pair.second; @@ -416,14 +475,14 @@ class RTCStatsCollectorWrapper { voice_media_info.senders.push_back(voice_sender_info); rtc::scoped_refptr rtp_sender = CreateMockSender( + cricket::MEDIA_TYPE_AUDIO, rtc::scoped_refptr(local_audio_track), - voice_sender_info.local_stats[0].ssrc, attachment_id++, - local_stream_ids); + voice_sender_info.local_stats[0].ssrc, + voice_sender_info.local_stats[0].ssrc + 10, local_stream_ids); pc_->AddSender(rtp_sender); } // Remote audio tracks and voice receiver infos - attachment_id = 181; for (auto& pair : remote_audio_track_info_pairs) { MediaStreamTrackInterface* remote_audio_track = pair.first; const cricket::VoiceReceiverInfo& voice_receiver_info = pair.second; @@ -434,14 +493,14 @@ class RTCStatsCollectorWrapper { rtc::scoped_refptr rtp_receiver = CreateMockReceiver( rtc::scoped_refptr(remote_audio_track), - voice_receiver_info.local_stats[0].ssrc, attachment_id++); + voice_receiver_info.local_stats[0].ssrc, + voice_receiver_info.local_stats[0].ssrc + 10); EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); pc_->AddReceiver(rtp_receiver); } // Local video tracks and video sender infos - attachment_id = 151; for (auto& pair : local_video_track_info_pairs) { MediaStreamTrackInterface* local_video_track = pair.first; const cricket::VideoSenderInfo& video_sender_info = pair.second; @@ -450,14 +509,14 @@ class RTCStatsCollectorWrapper { video_media_info.senders.push_back(video_sender_info); rtc::scoped_refptr rtp_sender = CreateMockSender( + cricket::MEDIA_TYPE_VIDEO, rtc::scoped_refptr(local_video_track), - video_sender_info.local_stats[0].ssrc, attachment_id++, - local_stream_ids); + video_sender_info.local_stats[0].ssrc, + video_sender_info.local_stats[0].ssrc + 10, local_stream_ids); pc_->AddSender(rtp_sender); } // Remote video tracks and video receiver infos - attachment_id = 191; for (auto& pair : remote_video_track_info_pairs) { MediaStreamTrackInterface* remote_video_track = pair.first; const cricket::VideoReceiverInfo& video_receiver_info = pair.second; @@ -468,7 +527,8 @@ class RTCStatsCollectorWrapper { rtc::scoped_refptr rtp_receiver = CreateMockReceiver( rtc::scoped_refptr(remote_video_track), - video_receiver_info.local_stats[0].ssrc, attachment_id++); + video_receiver_info.local_stats[0].ssrc, + video_receiver_info.local_stats[0].ssrc + 10); EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); pc_->AddReceiver(rtp_receiver); @@ -536,6 +596,7 @@ class RTCStatsCollectorTest : public ::testing::Test { std::string receiver_track_id; std::string remote_stream_id; std::string peer_connection_id; + std::string media_source_id; }; // Sets up the example stats graph (see ASCII art below) used for testing the @@ -583,7 +644,7 @@ class RTCStatsCollectorTest : public ::testing::Test { video_media_channel->SetStats(video_media_info); // track (sender) graph.sender = stats_->SetupLocalTrackAndSender( - cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", 3, false); + cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", 3, false, 50); graph.sender_track_id = "RTCMediaStreamTrack_sender_" + rtc::ToString(graph.sender->AttachmentId()); // track (receiver) and stream (remote stream) @@ -594,20 +655,25 @@ class RTCStatsCollectorTest : public ::testing::Test { graph.remote_stream_id = "RTCMediaStream_RemoteStreamId"; // peer-connection graph.peer_connection_id = "RTCPeerConnection"; + // media-source (kind: video) + graph.media_source_id = + "RTCVideoSource_" + rtc::ToString(graph.sender->AttachmentId()); // Expected stats graph: // - // track (sender) stream (remote stream) ---> track (receiver) - // ^ ^ - // | | - // outbound-rtp inbound-rtp ---------------+ - // | | | | - // v v v v - // codec (send) transport codec (recv) peer-connection + // +--- track (sender) stream (remote stream) ---> track (receiver) + // | ^ ^ + // | | | + // | +--------- outbound-rtp inbound-rtp ---------------+ + // | | | | | | + // | | v v v v + // | | codec (send) transport codec (recv) peer-connection + // v v + // media-source // Verify the stats graph is set up correctly. graph.full_report = stats_->GetStatsReport(); - EXPECT_EQ(graph.full_report->size(), 9u); + EXPECT_EQ(graph.full_report->size(), 10u); EXPECT_TRUE(graph.full_report->Get(graph.send_codec_id)); EXPECT_TRUE(graph.full_report->Get(graph.recv_codec_id)); EXPECT_TRUE(graph.full_report->Get(graph.outbound_rtp_id)); @@ -617,8 +683,13 @@ class RTCStatsCollectorTest : public ::testing::Test { EXPECT_TRUE(graph.full_report->Get(graph.receiver_track_id)); EXPECT_TRUE(graph.full_report->Get(graph.remote_stream_id)); EXPECT_TRUE(graph.full_report->Get(graph.peer_connection_id)); + EXPECT_TRUE(graph.full_report->Get(graph.media_source_id)); + const auto& sender_track = graph.full_report->Get(graph.sender_track_id) + ->cast_to(); + EXPECT_EQ(*sender_track.media_source_id, graph.media_source_id); const auto& outbound_rtp = graph.full_report->Get(graph.outbound_rtp_id) ->cast_to(); + EXPECT_EQ(*outbound_rtp.media_source_id, graph.media_source_id); EXPECT_EQ(*outbound_rtp.codec_id, graph.send_codec_id); EXPECT_EQ(*outbound_rtp.track_id, graph.sender_track_id); EXPECT_EQ(*outbound_rtp.transport_id, graph.transport_id); @@ -1386,6 +1457,8 @@ TEST_F(RTCStatsCollectorTest, IdForType(report), report->timestamp_us(), RTCMediaStreamTrackKind::kAudio); expected_local_audio_track_ssrc1.track_identifier = local_audio_track->id(); + expected_local_audio_track_ssrc1.media_source_id = + "RTCAudioSource_11"; // Attachment ID = SSRC + 10 expected_local_audio_track_ssrc1.remote_source = false; expected_local_audio_track_ssrc1.ended = true; expected_local_audio_track_ssrc1.detached = false; @@ -1457,6 +1530,8 @@ TEST_F(RTCStatsCollectorTest, IdForType(report), report->timestamp_us(), RTCMediaStreamTrackKind::kAudio); expected_remote_audio_track.track_identifier = remote_audio_track->id(); + // |expected_remote_audio_track.media_source_id| should be undefined + // because the track is remote. expected_remote_audio_track.remote_source = true; expected_remote_audio_track.ended = false; expected_remote_audio_track.detached = false; @@ -1530,6 +1605,8 @@ TEST_F(RTCStatsCollectorTest, stats_of_track_type[0]->id(), report->timestamp_us(), RTCMediaStreamTrackKind::kVideo); expected_local_video_track_ssrc1.track_identifier = local_video_track->id(); + expected_local_video_track_ssrc1.media_source_id = + "RTCVideoSource_11"; // Attachment ID = SSRC + 10 expected_local_video_track_ssrc1.remote_source = false; expected_local_video_track_ssrc1.ended = false; expected_local_video_track_ssrc1.detached = false; @@ -1601,6 +1678,8 @@ TEST_F(RTCStatsCollectorTest, RTCMediaStreamTrackKind::kVideo); expected_remote_video_track_ssrc3.track_identifier = remote_video_track_ssrc3->id(); + // |expected_remote_video_track_ssrc3.media_source_id| should be undefined + // because the track is remote. expected_remote_video_track_ssrc3.remote_source = true; expected_remote_video_track_ssrc3.ended = true; expected_remote_video_track_ssrc3.detached = false; @@ -1801,12 +1880,14 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); voice_media_channel->SetStats(voice_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO, - "LocalAudioTrackID", 1, true); + "LocalAudioTrackID", 1, true, + /*attachment_id=*/50); rtc::scoped_refptr report = stats_->GetStatsReport(); RTCOutboundRTPStreamStats expected_audio("RTCOutboundRTPAudioStream_1", report->timestamp_us()); + expected_audio.media_source_id = "RTCAudioSource_50"; expected_audio.ssrc = 1; expected_audio.is_remote = false; expected_audio.media_type = "audio"; @@ -1865,7 +1946,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); video_media_channel->SetStats(video_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_VIDEO, - "LocalVideoTrackID", 1, true); + "LocalVideoTrackID", 1, true, + /*attachment_id=*/50); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -1876,6 +1958,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { RTCOutboundRTPStreamStats expected_video(stats_of_my_type[0]->id(), report->timestamp_us()); + expected_video.media_source_id = "RTCVideoSource_50"; expected_video.ssrc = 1; expected_video.is_remote = false; expected_video.media_type = "video"; @@ -2081,12 +2164,14 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) { auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); voice_media_channel->SetStats(voice_media_info); stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO, - "LocalAudioTrackID", 1, false); + "LocalAudioTrackID", 1, false, + /*attachment_id=*/50); rtc::scoped_refptr report = stats_->GetStatsReport(); RTCOutboundRTPStreamStats expected_audio("RTCOutboundRTPAudioStream_1", report->timestamp_us()); + expected_audio.media_source_id = "RTCAudioSource_50"; expected_audio.ssrc = 1; expected_audio.is_remote = false; expected_audio.media_type = "audio"; @@ -2108,22 +2193,194 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) { EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } +TEST_F(RTCStatsCollectorTest, RTCAudioSourceStatsCollectedForSenderWithTrack) { + const uint32_t kSsrc = 4; + const int kAttachmentId = 42; + + 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 = kSsrc; + auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); + voice_media_channel->SetStats(voice_media_info); + stats_->SetupLocalTrackAndSender(cricket::MEDIA_TYPE_AUDIO, + "LocalAudioTrackID", kSsrc, false, + kAttachmentId); + + rtc::scoped_refptr report = stats_->GetStatsReport(); + + RTCAudioSourceStats expected_audio("RTCAudioSource_42", + report->timestamp_us()); + expected_audio.track_identifier = "LocalAudioTrackID"; + expected_audio.kind = "audio"; + + ASSERT_TRUE(report->Get(expected_audio.id())); + EXPECT_EQ(report->Get(expected_audio.id())->cast_to(), + expected_audio); +} + +TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { + const uint32_t kSsrc = 4; + const int kAttachmentId = 42; + const int kVideoSourceWidth = 12; + const int kVideoSourceHeight = 34; + + cricket::VideoMediaInfo video_media_info; + video_media_info.senders.push_back(cricket::VideoSenderInfo()); + video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); + video_media_info.senders[0].local_stats[0].ssrc = kSsrc; + video_media_info.senders[0].framerate_input = 29; + auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); + video_media_channel->SetStats(video_media_info); + + auto video_source = FakeVideoTrackSourceForStats::Create(kVideoSourceWidth, + kVideoSourceHeight); + auto video_track = FakeVideoTrackForStats::Create( + "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); + rtc::scoped_refptr sender = CreateMockSender( + cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {}); + pc_->AddSender(sender); + + rtc::scoped_refptr report = stats_->GetStatsReport(); + + RTCVideoSourceStats expected_video("RTCVideoSource_42", + report->timestamp_us()); + expected_video.track_identifier = "LocalVideoTrackID"; + expected_video.kind = "video"; + expected_video.width = kVideoSourceWidth; + expected_video.height = kVideoSourceHeight; + // |expected_video.frames| is expected to be undefined because it is not set. + // TODO(hbos): When implemented, set its expected value here. + expected_video.frames_per_second = 29; + + ASSERT_TRUE(report->Get(expected_video.id())); + EXPECT_EQ(report->Get(expected_video.id())->cast_to(), + expected_video); +} + +// This test exercises the current behavior and code path, but the correct +// behavior is to report frame rate even if we have no SSRC. +// TODO(hbos): When we know the frame rate even if we have no SSRC, update the +// expectations of this test. +TEST_F(RTCStatsCollectorTest, + RTCVideoSourceStatsMissingFrameRateWhenSenderHasNoSsrc) { + // TODO(https://crbug.com/webrtc/8694): When 0 is no longer a magic value for + // "none", update this test. + const uint32_t kNoSsrc = 0; + const int kAttachmentId = 42; + const int kVideoSourceWidth = 12; + const int kVideoSourceHeight = 34; + + cricket::VideoMediaInfo video_media_info; + video_media_info.senders.push_back(cricket::VideoSenderInfo()); + video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); + video_media_info.senders[0].framerate_input = 29; + auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); + video_media_channel->SetStats(video_media_info); + + auto video_source = FakeVideoTrackSourceForStats::Create(kVideoSourceWidth, + kVideoSourceHeight); + auto video_track = FakeVideoTrackForStats::Create( + "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); + rtc::scoped_refptr sender = CreateMockSender( + cricket::MEDIA_TYPE_VIDEO, video_track, kNoSsrc, kAttachmentId, {}); + pc_->AddSender(sender); + + rtc::scoped_refptr report = stats_->GetStatsReport(); + ASSERT_TRUE(report->Get("RTCVideoSource_42")); + auto video_stats = + report->Get("RTCVideoSource_42")->cast_to(); + EXPECT_FALSE(video_stats.frames_per_second.is_defined()); +} + +// The track not having a source is not expected to be true in practise, but +// this is true in some tests relying on fakes. This test covers that code path. +TEST_F(RTCStatsCollectorTest, + RTCVideoSourceStatsMissingResolutionWhenTrackHasNoSource) { + const uint32_t kSsrc = 4; + const int kAttachmentId = 42; + + cricket::VideoMediaInfo video_media_info; + video_media_info.senders.push_back(cricket::VideoSenderInfo()); + video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); + video_media_info.senders[0].local_stats[0].ssrc = kSsrc; + video_media_info.senders[0].framerate_input = 29; + auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); + video_media_channel->SetStats(video_media_info); + + auto video_track = FakeVideoTrackForStats::Create( + "LocalVideoTrackID", MediaStreamTrackInterface::kLive, + /*source=*/nullptr); + rtc::scoped_refptr sender = CreateMockSender( + cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {}); + pc_->AddSender(sender); + + rtc::scoped_refptr report = stats_->GetStatsReport(); + ASSERT_TRUE(report->Get("RTCVideoSource_42")); + auto video_stats = + report->Get("RTCVideoSource_42")->cast_to(); + EXPECT_FALSE(video_stats.width.is_defined()); + EXPECT_FALSE(video_stats.height.is_defined()); +} + +TEST_F(RTCStatsCollectorTest, + RTCAudioSourceStatsNotCollectedForSenderWithoutTrack) { + const uint32_t kSsrc = 4; + const int kAttachmentId = 42; + + 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 = kSsrc; + auto* voice_media_channel = pc_->AddVoiceChannel("AudioMid", "TransportName"); + voice_media_channel->SetStats(voice_media_info); + rtc::scoped_refptr sender = CreateMockSender( + cricket::MEDIA_TYPE_AUDIO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); + pc_->AddSender(sender); + + rtc::scoped_refptr report = stats_->GetStatsReport(); + EXPECT_FALSE(report->Get("RTCAudioSource_42")); +} + +TEST_F(RTCStatsCollectorTest, + RTCVideoSourceStatsNotCollectedForSenderWithoutTrack) { + const uint32_t kSsrc = 4; + const int kAttachmentId = 42; + + cricket::VideoMediaInfo video_media_info; + video_media_info.senders.push_back(cricket::VideoSenderInfo()); + video_media_info.senders[0].local_stats.push_back(cricket::SsrcSenderInfo()); + video_media_info.senders[0].local_stats[0].ssrc = kSsrc; + video_media_info.senders[0].framerate_input = 29; + auto* video_media_channel = pc_->AddVideoChannel("VideoMid", "TransportName"); + video_media_channel->SetStats(video_media_info); + + rtc::scoped_refptr sender = CreateMockSender( + cricket::MEDIA_TYPE_VIDEO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); + pc_->AddSender(sender); + + rtc::scoped_refptr report = stats_->GetStatsReport(); + EXPECT_FALSE(report->Get("RTCVideoSource_42")); +} + TEST_F(RTCStatsCollectorTest, GetStatsWithSenderSelector) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); // Expected stats graph when filtered by sender: // - // track (sender) - // ^ - // | - // outbound-rtp - // | | - // v v - // codec (send) transport + // +--- track (sender) + // | ^ + // | | + // | +--------- outbound-rtp + // | | | | + // | | v v + // | | codec (send) transport + // v v + // media-source rtc::scoped_refptr sender_report = stats_->GetStatsReportWithSenderSelector(graph.sender); EXPECT_TRUE(sender_report); EXPECT_EQ(sender_report->timestamp_us(), graph.full_report->timestamp_us()); - EXPECT_EQ(sender_report->size(), 4u); + EXPECT_EQ(sender_report->size(), 5u); EXPECT_TRUE(sender_report->Get(graph.send_codec_id)); EXPECT_FALSE(sender_report->Get(graph.recv_codec_id)); EXPECT_TRUE(sender_report->Get(graph.outbound_rtp_id)); @@ -2133,19 +2390,20 @@ TEST_F(RTCStatsCollectorTest, GetStatsWithSenderSelector) { EXPECT_FALSE(sender_report->Get(graph.receiver_track_id)); EXPECT_FALSE(sender_report->Get(graph.remote_stream_id)); EXPECT_FALSE(sender_report->Get(graph.peer_connection_id)); + EXPECT_TRUE(sender_report->Get(graph.media_source_id)); } TEST_F(RTCStatsCollectorTest, GetStatsWithReceiverSelector) { ExampleStatsGraph graph = SetupExampleStatsGraphForSelectorTests(); // Expected stats graph when filtered by receiver: // - // track (receiver) - // ^ - // | - // inbound-rtp ---------------+ - // | | - // v v - // transport codec (recv) + // track (receiver) + // ^ + // | + // inbound-rtp ---------------+ + // | | + // v v + // transport codec (recv) rtc::scoped_refptr receiver_report = stats_->GetStatsReportWithReceiverSelector(graph.receiver); EXPECT_TRUE(receiver_report); @@ -2160,6 +2418,7 @@ TEST_F(RTCStatsCollectorTest, GetStatsWithReceiverSelector) { EXPECT_TRUE(receiver_report->Get(graph.receiver_track_id)); EXPECT_FALSE(receiver_report->Get(graph.remote_stream_id)); EXPECT_FALSE(receiver_report->Get(graph.peer_connection_id)); + EXPECT_FALSE(receiver_report->Get(graph.media_source_id)); } TEST_F(RTCStatsCollectorTest, GetStatsWithNullSenderSelector) { @@ -2188,7 +2447,7 @@ TEST_F(RTCStatsCollectorTest, StatsReportedOnZeroSsrc) { CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "audioTrack", MediaStreamTrackInterface::kLive); rtc::scoped_refptr sender = - CreateMockSender(track, 0, 49, {}); + CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 0, 49, {}); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2207,7 +2466,7 @@ TEST_F(RTCStatsCollectorTest, DoNotCrashOnSsrcChange) { CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "audioTrack", MediaStreamTrackInterface::kLive); rtc::scoped_refptr sender = - CreateMockSender(track, 4711, 49, {}); + CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 4711, 49, {}); pc_->AddSender(sender); // We do not generate any matching voice_sender_info stats. diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index 438b47b160..10b17e5986 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -317,8 +317,8 @@ class RTCStatsVerifier { EXPECT_TRUE(valid_reference) << stats_->type() << "." << member.name() << " is not a reference to an " - << "existing dictionary of type " << expected_type << " (" - << member.ValueToString() << ")."; + << "existing dictionary of type " << expected_type << " (value: " + << (member.is_defined() ? member.ValueToString() : "null") << ")."; MarkMemberTested(member, valid_reference); } @@ -394,6 +394,18 @@ class RTCStatsReportVerifier { } else if (stats.type() == RTCOutboundRTPStreamStats::kType) { verify_successful &= VerifyRTCOutboundRTPStreamStats( stats.cast_to()); + } else if (stats.type() == RTCAudioSourceStats::kType) { + // RTCAudioSourceStats::kType and RTCVideoSourceStats::kType both have + // the value "media-source", but they are distinguishable with pointer + // equality (==). In JavaScript they would be distinguished with |kind|. + verify_successful &= + VerifyRTCAudioSourceStats(stats.cast_to()); + } else if (stats.type() == RTCVideoSourceStats::kType) { + // RTCAudioSourceStats::kType and RTCVideoSourceStats::kType both have + // the value "media-source", but they are distinguishable with pointer + // equality (==). In JavaScript they would be distinguished with |kind|. + verify_successful &= + VerifyRTCVideoSourceStats(stats.cast_to()); } else if (stats.type() == RTCTransportStats::kType) { verify_successful &= VerifyRTCTransportStats(stats.cast_to()); @@ -542,6 +554,15 @@ class RTCStatsReportVerifier { verifier.TestMemberIsDefined(media_stream_track.kind); // Video or audio media stream track? if (*media_stream_track.kind == RTCMediaStreamTrackKind::kVideo) { + // The type of the referenced media source depends on kind. + if (media_stream_track.remote_source.is_defined() && + !*media_stream_track.remote_source) { + verifier.TestMemberIsIDReference(media_stream_track.media_source_id, + RTCVideoSourceStats::kType); + } else { + // Remote tracks don't have media source stats. + verifier.TestMemberIsUndefined(media_stream_track.media_source_id); + } // Video-only members verifier.TestMemberIsNonNegative( media_stream_track.frame_width); @@ -600,6 +621,15 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(media_stream_track.total_samples_duration); } else { RTC_DCHECK_EQ(*media_stream_track.kind, RTCMediaStreamTrackKind::kAudio); + // The type of the referenced media source depends on kind. + if (media_stream_track.remote_source.is_defined() && + !*media_stream_track.remote_source) { + verifier.TestMemberIsIDReference(media_stream_track.media_source_id, + RTCAudioSourceStats::kType); + } else { + // Remote tracks don't have media source stats. + verifier.TestMemberIsUndefined(media_stream_track.media_source_id); + } // Video-only members should be undefined verifier.TestMemberIsUndefined(media_stream_track.frame_width); verifier.TestMemberIsUndefined(media_stream_track.frame_height); @@ -777,8 +807,12 @@ class RTCStatsReportVerifier { VerifyRTCRTPStreamStats(outbound_stream, &verifier); if (outbound_stream.media_type.is_defined() && *outbound_stream.media_type == "video") { + verifier.TestMemberIsIDReference(outbound_stream.media_source_id, + RTCVideoSourceStats::kType); verifier.TestMemberIsNonNegative(outbound_stream.qp_sum); } else { + verifier.TestMemberIsIDReference(outbound_stream.media_source_id, + RTCAudioSourceStats::kType); verifier.TestMemberIsUndefined(outbound_stream.qp_sum); } verifier.TestMemberIsNonNegative(outbound_stream.packets_sent); @@ -812,6 +846,40 @@ class RTCStatsReportVerifier { return verifier.ExpectAllMembersSuccessfullyTested(); } + void VerifyRTCMediaSourceStats(const RTCMediaSourceStats& media_source, + RTCStatsVerifier* verifier) { + verifier->TestMemberIsDefined(media_source.track_identifier); + verifier->TestMemberIsDefined(media_source.kind); + if (media_source.kind.is_defined()) { + EXPECT_TRUE((*media_source.kind == "audio" && + media_source.type() == RTCAudioSourceStats::kType) || + (*media_source.kind == "video" && + media_source.type() == RTCVideoSourceStats::kType)); + } + } + + bool VerifyRTCAudioSourceStats(const RTCAudioSourceStats& audio_source) { + RTCStatsVerifier verifier(report_, &audio_source); + VerifyRTCMediaSourceStats(audio_source, &verifier); + return verifier.ExpectAllMembersSuccessfullyTested(); + } + + bool VerifyRTCVideoSourceStats(const RTCVideoSourceStats& video_source) { + RTCStatsVerifier verifier(report_, &video_source); + VerifyRTCMediaSourceStats(video_source, &verifier); + // TODO(hbos): This integration test uses fakes that doesn't support + // VideoTrackSourceInterface::Stats. When this is fixed we should + // TestMemberIsNonNegative() for |width| and |height| instead to + // reflect real code. + verifier.TestMemberIsUndefined(video_source.width); + verifier.TestMemberIsUndefined(video_source.height); + // TODO(hbos): When |frames| is implemented test that this member should be + // expected to be non-negative. + verifier.TestMemberIsUndefined(video_source.frames); + verifier.TestMemberIsNonNegative(video_source.frames_per_second); + return verifier.ExpectAllMembersSuccessfullyTested(); + } + bool VerifyRTCTransportStats(const RTCTransportStats& transport) { RTCStatsVerifier verifier(report_, &transport); verifier.TestMemberIsNonNegative(transport.bytes_sent); diff --git a/pc/rtc_stats_traversal.cc b/pc/rtc_stats_traversal.cc index e7af0e10e9..16a6c9d668 100644 --- a/pc/rtc_stats_traversal.cc +++ b/pc/rtc_stats_traversal.cc @@ -94,7 +94,8 @@ std::vector GetStatsReferencedIds(const RTCStats& stats) { const auto& stream = static_cast(stats); AddIdsIfDefined(stream.track_ids, &neighbor_ids); } else if (type == RTCMediaStreamTrackStats::kType) { - // RTCMediaStreamTrackStats does not have any neighbor references. + const auto& track = static_cast(stats); + AddIdIfDefined(track.media_source_id, &neighbor_ids); } else if (type == RTCPeerConnectionStats::kType) { // RTCPeerConnectionStats does not have any neighbor references. } else if (type == RTCInboundRTPStreamStats::kType || @@ -104,6 +105,14 @@ std::vector GetStatsReferencedIds(const RTCStats& stats) { AddIdIfDefined(rtp.track_id, &neighbor_ids); AddIdIfDefined(rtp.transport_id, &neighbor_ids); AddIdIfDefined(rtp.codec_id, &neighbor_ids); + if (type == RTCOutboundRTPStreamStats::kType) { + const auto& outbound_rtp = + static_cast(stats); + AddIdIfDefined(outbound_rtp.media_source_id, &neighbor_ids); + } + } else if (type == RTCAudioSourceStats::kType || + type == RTCVideoSourceStats::kType) { + // RTC[Audio/Video]SourceStats does not have any neighbor references. } else if (type == RTCTransportStats::kType) { // RTCTransportStats does not have any neighbor references. const auto& transport = static_cast(stats); diff --git a/stats/rtcstats_objects.cc b/stats/rtcstats_objects.cc index e6c96e0092..f97e23fb04 100644 --- a/stats/rtcstats_objects.cc +++ b/stats/rtcstats_objects.cc @@ -361,6 +361,7 @@ RTCMediaStreamStats::~RTCMediaStreamStats() {} // clang-format off WEBRTC_RTCSTATS_IMPL(RTCMediaStreamTrackStats, RTCStats, "track", &track_identifier, + &media_source_id, &remote_source, &ended, &detached, @@ -409,6 +410,7 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats(std::string&& id, const char* kind) : RTCStats(std::move(id), timestamp_us), track_identifier("trackIdentifier"), + media_source_id("mediaSourceId"), remote_source("remoteSource"), ended("ended"), detached("detached"), @@ -463,6 +465,7 @@ RTCMediaStreamTrackStats::RTCMediaStreamTrackStats( const RTCMediaStreamTrackStats& other) : RTCStats(other.id(), other.timestamp_us()), track_identifier(other.track_identifier), + media_source_id(other.media_source_id), remote_source(other.remote_source), ended(other.ended), detached(other.detached), @@ -668,6 +671,7 @@ RTCInboundRTPStreamStats::~RTCInboundRTPStreamStats() {} // clang-format off WEBRTC_RTCSTATS_IMPL( RTCOutboundRTPStreamStats, RTCRTPStreamStats, "outbound-rtp", + &media_source_id, &packets_sent, &retransmitted_packets_sent, &bytes_sent, @@ -687,6 +691,7 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(const std::string& id, RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(std::string&& id, int64_t timestamp_us) : RTCRTPStreamStats(std::move(id), timestamp_us), + media_source_id("mediaSourceId"), packets_sent("packetsSent"), retransmitted_packets_sent("retransmittedPacketsSent"), bytes_sent("bytesSent"), @@ -701,6 +706,7 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats(std::string&& id, RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( const RTCOutboundRTPStreamStats& other) : RTCRTPStreamStats(other), + media_source_id(other.media_source_id), packets_sent(other.packets_sent), retransmitted_packets_sent(other.retransmitted_packets_sent), bytes_sent(other.bytes_sent), @@ -714,6 +720,73 @@ RTCOutboundRTPStreamStats::RTCOutboundRTPStreamStats( RTCOutboundRTPStreamStats::~RTCOutboundRTPStreamStats() {} +// clang-format off +WEBRTC_RTCSTATS_IMPL(RTCMediaSourceStats, RTCStats, "parent-media-source", + &track_identifier, + &kind) +// clang-format on + +RTCMediaSourceStats::RTCMediaSourceStats(const std::string& id, + int64_t timestamp_us) + : RTCMediaSourceStats(std::string(id), timestamp_us) {} + +RTCMediaSourceStats::RTCMediaSourceStats(std::string&& id, int64_t timestamp_us) + : RTCStats(std::move(id), timestamp_us), + track_identifier("trackIdentifier"), + kind("kind") {} + +RTCMediaSourceStats::RTCMediaSourceStats(const RTCMediaSourceStats& other) + : RTCStats(other.id(), other.timestamp_us()), + track_identifier(other.track_identifier), + kind(other.kind) {} + +RTCMediaSourceStats::~RTCMediaSourceStats() {} + +// clang-format off +WEBRTC_RTCSTATS_IMPL_NO_MEMBERS( + RTCAudioSourceStats, RTCMediaSourceStats, "media-source") +// clang-format on + +RTCAudioSourceStats::RTCAudioSourceStats(const std::string& id, + int64_t timestamp_us) + : RTCAudioSourceStats(std::string(id), timestamp_us) {} + +RTCAudioSourceStats::RTCAudioSourceStats(std::string&& id, int64_t timestamp_us) + : RTCMediaSourceStats(std::move(id), timestamp_us) {} + +RTCAudioSourceStats::RTCAudioSourceStats(const RTCAudioSourceStats& other) + : RTCMediaSourceStats(other) {} + +RTCAudioSourceStats::~RTCAudioSourceStats() {} + +// clang-format off +WEBRTC_RTCSTATS_IMPL(RTCVideoSourceStats, RTCMediaSourceStats, "media-source", + &width, + &height, + &frames, + &frames_per_second) +// clang-format on + +RTCVideoSourceStats::RTCVideoSourceStats(const std::string& id, + int64_t timestamp_us) + : RTCVideoSourceStats(std::string(id), timestamp_us) {} + +RTCVideoSourceStats::RTCVideoSourceStats(std::string&& id, int64_t timestamp_us) + : RTCMediaSourceStats(std::move(id), timestamp_us), + width("width"), + height("height"), + frames("frames"), + frames_per_second("framesPerSecond") {} + +RTCVideoSourceStats::RTCVideoSourceStats(const RTCVideoSourceStats& other) + : RTCMediaSourceStats(other), + width(other.width), + height(other.height), + frames(other.frames), + frames_per_second(other.frames_per_second) {} + +RTCVideoSourceStats::~RTCVideoSourceStats() {} + // clang-format off WEBRTC_RTCSTATS_IMPL(RTCTransportStats, RTCStats, "transport", &bytes_sent,