diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index a9a623e87285..42d9504e82ce 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -2567,6 +2567,29 @@ RefPtr PeerConnectionImpl::GetSenderStats( }); } +static UniquePtr GetDataChannelStats_s( + const RefPtr& aDataConnection, + const DOMHighResTimeStamp aTimestamp) { + UniquePtr report(new dom::RTCStatsCollection); + if (aDataConnection) { + aDataConnection->AppendStatsToReport(report, aTimestamp); + } + return report; +} + +RefPtr PeerConnectionImpl::GetDataChannelStats( + const RefPtr& aDataChannelConnection, + const DOMHighResTimeStamp aTimestamp) { + // Gather stats from DataChannels + return InvokeAsync( + GetMainThreadSerialEventTarget(), __func__, + [aDataChannelConnection, aTimestamp]() { + return dom::RTCStatsPromise::CreateAndResolve( + GetDataChannelStats_s(aDataChannelConnection, aTimestamp), + __func__); + }); +} + void PeerConnectionImpl::RecordConduitTelemetry() { if (!mMedia) { return; @@ -2663,6 +2686,8 @@ RefPtr PeerConnectionImpl::GetStats( } else { promises.AppendElement(mTransportHandler->GetIceStats("", now)); } + + promises.AppendElement(GetDataChannelStats(mDataConnection, now)); } // This is what we're going to return; all the stuff in |promises| will be @@ -2760,6 +2785,8 @@ RefPtr PeerConnectionImpl::GetStats( report->mRtpContributingSourceStats, idGen); AssignWithOpaqueIds(stats->mTrickledIceCandidateStats, report->mTrickledIceCandidateStats, idGen); + AssignWithOpaqueIds(stats->mDataChannelStats, + report->mDataChannelStats, idGen); if (!report->mRawLocalCandidates.AppendElements( stats->mRawLocalCandidates, fallible) || !report->mRawRemoteCandidates.AppendElements( diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 0d0d9bae35ef..278e90288628 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -450,6 +450,9 @@ class PeerConnectionImpl final const RefPtr& aPipeline); RefPtr GetSenderStats( const RefPtr& aPipeline); + RefPtr GetDataChannelStats( + const RefPtr& aDataChannelConnection, + const DOMHighResTimeStamp aTimestamp); nsresult CalculateFingerprint(const std::string& algorithm, std::vector* fingerprint) const; nsresult ConfigureJsepSessionCodecs(); diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index 12bf79d49acd..6e841de97326 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -43,9 +43,12 @@ #include "nsThreadUtils.h" #include "nsNetUtil.h" #include "nsNetCID.h" +#include "mozilla/RandomNum.h" #include "mozilla/StaticPtr.h" #include "mozilla/StaticMutex.h" #include "mozilla/Unused.h" +#include "mozilla/dom/RTCDataChannelBinding.h" +#include "mozilla/dom/RTCStatsReportBinding.h" #ifdef MOZ_PEERCONNECTION # include "mtransport/runnable_utils.h" # include "signaling/src/peerconnection/MediaTransportHandler.h" @@ -648,6 +651,60 @@ void DataChannelConnection::SetMaxMessageSize(bool aMaxMessageSizeSet, uint64_t DataChannelConnection::GetMaxMessageSize() { return mMaxMessageSize; } +void DataChannelConnection::AppendStatsToReport( + const UniquePtr& aReport, + const DOMHighResTimeStamp aTimestamp) const { + ASSERT_WEBRTC(NS_IsMainThread()); + nsString temp; + for (const RefPtr& chan : mChannels.GetAll()) { + // If channel is empty, ignore + if (!chan) { + continue; + } + mozilla::dom::RTCDataChannelStats stats; + nsString id = NS_LITERAL_STRING("dc"); + id.AppendInt(chan->GetStream()); + stats.mId.Construct(id); + chan->GetLabel(temp); + stats.mTimestamp.Construct(aTimestamp); + stats.mType.Construct(mozilla::dom::RTCStatsType::Data_channel); + stats.mLabel.Construct(temp); + chan->GetProtocol(temp); + stats.mProtocol.Construct(temp); + stats.mDataChannelIdentifier.Construct(chan->GetStream()); + { + using State = mozilla::dom::RTCDataChannelState; + State state; + switch (chan->GetReadyState()) { + case CONNECTING: + state = State::Connecting; + break; + case OPEN: + state = State::Open; + break; + case CLOSING: + state = State::Closing; + break; + case CLOSED: + state = State::Closed; + break; + default: + MOZ_ASSERT(false, "Unknown DataChannel state"); + continue; + }; + stats.mState.Construct(state); + } + auto counters = chan->GetTrafficCounters(); + stats.mMessagesSent.Construct(counters.mMessagesSent); + stats.mBytesSent.Construct(counters.mBytesSent); + stats.mMessagesReceived.Construct(counters.mMessagesReceived); + stats.mBytesReceived.Construct(counters.mBytesReceived); + if (!aReport->mDataChannelStats.AppendElement(stats, fallible)) { + mozalloc_handle_oom(0); + } + } +} + #ifdef MOZ_PEERCONNECTION bool DataChannelConnection::ConnectToTransport(const std::string& aTransportId, @@ -1697,6 +1754,12 @@ void DataChannelConnection::HandleDataMessage(const void* data, size_t length, return; } + channel->WithTrafficCounters( + [&data_length](DataChannel::TrafficCounters& counters) { + counters.mMessagesReceived++; + counters.mBytesReceived += data_length; + }); + // Notify onmessage DC_DEBUG(("%s: sending ON_DATA_%s%s for %p", __FUNCTION__, (type == DataChannelOnMessageAvailable::ON_DATA_STRING) ? "STRING" @@ -2890,13 +2953,22 @@ int DataChannelConnection::SendDataMsgCommon(uint16_t stream, } auto& channel = *channelPtr; - + int err = 0; if (isBinary) { - return SendDataMsg(channel, data, len, DATA_CHANNEL_PPID_BINARY_PARTIAL, - DATA_CHANNEL_PPID_BINARY); + err = SendDataMsg(channel, data, len, DATA_CHANNEL_PPID_BINARY_PARTIAL, + DATA_CHANNEL_PPID_BINARY); + } else { + err = SendDataMsg(channel, data, len, DATA_CHANNEL_PPID_DOMSTRING_PARTIAL, + DATA_CHANNEL_PPID_DOMSTRING); } - return SendDataMsg(channel, data, len, DATA_CHANNEL_PPID_DOMSTRING_PARTIAL, - DATA_CHANNEL_PPID_DOMSTRING); + if (!err) { + channel.WithTrafficCounters([&len](DataChannel::TrafficCounters& counters) { + counters.mMessagesSent++; + counters.mBytesSent += len; + }); + } + + return err; } void DataChannelConnection::Stop() { @@ -3266,6 +3338,11 @@ void DataChannel::SendOrQueue(DataChannelOnMessageAvailable* aMessage) { mMainThreadEventTarget->Dispatch(runnable.forget()); } +DataChannel::TrafficCounters DataChannel::GetTrafficCounters() const { + MutexAutoLock lock(mStatsLock); + return mTrafficCounters; +} + bool DataChannel::EnsureValidStream(ErrorResult& aRv) { MOZ_ASSERT(mConnection); if (mConnection && mStream != INVALID_STREAM) { @@ -3275,4 +3352,10 @@ bool DataChannel::EnsureValidStream(ErrorResult& aRv) { return false; } +void DataChannel::WithTrafficCounters( + const std::function& aFn) { + MutexAutoLock lock(mStatsLock); + aFn(mTrafficCounters); +} + } // namespace mozilla diff --git a/netwerk/sctp/datachannel/DataChannel.h b/netwerk/sctp/datachannel/DataChannel.h index d9a257afa6c5..5afbb46f18a4 100644 --- a/netwerk/sctp/datachannel/DataChannel.h +++ b/netwerk/sctp/datachannel/DataChannel.h @@ -51,6 +51,9 @@ class DataChannel; class DataChannelOnMessageAvailable; class MediaPacket; class MediaTransportHandler; +namespace dom { +struct RTCStatsCollection; +}; // For sending outgoing messages. // This class only holds a reference to the data and the info structure but does @@ -153,6 +156,8 @@ class DataChannelConnection final : public net::NeckoTargetHolder void SetMaxMessageSize(bool aMaxMessageSizeSet, uint64_t aMaxMessageSize); uint64_t GetMaxMessageSize(); + void AppendStatsToReport(const UniquePtr& aReport, + const DOMHighResTimeStamp aTimestamp) const; #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT // These block; they require something to decide on listener/connector // (though you can do simultaneous Connect()). Do not call these from @@ -305,7 +310,7 @@ class DataChannelConnection final : public net::NeckoTargetHolder void HandleNotification(const union sctp_notification* notif, size_t n); #ifdef SCTP_DTLS_SUPPORTED - bool IsSTSThread() { + bool IsSTSThread() const { bool on = false; if (mSTS) { mSTS->IsOnCurrentThread(&on); @@ -426,7 +431,8 @@ class DataChannel { mIsRecvBinary(false), mBufferedThreshold(0), // default from spec mBufferedAmount(0), - mMainThreadEventTarget(connection->GetNeckoTarget()) { + mMainThreadEventTarget(connection->GetNeckoTarget()), + mStatsLock("netwer::sctp::DataChannel::mStatsLock") { NS_ASSERTION(mConnection, "NULL connection"); } @@ -508,6 +514,15 @@ class DataChannel { void SendOrQueue(DataChannelOnMessageAvailable* aMessage); + struct TrafficCounters { + uint32_t mMessagesSent = 0; + uint64_t mBytesSent = 0; + uint32_t mMessagesReceived = 0; + uint64_t mBytesReceived = 0; + }; + + TrafficCounters GetTrafficCounters() const; + protected: // These are both mainthread only DataChannelListener* mListener; @@ -516,6 +531,7 @@ class DataChannel { private: nsresult AddDataToBinaryMsg(const char* data, uint32_t size); bool EnsureValidStream(ErrorResult& aRv); + void WithTrafficCounters(const std::function&); RefPtr mConnection; nsCString mLabel; @@ -538,6 +554,8 @@ class DataChannel { nsTArray> mBufferedData; // GUARDED_BY(mConnection->mLock) nsCOMPtr mMainThreadEventTarget; + mutable Mutex mStatsLock; // protects mTrafficCounters + TrafficCounters mTrafficCounters; }; // used to dispatch notifications of incoming data to the main thread