From 0adb8285b101ee7e8a8e1b03200018449f606d6a Mon Sep 17 00:00:00 2001 From: hbos Date: Wed, 23 Nov 2016 02:32:06 -0800 Subject: [PATCH] RTCCodecStats[1] added. RTCStatsCollector supports "payloadType", "codec" and "clockRate". "channels", "parameters" and "implementation" need to be supported before closing crbug.com/659117. [1] https://w3c.github.io/webrtc-stats/#codec-dict* BUG=chromium:659117, chromium:627816, chromium:657854 NOTRY=True Review-Url: https://codereview.webrtc.org/2509803004 Cr-Commit-Position: refs/heads/master@{#15207} --- webrtc/api/rtcstatscollector.cc | 258 ++++++++++++++++------- webrtc/api/rtcstatscollector.h | 15 +- webrtc/api/rtcstatscollector_unittest.cc | 166 ++++++++++++++- webrtc/api/stats/rtcstats_objects.h | 43 +++- webrtc/stats/rtcstats_objects.cc | 38 ++++ 5 files changed, 427 insertions(+), 93 deletions(-) diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc index b866b68b16..0260e01d46 100644 --- a/webrtc/api/rtcstatscollector.cc +++ b/webrtc/api/rtcstatscollector.cc @@ -33,6 +33,20 @@ std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { return "RTCCertificate_" + fingerprint; } +std::string RTCCodecStatsIDFromDirectionMediaAndPayload( + bool inbound, bool audio, uint32_t payload_type) { + // TODO(hbos): When we are able to handle multiple m= lines of the same media + // type (and multiple BaseChannels for the same type is possible?) this needs + // to be updated to differentiate the transport being used, and stats need to + // be collected for all of them. crbug.com/659117 + if (inbound) { + return audio ? "RTCCodec_InboundAudio_" + rtc::ToString<>(payload_type) + : "RTCCodec_InboundVideo_" + rtc::ToString<>(payload_type); + } + return audio ? "RTCCodec_OutboundAudio_" + rtc::ToString<>(payload_type) + : "RTCCodec_OutboundVideo_" + rtc::ToString<>(payload_type); +} + std::string RTCIceCandidatePairStatsIDFromConnectionInfo( const cricket::ConnectionInfo& info) { return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" + @@ -100,6 +114,21 @@ const char* DataStateToRTCDataChannelState( } } +std::unique_ptr CodecStatsFromRtpCodecParameters( + uint64_t timestamp_us, bool inbound, bool audio, + const RtpCodecParameters& codec_params) { + RTC_DCHECK_GE(codec_params.payload_type, 0); + RTC_DCHECK_LE(codec_params.payload_type, 127); + uint32_t payload_type = static_cast(codec_params.payload_type); + std::unique_ptr codec_stats(new RTCCodecStats( + RTCCodecStatsIDFromDirectionMediaAndPayload(inbound, audio, payload_type), + timestamp_us)); + codec_stats->payload_type = payload_type; + codec_stats->codec = (audio ? "audio/" : "video/") + codec_params.mime_type; + codec_stats->clock_rate = static_cast(codec_params.clock_rate); + return codec_stats; +} + void SetMediaStreamTrackStatsFromMediaStreamTrackInterface( const MediaStreamTrackInterface& track, RTCMediaStreamTrackStats* track_stats) { @@ -402,14 +431,17 @@ void RTCStatsCollector::ProducePartialResultsOnSignalingThread( SessionStats session_stats; if (pc_->session()->GetTransportStats(&session_stats)) { std::map transport_cert_stats = - PrepareTransportCertificateStats_s(session_stats); + PrepareTransportCertificateStats(session_stats); + MediaInfo media_info = PrepareMediaInfo(session_stats); ProduceCertificateStats_s( timestamp_us, transport_cert_stats, report.get()); + ProduceCodecStats_s( + timestamp_us, media_info, report.get()); ProduceIceCandidateAndPairStats_s( timestamp_us, session_stats, report.get()); ProduceRTPStreamStats_s( - timestamp_us, session_stats, report.get()); + timestamp_us, session_stats, media_info, report.get()); ProduceTransportStats_s( timestamp_us, session_stats, transport_cert_stats, report.get()); } @@ -511,6 +543,38 @@ void RTCStatsCollector::ProduceCertificateStats_s( } } +void RTCStatsCollector::ProduceCodecStats_s( + int64_t timestamp_us, const MediaInfo& media_info, + RTCStatsReport* report) const { + RTC_DCHECK(signaling_thread_->IsCurrent()); + // Audio + if (media_info.voice) { + // Inbound + for (const auto& pair : media_info.voice->receive_codecs) { + report->AddStats(CodecStatsFromRtpCodecParameters( + timestamp_us, true, true, pair.second)); + } + // Outbound + for (const auto& pair : media_info.voice->send_codecs) { + report->AddStats(CodecStatsFromRtpCodecParameters( + timestamp_us, false, true, pair.second)); + } + } + // Video + if (media_info.video) { + // Inbound + for (const auto& pair : media_info.video->receive_codecs) { + report->AddStats(CodecStatsFromRtpCodecParameters( + timestamp_us, true, false, pair.second)); + } + // Outbound + for (const auto& pair : media_info.video->send_codecs) { + report->AddStats(CodecStatsFromRtpCodecParameters( + timestamp_us, false, false, pair.second)); + } + } +} + void RTCStatsCollector::ProduceDataChannelStats_s( int64_t timestamp_us, RTCStatsReport* report) const { RTC_DCHECK(signaling_thread_->IsCurrent()); @@ -602,93 +666,107 @@ void RTCStatsCollector::ProducePeerConnectionStats_s( void RTCStatsCollector::ProduceRTPStreamStats_s( int64_t timestamp_us, const SessionStats& session_stats, - RTCStatsReport* report) const { + const MediaInfo& media_info, RTCStatsReport* report) const { RTC_DCHECK(signaling_thread_->IsCurrent()); // Audio - if (pc_->session()->voice_channel()) { - cricket::VoiceMediaInfo voice_media_info; - if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) { - std::string transport_id = RTCTransportStatsIDFromBaseChannel( - session_stats.proxy_to_transport, *pc_->session()->voice_channel()); - RTC_DCHECK(!transport_id.empty()); - // Inbound - for (const cricket::VoiceReceiverInfo& voice_receiver_info : - voice_media_info.receivers) { - // TODO(nisse): SSRC == 0 currently means none. Delete check when that - // is fixed. - if (voice_receiver_info.ssrc() == 0) - continue; - std::unique_ptr inbound_audio( - new RTCInboundRTPStreamStats( - RTCInboundRTPStreamStatsIDFromSSRC( - true, voice_receiver_info.ssrc()), - timestamp_us)); - SetInboundRTPStreamStatsFromVoiceReceiverInfo( - voice_receiver_info, inbound_audio.get()); - inbound_audio->transport_id = transport_id; - report->AddStats(std::move(inbound_audio)); + if (media_info.voice) { + std::string transport_id = RTCTransportStatsIDFromBaseChannel( + session_stats.proxy_to_transport, *pc_->session()->voice_channel()); + RTC_DCHECK(!transport_id.empty()); + // Inbound + for (const cricket::VoiceReceiverInfo& voice_receiver_info : + media_info.voice->receivers) { + // TODO(nisse): SSRC == 0 currently means none. Delete check when that + // is fixed. + if (voice_receiver_info.ssrc() == 0) + continue; + std::unique_ptr inbound_audio( + new RTCInboundRTPStreamStats( + RTCInboundRTPStreamStatsIDFromSSRC( + true, voice_receiver_info.ssrc()), + timestamp_us)); + SetInboundRTPStreamStatsFromVoiceReceiverInfo( + voice_receiver_info, inbound_audio.get()); + inbound_audio->transport_id = transport_id; + if (voice_receiver_info.codec_payload_type) { + inbound_audio->codec_id = + RTCCodecStatsIDFromDirectionMediaAndPayload( + true, true, *voice_receiver_info.codec_payload_type); } - // Outbound - for (const cricket::VoiceSenderInfo& voice_sender_info : - voice_media_info.senders) { - // TODO(nisse): SSRC == 0 currently means none. Delete check when that - // is fixed. - if (voice_sender_info.ssrc() == 0) - continue; - std::unique_ptr outbound_audio( - new RTCOutboundRTPStreamStats( - RTCOutboundRTPStreamStatsIDFromSSRC( - true, voice_sender_info.ssrc()), - timestamp_us)); - SetOutboundRTPStreamStatsFromVoiceSenderInfo( - voice_sender_info, outbound_audio.get()); - outbound_audio->transport_id = transport_id; - report->AddStats(std::move(outbound_audio)); + report->AddStats(std::move(inbound_audio)); + } + // Outbound + for (const cricket::VoiceSenderInfo& voice_sender_info : + media_info.voice->senders) { + // TODO(nisse): SSRC == 0 currently means none. Delete check when that + // is fixed. + if (voice_sender_info.ssrc() == 0) + continue; + std::unique_ptr outbound_audio( + new RTCOutboundRTPStreamStats( + RTCOutboundRTPStreamStatsIDFromSSRC( + true, voice_sender_info.ssrc()), + timestamp_us)); + SetOutboundRTPStreamStatsFromVoiceSenderInfo( + voice_sender_info, outbound_audio.get()); + outbound_audio->transport_id = transport_id; + if (voice_sender_info.codec_payload_type) { + outbound_audio->codec_id = + RTCCodecStatsIDFromDirectionMediaAndPayload( + false, true, *voice_sender_info.codec_payload_type); } + report->AddStats(std::move(outbound_audio)); } } // Video - if (pc_->session()->video_channel()) { - cricket::VideoMediaInfo video_media_info; - if (pc_->session()->video_channel()->GetStats(&video_media_info)) { - std::string transport_id = RTCTransportStatsIDFromBaseChannel( - session_stats.proxy_to_transport, *pc_->session()->video_channel()); - RTC_DCHECK(!transport_id.empty()); - // Inbound - for (const cricket::VideoReceiverInfo& video_receiver_info : - video_media_info.receivers) { - // TODO(nisse): SSRC == 0 currently means none. Delete check when that - // is fixed. - if (video_receiver_info.ssrc() == 0) - continue; - std::unique_ptr inbound_video( - new RTCInboundRTPStreamStats( - RTCInboundRTPStreamStatsIDFromSSRC( - false, video_receiver_info.ssrc()), - timestamp_us)); - SetInboundRTPStreamStatsFromVideoReceiverInfo( - video_receiver_info, inbound_video.get()); - inbound_video->transport_id = transport_id; - report->AddStats(std::move(inbound_video)); + if (media_info.video) { + std::string transport_id = RTCTransportStatsIDFromBaseChannel( + session_stats.proxy_to_transport, *pc_->session()->video_channel()); + RTC_DCHECK(!transport_id.empty()); + // Inbound + for (const cricket::VideoReceiverInfo& video_receiver_info : + media_info.video->receivers) { + // TODO(nisse): SSRC == 0 currently means none. Delete check when that + // is fixed. + if (video_receiver_info.ssrc() == 0) + continue; + std::unique_ptr inbound_video( + new RTCInboundRTPStreamStats( + RTCInboundRTPStreamStatsIDFromSSRC( + false, video_receiver_info.ssrc()), + timestamp_us)); + SetInboundRTPStreamStatsFromVideoReceiverInfo( + video_receiver_info, inbound_video.get()); + inbound_video->transport_id = transport_id; + if (video_receiver_info.codec_payload_type) { + inbound_video->codec_id = + RTCCodecStatsIDFromDirectionMediaAndPayload( + true, false, *video_receiver_info.codec_payload_type); } - // Outbound - for (const cricket::VideoSenderInfo& video_sender_info : - video_media_info.senders) { - // TODO(nisse): SSRC == 0 currently means none. Delete check when that - // is fixed. - if (video_sender_info.ssrc() == 0) - continue; - std::unique_ptr outbound_video( - new RTCOutboundRTPStreamStats( - RTCOutboundRTPStreamStatsIDFromSSRC( - false, video_sender_info.ssrc()), - timestamp_us)); - SetOutboundRTPStreamStatsFromVideoSenderInfo( - video_sender_info, outbound_video.get()); - outbound_video->transport_id = transport_id; - report->AddStats(std::move(outbound_video)); + report->AddStats(std::move(inbound_video)); + } + // Outbound + for (const cricket::VideoSenderInfo& video_sender_info : + media_info.video->senders) { + // TODO(nisse): SSRC == 0 currently means none. Delete check when that + // is fixed. + if (video_sender_info.ssrc() == 0) + continue; + std::unique_ptr outbound_video( + new RTCOutboundRTPStreamStats( + RTCOutboundRTPStreamStatsIDFromSSRC( + false, video_sender_info.ssrc()), + timestamp_us)); + SetOutboundRTPStreamStatsFromVideoSenderInfo( + video_sender_info, outbound_video.get()); + outbound_video->transport_id = transport_id; + if (video_sender_info.codec_payload_type) { + outbound_video->codec_id = + RTCCodecStatsIDFromDirectionMediaAndPayload( + false, false, *video_sender_info.codec_payload_type); } + report->AddStats(std::move(outbound_video)); } } } @@ -760,7 +838,7 @@ void RTCStatsCollector::ProduceTransportStats_s( } std::map -RTCStatsCollector::PrepareTransportCertificateStats_s( +RTCStatsCollector::PrepareTransportCertificateStats( const SessionStats& session_stats) const { RTC_DCHECK(signaling_thread_->IsCurrent()); std::map transport_cert_stats; @@ -785,6 +863,26 @@ RTCStatsCollector::PrepareTransportCertificateStats_s( return transport_cert_stats; } +RTCStatsCollector::MediaInfo RTCStatsCollector::PrepareMediaInfo( + const SessionStats& session_stats) const { + MediaInfo media_info; + if (pc_->session()->voice_channel()) { + cricket::VoiceMediaInfo voice_media_info; + if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) { + media_info.voice = rtc::Optional( + std::move(voice_media_info)); + } + } + if (pc_->session()->video_channel()) { + cricket::VideoMediaInfo video_media_info; + if (pc_->session()->video_channel()->GetStats(&video_media_info)) { + media_info.video = rtc::Optional( + std::move(video_media_info)); + } + } + return media_info; +} + void RTCStatsCollector::OnDataChannelCreated(DataChannel* channel) { channel->SignalOpened.connect(this, &RTCStatsCollector::OnDataChannelOpened); channel->SignalClosed.connect(this, &RTCStatsCollector::OnDataChannelClosed); diff --git a/webrtc/api/rtcstatscollector.h b/webrtc/api/rtcstatscollector.h index 08bab803e2..c32d65f246 100644 --- a/webrtc/api/rtcstatscollector.h +++ b/webrtc/api/rtcstatscollector.h @@ -21,11 +21,13 @@ #include "webrtc/api/stats/rtcstats_objects.h" #include "webrtc/api/stats/rtcstatsreport.h" #include "webrtc/base/asyncinvoker.h" +#include "webrtc/base/optional.h" #include "webrtc/base/refcount.h" #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/sslidentity.h" #include "webrtc/base/timeutils.h" +#include "webrtc/media/base/mediachannel.h" namespace cricket { class Candidate; @@ -86,6 +88,10 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface, std::unique_ptr local; std::unique_ptr remote; }; + struct MediaInfo { + rtc::Optional voice; + rtc::Optional video; + }; void AddPartialResults_s(rtc::scoped_refptr partial_report); void DeliverCachedReport(); @@ -95,6 +101,10 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface, int64_t timestamp_us, const std::map& transport_cert_stats, RTCStatsReport* report) const; + // Produces |RTCCodecStats|. + void ProduceCodecStats_s( + int64_t timestamp_us, const MediaInfo& media_info, + RTCStatsReport* report) const; // Produces |RTCDataChannelStats|. void ProduceDataChannelStats_s( int64_t timestamp_us, RTCStatsReport* report) const; @@ -111,7 +121,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface, // Produces |RTCInboundRTPStreamStats| and |RTCOutboundRTPStreamStats|. void ProduceRTPStreamStats_s( int64_t timestamp_us, const SessionStats& session_stats, - RTCStatsReport* report) const; + const MediaInfo& media_info, RTCStatsReport* report) const; // Produces |RTCTransportStats|. void ProduceTransportStats_s( int64_t timestamp_us, const SessionStats& session_stats, @@ -120,7 +130,8 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface, // Helper function to stats-producing functions. std::map - PrepareTransportCertificateStats_s(const SessionStats& session_stats) const; + PrepareTransportCertificateStats(const SessionStats& session_stats) const; + MediaInfo PrepareMediaInfo(const SessionStats& session_stats) const; // Slots for signals (sigslot) that are wired up to |pc_|. void OnDataChannelCreated(DataChannel* channel); diff --git a/webrtc/api/rtcstatscollector_unittest.cc b/webrtc/api/rtcstatscollector_unittest.cc index 64e7e0a97b..f27eb32980 100644 --- a/webrtc/api/rtcstatscollector_unittest.cc +++ b/webrtc/api/rtcstatscollector_unittest.cc @@ -15,9 +15,10 @@ #include #include +#include "webrtc/api/jsepsessiondescription.h" #include "webrtc/api/mediastream.h" #include "webrtc/api/mediastreamtrack.h" -#include "webrtc/api/jsepsessiondescription.h" +#include "webrtc/api/rtpparameters.h" #include "webrtc/api/stats/rtcstats_objects.h" #include "webrtc/api/stats/rtcstatsreport.h" #include "webrtc/api/test/mock_datachannel.h" @@ -52,6 +53,10 @@ void PrintTo(const RTCCertificateStats& stats, ::std::ostream* os) { *os << stats.ToString(); } +void PrintTo(const RTCCodecStats& stats, ::std::ostream* os) { + *os << stats.ToString(); +} + void PrintTo(const RTCDataChannelStats& stats, ::std::ostream* os) { *os << stats.ToString(); } @@ -806,6 +811,117 @@ TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsSingle) { ExpectReportContainsCertificateInfo(report, *remote_certinfo.get()); } +TEST_F(RTCStatsCollectorTest, CollectRTCCodecStats) { + MockVoiceMediaChannel* voice_media_channel = new MockVoiceMediaChannel(); + cricket::VoiceChannel voice_channel( + test_->worker_thread(), test_->network_thread(), test_->media_engine(), + voice_media_channel, nullptr, "VoiceContentName", false); + + MockVideoMediaChannel* video_media_channel = new MockVideoMediaChannel(); + cricket::VideoChannel video_channel( + test_->worker_thread(), test_->network_thread(), video_media_channel, + nullptr, "VideoContentName", false); + + // Audio + cricket::VoiceMediaInfo voice_media_info; + + RtpCodecParameters inbound_audio_codec; + inbound_audio_codec.payload_type = 1; + inbound_audio_codec.mime_type = "opus"; + inbound_audio_codec.clock_rate = 1337; + voice_media_info.receive_codecs.insert( + std::make_pair(inbound_audio_codec.payload_type, inbound_audio_codec)); + + RtpCodecParameters outbound_audio_codec; + outbound_audio_codec.payload_type = 2; + outbound_audio_codec.mime_type = "isac"; + outbound_audio_codec.clock_rate = 1338; + voice_media_info.send_codecs.insert( + std::make_pair(outbound_audio_codec.payload_type, outbound_audio_codec)); + + EXPECT_CALL(*voice_media_channel, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(voice_media_info), Return(true))); + + // Video + cricket::VideoMediaInfo video_media_info; + + RtpCodecParameters inbound_video_codec; + inbound_video_codec.payload_type = 3; + inbound_video_codec.mime_type = "H264"; + inbound_video_codec.clock_rate = 1339; + video_media_info.receive_codecs.insert( + std::make_pair(inbound_video_codec.payload_type, inbound_video_codec)); + + RtpCodecParameters outbound_video_codec; + outbound_video_codec.payload_type = 4; + outbound_video_codec.mime_type = "VP8"; + outbound_video_codec.clock_rate = 1340; + video_media_info.send_codecs.insert( + std::make_pair(outbound_video_codec.payload_type, outbound_video_codec)); + + EXPECT_CALL(*video_media_channel, GetStats(_)) + .WillOnce(DoAll(SetArgPointee<0>(video_media_info), Return(true))); + + SessionStats session_stats; + session_stats.proxy_to_transport["VoiceContentName"] = "TransportName"; + session_stats.proxy_to_transport["VideoContentName"] = "TransportName"; + session_stats.transport_stats["TransportName"].transport_name = + "TransportName"; + + EXPECT_CALL(test_->session(), GetTransportStats(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats), Return(true))); + EXPECT_CALL(test_->session(), voice_channel()) + .WillRepeatedly(Return(&voice_channel)); + EXPECT_CALL(test_->session(), video_channel()) + .WillRepeatedly(Return(&video_channel)); + + rtc::scoped_refptr report = GetStatsReport(); + + RTCCodecStats expected_inbound_audio_codec( + "RTCCodec_InboundAudio_1", report->timestamp_us()); + expected_inbound_audio_codec.payload_type = 1; + expected_inbound_audio_codec.codec = "audio/opus"; + expected_inbound_audio_codec.clock_rate = 1337; + + RTCCodecStats expected_outbound_audio_codec( + "RTCCodec_OutboundAudio_2", report->timestamp_us()); + expected_outbound_audio_codec.payload_type = 2; + expected_outbound_audio_codec.codec = "audio/isac"; + expected_outbound_audio_codec.clock_rate = 1338; + + RTCCodecStats expected_inbound_video_codec( + "RTCCodec_InboundVideo_3", report->timestamp_us()); + expected_inbound_video_codec.payload_type = 3; + expected_inbound_video_codec.codec = "video/H264"; + expected_inbound_video_codec.clock_rate = 1339; + + RTCCodecStats expected_outbound_video_codec( + "RTCCodec_OutboundVideo_4", report->timestamp_us()); + expected_outbound_video_codec.payload_type = 4; + expected_outbound_video_codec.codec = "video/VP8"; + expected_outbound_video_codec.clock_rate = 1340; + + ASSERT(report->Get(expected_inbound_audio_codec.id())); + EXPECT_EQ(expected_inbound_audio_codec, + report->Get(expected_inbound_audio_codec.id())->cast_to< + RTCCodecStats>()); + + ASSERT(report->Get(expected_outbound_audio_codec.id())); + EXPECT_EQ(expected_outbound_audio_codec, + report->Get(expected_outbound_audio_codec.id())->cast_to< + RTCCodecStats>()); + + ASSERT(report->Get(expected_inbound_video_codec.id())); + EXPECT_EQ(expected_inbound_video_codec, + report->Get(expected_inbound_video_codec.id())->cast_to< + RTCCodecStats>()); + + ASSERT(report->Get(expected_outbound_video_codec.id())); + EXPECT_EQ(expected_outbound_video_codec, + report->Get(expected_outbound_video_codec.id())->cast_to< + RTCCodecStats>()); +} + TEST_F(RTCStatsCollectorTest, CollectRTCCertificateStatsMultiple) { std::unique_ptr audio_local_certinfo = CreateFakeCertificateAndInfoFromDers( @@ -1320,14 +1436,24 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { voice_media_channel, nullptr, "VoiceContentName", false); cricket::VoiceMediaInfo voice_media_info; + voice_media_info.receivers.push_back(cricket::VoiceReceiverInfo()); voice_media_info.receivers[0].local_stats.push_back( cricket::SsrcReceiverInfo()); voice_media_info.receivers[0].local_stats[0].ssrc = 1; voice_media_info.receivers[0].packets_rcvd = 2; voice_media_info.receivers[0].bytes_rcvd = 3; + voice_media_info.receivers[0].codec_payload_type = rtc::Optional(42); voice_media_info.receivers[0].jitter_ms = 4500; voice_media_info.receivers[0].fraction_lost = 5.5f; + + RtpCodecParameters codec_parameters; + codec_parameters.payload_type = 42; + codec_parameters.mime_type = "dummy"; + codec_parameters.clock_rate = 0; + voice_media_info.receive_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))); @@ -1356,6 +1482,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { expected_audio.media_type = "audio"; expected_audio.transport_id = "RTCTransport_TransportName_" + rtc::ToString<>(cricket::ICE_CANDIDATE_COMPONENT_RTP); + expected_audio.codec_id = "RTCCodec_InboundAudio_42"; expected_audio.packets_received = 2; expected_audio.bytes_received = 3; expected_audio.jitter = 4.5; @@ -1367,6 +1494,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Audio) { EXPECT_EQ(audio, expected_audio); EXPECT_TRUE(report->Get(*expected_audio.transport_id)); + EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { @@ -1376,6 +1504,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { nullptr, "VideoContentName", false); cricket::VideoMediaInfo video_media_info; + video_media_info.receivers.push_back(cricket::VideoReceiverInfo()); video_media_info.receivers[0].local_stats.push_back( cricket::SsrcReceiverInfo()); @@ -1383,9 +1512,18 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { video_media_info.receivers[0].packets_rcvd = 2; video_media_info.receivers[0].bytes_rcvd = 3; video_media_info.receivers[0].fraction_lost = 4.5f; + video_media_info.receivers[0].codec_payload_type = rtc::Optional(42); video_media_info.receivers[0].firs_sent = 5; video_media_info.receivers[0].plis_sent = 6; video_media_info.receivers[0].nacks_sent = 7; + + RtpCodecParameters codec_parameters; + codec_parameters.payload_type = 42; + codec_parameters.mime_type = "dummy"; + codec_parameters.clock_rate = 0; + video_media_info.receive_codecs.insert( + std::make_pair(codec_parameters.payload_type, codec_parameters)); + EXPECT_CALL(*video_media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(video_media_info), Return(true))); @@ -1414,6 +1552,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { expected_video.media_type = "video"; expected_video.transport_id = "RTCTransport_TransportName_" + rtc::ToString<>(cricket::ICE_CANDIDATE_COMPONENT_RTP); + expected_video.codec_id = "RTCCodec_InboundVideo_42"; expected_video.fir_count = 5; expected_video.pli_count = 6; expected_video.nack_count = 7; @@ -1427,6 +1566,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { EXPECT_EQ(video, expected_video); EXPECT_TRUE(report->Get(*expected_video.transport_id)); + EXPECT_TRUE(report->Get(*video.codec_id)); } TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { @@ -1436,12 +1576,22 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { voice_media_channel, nullptr, "VoiceContentName", 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].rtt_ms = 4500; + voice_media_info.senders[0].codec_payload_type = rtc::Optional(42); + + RtpCodecParameters codec_parameters; + codec_parameters.payload_type = 42; + codec_parameters.mime_type = "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))); @@ -1470,6 +1620,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { expected_audio.media_type = "audio"; 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; expected_audio.round_trip_time = 4.5; @@ -1480,6 +1631,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { EXPECT_EQ(audio, expected_audio); EXPECT_TRUE(report->Get(*expected_audio.transport_id)); + EXPECT_TRUE(report->Get(*expected_audio.codec_id)); } TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { @@ -1489,6 +1641,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { nullptr, "VideoContentName", false); 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 = 1; @@ -1498,6 +1651,15 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { video_media_info.senders[0].packets_sent = 5; video_media_info.senders[0].bytes_sent = 6; video_media_info.senders[0].rtt_ms = 7500; + video_media_info.senders[0].codec_payload_type = rtc::Optional(42); + + RtpCodecParameters codec_parameters; + codec_parameters.payload_type = 42; + codec_parameters.mime_type = "dummy"; + codec_parameters.clock_rate = 0; + video_media_info.send_codecs.insert( + std::make_pair(codec_parameters.payload_type, codec_parameters)); + EXPECT_CALL(*video_media_channel, GetStats(_)) .WillOnce(DoAll(SetArgPointee<0>(video_media_info), Return(true))); @@ -1526,6 +1688,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { expected_video.media_type = "video"; expected_video.transport_id = "RTCTransport_TransportName_" + rtc::ToString<>(cricket::ICE_CANDIDATE_COMPONENT_RTP); + expected_video.codec_id = "RTCCodec_OutboundVideo_42"; expected_video.fir_count = 2; expected_video.pli_count = 3; expected_video.nack_count = 4; @@ -1539,6 +1702,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Video) { EXPECT_EQ(video, expected_video); EXPECT_TRUE(report->Get(*expected_video.transport_id)); + EXPECT_TRUE(report->Get(*expected_video.codec_id)); } TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { diff --git a/webrtc/api/stats/rtcstats_objects.h b/webrtc/api/stats/rtcstats_objects.h index 3263379e22..f9bf9ea9a8 100644 --- a/webrtc/api/stats/rtcstats_objects.h +++ b/webrtc/api/stats/rtcstats_objects.h @@ -59,6 +59,30 @@ class RTCCertificateStats final : public RTCStats { RTCStatsMember issuer_certificate_id; }; +// https://w3c.github.io/webrtc-stats/#codec-dict* +// Tracking bug crbug.com/659117 +// TODO(hbos): The present codec ID assignment is not sufficient to support +// Unified Plan or unbundled connections in all cases. crbug.com/659117 +class RTCCodecStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCCodecStats(const std::string& id, int64_t timestamp_us); + RTCCodecStats(std::string&& id, int64_t timestamp_us); + RTCCodecStats(const RTCCodecStats& other); + ~RTCCodecStats() override; + + RTCStatsMember payload_type; + RTCStatsMember codec; + RTCStatsMember clock_rate; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/659117 + RTCStatsMember channels; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/659117 + RTCStatsMember parameters; + // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/659117 + RTCStatsMember implementation; +}; + // https://w3c.github.io/webrtc-stats/#dcstats-dict* class RTCDataChannelStats final : public RTCStats { public: @@ -81,7 +105,7 @@ class RTCDataChannelStats final : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#candidatepair-dict* -// TODO(hbos): Finish implementation. Tracking bug crbug.com/633550 +// TODO(hbos): Tracking bug crbug.com/633550 class RTCIceCandidatePairStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -184,7 +208,7 @@ class RTCRemoteIceCandidateStats final : public RTCIceCandidateStats { }; // https://w3c.github.io/webrtc-stats/#msstats-dict* -// TODO(hbos): Finish implementation. Tracking bug crbug.com/660827 +// TODO(hbos): Tracking bug crbug.com/660827 class RTCMediaStreamStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -199,7 +223,7 @@ class RTCMediaStreamStats final : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#mststats-dict* -// TODO(hbos): Finish implementation. Tracking bug crbug.com/659137 +// TODO(hbos): Tracking bug crbug.com/659137 class RTCMediaStreamTrackStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -243,7 +267,7 @@ class RTCMediaStreamTrackStats final : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#pcstats-dict* -// TODO(hbos): Finish implementation. Tracking bug crbug.com/636818 +// TODO(hbos): Tracking bug crbug.com/636818 class RTCPeerConnectionStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -262,7 +286,7 @@ class RTCPeerConnectionStats final : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#streamstats-dict* -// TODO(hbos): Finish implementation. Tracking bug crbug.com/657854 +// TODO(hbos): Tracking bug crbug.com/657854 class RTCRTPStreamStats : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -281,7 +305,6 @@ class RTCRTPStreamStats : public RTCStats { // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/657854, 659137 RTCStatsMember media_track_id; RTCStatsMember transport_id; - // TODO(hbos): Not collected by |RTCStatsCollector|. crbug.com/657854, 659117 RTCStatsMember codec_id; // FIR and PLI counts are only defined for |media_type == "video"|. RTCStatsMember fir_count; @@ -299,8 +322,8 @@ class RTCRTPStreamStats : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict* -// TODO(hbos): Finish implementation and support the remote case -// |is_remote = true|. Tracking bug crbug.com/657855 +// Tracking bug crbug.com/657855 +// TODO(hbos): Support the remote case |is_remote = true|. crbug.com/657855 class RTCInboundRTPStreamStats final : public RTCRTPStreamStats { public: WEBRTC_RTCSTATS_DECL(); @@ -341,8 +364,8 @@ class RTCInboundRTPStreamStats final : public RTCRTPStreamStats { }; // https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict* -// TODO(hbos): Finish implementation and support the remote case -// |is_remote = true|. Tracking bug crbug.com/657856 +// Tracking bug crbug.com/657856 +// TODO(hbos): Support the remote case |is_remote = true|. crbug.com/657856 class RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { public: WEBRTC_RTCSTATS_DECL(); diff --git a/webrtc/stats/rtcstats_objects.cc b/webrtc/stats/rtcstats_objects.cc index 54b587b129..46cf0c38b6 100644 --- a/webrtc/stats/rtcstats_objects.cc +++ b/webrtc/stats/rtcstats_objects.cc @@ -62,6 +62,44 @@ RTCCertificateStats::RTCCertificateStats( RTCCertificateStats::~RTCCertificateStats() { } +WEBRTC_RTCSTATS_IMPL(RTCCodecStats, RTCStats, "codec", + &payload_type, + &codec, + &clock_rate, + &channels, + ¶meters, + &implementation); + +RTCCodecStats::RTCCodecStats( + const std::string& id, int64_t timestamp_us) + : RTCCodecStats(std::string(id), timestamp_us) { +} + +RTCCodecStats::RTCCodecStats( + std::string&& id, int64_t timestamp_us) + : RTCStats(std::move(id), timestamp_us), + payload_type("payloadType"), + codec("codec"), + clock_rate("clockRate"), + channels("channels"), + parameters("parameters"), + implementation("implementation") { +} + +RTCCodecStats::RTCCodecStats( + const RTCCodecStats& other) + : RTCStats(other.id(), other.timestamp_us()), + payload_type(other.payload_type), + codec(other.codec), + clock_rate(other.clock_rate), + channels(other.channels), + parameters(other.parameters), + implementation(other.implementation) { +} + +RTCCodecStats::~RTCCodecStats() { +} + WEBRTC_RTCSTATS_IMPL(RTCDataChannelStats, RTCStats, "data-channel", &label, &protocol,