Implement RTCMediaSourceStats and friends in standard getStats().

This implements RTCAudioSourceStats and RTCVideoSourceStats, both
inheriting from abstract dictionary RTCMediaSourceStats:
https://w3c.github.io/webrtc-stats/#dom-rtcmediasourcestats

All members are implemented except for the total "frames" counter:
- trackIdentifier
- kind
- width
- height
- framesPerSecond

This means to make googFrameWidthInput, googFrameHeightInput and
googFrameRateInput obsolete.

Implemented using the same code path as the goog stats, there are
some minor bugs that should be fixed in the future, but not this CL:
1. We create media-source objects on a per-track attachment basis.
   If the same track is attached multiple times this results in
   multiple media-source objects, but the spec says it should be on a
   per-source basis.
2. framesPerSecond is only calculated after connecting (when we have a
   sender with SSRC), but if collected on a per-source basis the source
   should be able to tell us the FPS whether or not we are sending it.

Bug: webrtc:10453
Change-Id: I23705a79f15075dca2536275934af1904a7f0d39
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/137804
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28028}
This commit is contained in:
Henrik Boström 2019-05-22 15:49:42 +02:00 committed by Commit Bot
parent 58c71db1b3
commit 646fda0212
8 changed files with 623 additions and 63 deletions

View file

@ -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<webrtc::RTCStats> this_class::copy() const { \
return std::unique_ptr<webrtc::RTCStats>(new this_class(*this)); \
} \
\
const char* this_class::type() const { return this_class::kType; } \
\
std::vector<const webrtc::RTCStatsMemberInterface*> \
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

View file

@ -279,6 +279,7 @@ class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats {
~RTCMediaStreamTrackStats() override;
RTCStatsMember<std::string> track_identifier;
RTCStatsMember<std::string> media_source_id;
RTCStatsMember<bool> remote_source;
RTCStatsMember<bool> 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<std::string> media_source_id;
RTCStatsMember<uint32_t> packets_sent;
RTCStatsMember<uint64_t> retransmitted_packets_sent;
RTCStatsMember<uint64_t> bytes_sent;
@ -466,6 +468,50 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats {
RTCStatsMember<std::string> 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<std::string> track_identifier;
RTCStatsMember<std::string> 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<uint32_t> width;
RTCStatsMember<uint32_t> height;
// TODO(hbos): Implement this metric.
RTCStatsMember<uint32_t> frames;
RTCStatsMember<uint32_t> frames_per_second;
};
// https://w3c.github.io/webrtc-stats/#transportstats-dict*
class RTC_EXPORT RTCTransportStats final : public RTCStats {
public:

View file

@ -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<RTCMediaStreamTrackStats> 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<uint32_t>(
@ -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<RTCMediaSourceStats> media_source_stats;
if (track->kind() == MediaStreamTrackInterface::kAudioKind) {
media_source_stats = absl::make_unique<RTCAudioSourceStats>(
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<RTCVideoSourceStats>(
RTCMediaSourceStatsIDFromKindAndAttachment(
cricket::MEDIA_TYPE_VIDEO, sender_internal->AttachmentId()),
timestamp_us);
auto* video_track = static_cast<VideoTrackInterface*>(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<AudioTrackInterface> 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<VideoTrackInterface> 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));

View file

@ -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;

View file

@ -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<AudioTrackInterface> {
}
};
class FakeVideoTrackSourceForStats : public VideoTrackSourceInterface {
public:
static rtc::scoped_refptr<FakeVideoTrackSourceForStats> Create(
int input_width,
int input_height) {
return rtc::scoped_refptr<FakeVideoTrackSourceForStats>(
new rtc::RefCountedObject<FakeVideoTrackSourceForStats>(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<bool> 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<VideoFrame> (part of VideoTrackSourceInterface)
void AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink,
const rtc::VideoSinkWants& wants) override {}
void RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) override {}
private:
int input_width_;
int input_height_;
};
class FakeVideoTrackForStats : public MediaStreamTrack<VideoTrackInterface> {
public:
static rtc::scoped_refptr<FakeVideoTrackForStats> Create(
const std::string& id,
MediaStreamTrackInterface::TrackState state) {
MediaStreamTrackInterface::TrackState state,
rtc::scoped_refptr<VideoTrackSourceInterface> source) {
rtc::scoped_refptr<FakeVideoTrackForStats> video_track(
new rtc::RefCountedObject<FakeVideoTrackForStats>(id));
new rtc::RefCountedObject<FakeVideoTrackForStats>(id,
std::move(source)));
video_track->set_state(state);
return video_track;
}
explicit FakeVideoTrackForStats(const std::string& id)
: MediaStreamTrack<VideoTrackInterface>(id) {}
FakeVideoTrackForStats(const std::string& id,
rtc::scoped_refptr<VideoTrackSourceInterface> source)
: MediaStreamTrack<VideoTrackInterface>(id), source_(source) {}
std::string kind() const override {
return MediaStreamTrackInterface::kVideoKind;
@ -217,7 +268,12 @@ class FakeVideoTrackForStats : public MediaStreamTrack<VideoTrackInterface> {
const rtc::VideoSinkWants& wants) override {}
void RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) override {}
VideoTrackSourceInterface* GetSource() const override { return nullptr; }
VideoTrackSourceInterface* GetSource() const override {
return source_.get();
}
private:
rtc::scoped_refptr<VideoTrackSourceInterface> source_;
};
rtc::scoped_refptr<MediaStreamTrackInterface> CreateFakeTrack(
@ -228,24 +284,26 @@ rtc::scoped_refptr<MediaStreamTrackInterface> 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<MockRtpSenderInternal> CreateMockSender(
const rtc::scoped_refptr<MediaStreamTrackInterface>& track,
cricket::MediaType media_type,
rtc::scoped_refptr<MediaStreamTrackInterface> track,
uint32_t ssrc,
int attachment_id,
std::vector<std::string> 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<MockRtpSenderInternal> sender(
new rtc::RefCountedObject<MockRtpSenderInternal>());
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<MediaStream> local_stream;
if (add_stream) {
local_stream = MediaStream::Create("LocalStreamId");
@ -348,7 +407,7 @@ class RTCStatsCollectorWrapper {
}
rtc::scoped_refptr<MockRtpSenderInternal> 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<MediaStreamTrackInterface*, cricket::VoiceSenderInfo>>
@ -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<MockRtpSenderInternal> rtp_sender = CreateMockSender(
cricket::MEDIA_TYPE_AUDIO,
rtc::scoped_refptr<MediaStreamTrackInterface>(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<MockRtpReceiverInternal> rtp_receiver =
CreateMockReceiver(
rtc::scoped_refptr<MediaStreamTrackInterface>(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<MockRtpSenderInternal> rtp_sender = CreateMockSender(
cricket::MEDIA_TYPE_VIDEO,
rtc::scoped_refptr<MediaStreamTrackInterface>(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<MockRtpReceiverInternal> rtp_receiver =
CreateMockReceiver(
rtc::scoped_refptr<MediaStreamTrackInterface>(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<RTCMediaStreamTrackStats>();
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<RTCOutboundRTPStreamStats>();
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<RTCMediaStreamTrackStats>(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<RTCMediaStreamTrackStats>(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<const RTCStatsReport> 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<const RTCStatsReport> 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<const RTCStatsReport> 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<const RTCStatsReport> 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<RTCAudioSourceStats>(),
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<MockRtpSenderInternal> sender = CreateMockSender(
cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {});
pc_->AddSender(sender);
rtc::scoped_refptr<const RTCStatsReport> 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<RTCVideoSourceStats>(),
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<MockRtpSenderInternal> sender = CreateMockSender(
cricket::MEDIA_TYPE_VIDEO, video_track, kNoSsrc, kAttachmentId, {});
pc_->AddSender(sender);
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
ASSERT_TRUE(report->Get("RTCVideoSource_42"));
auto video_stats =
report->Get("RTCVideoSource_42")->cast_to<RTCVideoSourceStats>();
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<MockRtpSenderInternal> sender = CreateMockSender(
cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {});
pc_->AddSender(sender);
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
ASSERT_TRUE(report->Get("RTCVideoSource_42"));
auto video_stats =
report->Get("RTCVideoSource_42")->cast_to<RTCVideoSourceStats>();
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<MockRtpSenderInternal> sender = CreateMockSender(
cricket::MEDIA_TYPE_AUDIO, /*track=*/nullptr, kSsrc, kAttachmentId, {});
pc_->AddSender(sender);
rtc::scoped_refptr<const RTCStatsReport> 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<MockRtpSenderInternal> sender = CreateMockSender(
cricket::MEDIA_TYPE_VIDEO, /*track=*/nullptr, kSsrc, kAttachmentId, {});
pc_->AddSender(sender);
rtc::scoped_refptr<const RTCStatsReport> 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<const RTCStatsReport> 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<const RTCStatsReport> 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<MockRtpSenderInternal> sender =
CreateMockSender(track, 0, 49, {});
CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 0, 49, {});
pc_->AddSender(sender);
rtc::scoped_refptr<const RTCStatsReport> report = stats_->GetStatsReport();
@ -2207,7 +2466,7 @@ TEST_F(RTCStatsCollectorTest, DoNotCrashOnSsrcChange) {
CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "audioTrack",
MediaStreamTrackInterface::kLive);
rtc::scoped_refptr<MockRtpSenderInternal> 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.

View file

@ -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<RTCOutboundRTPStreamStats>());
} 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<RTCAudioSourceStats>());
} 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<RTCVideoSourceStats>());
} else if (stats.type() == RTCTransportStats::kType) {
verify_successful &=
VerifyRTCTransportStats(stats.cast_to<RTCTransportStats>());
@ -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<uint32_t>(
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<uint64_t>(outbound_stream.qp_sum);
} else {
verifier.TestMemberIsIDReference(outbound_stream.media_source_id,
RTCAudioSourceStats::kType);
verifier.TestMemberIsUndefined(outbound_stream.qp_sum);
}
verifier.TestMemberIsNonNegative<uint32_t>(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<uint32_t>() 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<uint32_t>(video_source.frames_per_second);
return verifier.ExpectAllMembersSuccessfullyTested();
}
bool VerifyRTCTransportStats(const RTCTransportStats& transport) {
RTCStatsVerifier verifier(report_, &transport);
verifier.TestMemberIsNonNegative<uint64_t>(transport.bytes_sent);

View file

@ -94,7 +94,8 @@ std::vector<const std::string*> GetStatsReferencedIds(const RTCStats& stats) {
const auto& stream = static_cast<const RTCMediaStreamStats&>(stats);
AddIdsIfDefined(stream.track_ids, &neighbor_ids);
} else if (type == RTCMediaStreamTrackStats::kType) {
// RTCMediaStreamTrackStats does not have any neighbor references.
const auto& track = static_cast<const RTCMediaStreamTrackStats&>(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<const std::string*> 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<const RTCOutboundRTPStreamStats&>(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<const RTCTransportStats&>(stats);

View file

@ -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,