--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -1173,16 +1173,28 @@ class RTCPeerConnection {
mozAddRIDExtension(receiver, extensionId) {
this._impl.addRIDExtension(receiver.track, extensionId);
}
mozAddRIDFilter(receiver, rid) {
this._impl.addRIDFilter(receiver.track, rid);
}
+ mozSetPacketCallback(callback) {
+ this._onPacket = callback;
+ }
+
+ mozEnablePacketDump(level, type, sending) {
+ this._impl.enablePacketDump(level, type, sending);
+ }
+
+ mozDisablePacketDump(level, type, sending) {
+ this._impl.disablePacketDump(level, type, sending);
+ }
+
get localDescription() {
this._checkClosed();
let sdp = this._impl.localDescription;
if (sdp.length == 0) {
return null;
}
return new this._win.RTCSessionDescription({ type: this._localType, sdp });
}
@@ -1611,16 +1623,23 @@ class PeerConnectionObserver {
}
onDTMFToneChange(trackId, tone) {
var pc = this._dompc;
var sender = pc._senders.find(({track}) => track.id == trackId);
sender.dtmf.dispatchEvent(new pc._win.RTCDTMFToneChangeEvent("tonechange",
{ tone }));
}
+
+ onPacket(level, type, sending, packet) {
+ var pc = this._dompc;
+ if (pc._onPacket) {
+ pc._onPacket(level, type, sending, packet);
+ }
+ }
}
setupPrototype(PeerConnectionObserver, {
classID: PC_OBS_CID,
contractID: PC_OBS_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer])
});
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -91,16 +91,18 @@ skip-if = toolkit == 'android' # no scre
[test_getUserMedia_trackCloneCleanup.html]
[test_getUserMedia_trackEnded.html]
[test_getUserMedia_peerIdentity.html]
[test_peerConnection_addIceCandidate.html]
[test_peerConnection_addtrack_removetrack_events.html]
skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_basicAudio.html]
skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_checkPacketDumpHook.html]
+skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_basicAudioNATSrflx.html]
skip-if = toolkit == 'android' # websockets don't work on android (bug 1266217)
[test_peerConnection_basicAudioNATRelay.html]
skip-if = toolkit == 'android' # websockets don't work on android (bug 1266217)
[test_peerConnection_basicAudioNATRelayTCP.html]
skip-if = toolkit == 'android' # websockets don't work on android (bug 1266217)
[test_peerConnection_basicAudioNATRelayTLS.html]
skip-if = true # need pyopenssl on builders, see bug 1323439 # toolkit == 'android' # websockets don't work on android (bug 1266217)
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_checkPacketDumpHook.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "1377299",
+ title: "Check that packet dump hooks generate callbacks"
+ });
+
+ function waitForPacket(pc, checkFunction) {
+ return new Promise(resolve => {
+ function onPacket(level, type, sending, packet) {
+ if (checkFunction(level, type, sending, packet)) {
+ SpecialPowers.wrap(pc).mozSetPacketCallback(() => {});
+ resolve();
+ }
+ }
+
+ SpecialPowers.wrap(pc).mozSetPacketCallback(onPacket);
+ }
+ );
+ }
+
+ async function waitForSendPacket(pc, type, level) {
+ await SpecialPowers.wrap(pc).mozEnablePacketDump(level, type, true);
+ await timeout(
+ waitForPacket(pc, (obsLevel, obsType, sending) => {
+ is(obsLevel, level, "Level for packet is " + level);
+ is(obsType, type, "Type for packet is " + type);
+ ok(sending, "This is a send packet");
+ return true;
+ }),
+ 10000, "Timeout waiting for " + type + " send packet on level " + level);
+ await SpecialPowers.wrap(pc).mozDisablePacketDump(level, type, true);
+ }
+
+ async function waitForRecvPacket(pc, type, level) {
+ await SpecialPowers.wrap(pc).mozEnablePacketDump(level, type, false);
+ await timeout(
+ waitForPacket(pc, (obsLevel, obsType, sending) => {
+ is(obsLevel, level, "Level for packet is " + level);
+ is(obsType, type, "Type for packet is " + type);
+ ok(!sending, "This is a recv packet");
+ return true;
+ }),
+ 10000, "Timeout waiting for " + type + " recv packet on level " + level);
+ await SpecialPowers.wrap(pc).mozDisablePacketDump(level, type, false);
+ }
+
+ var test;
+ runNetworkTest(function (options) {
+ test = new PeerConnectionTest(options);
+ test.setMediaConstraints([{audio: true, video: true}],
+ [{audio: true, video: true}]);
+ // pc.js uses video elements by default, we want to test audio elements here
+ test.pcLocal.audioElementsOnly = true;
+
+ test.chain.insertBefore('PC_LOCAL_WAIT_FOR_MEDIA_FLOW',[
+ async function PC_LOCAL_CHECK_PACKET_DUMP_HOOKS() {
+ await waitForRecvPacket(test.pcLocal._pc, "rtp", 0);
+ await waitForRecvPacket(test.pcLocal._pc, "rtcp", 0);
+ await waitForRecvPacket(test.pcLocal._pc, "srtp", 0);
+ await waitForRecvPacket(test.pcLocal._pc, "srtcp", 0);
+ await waitForSendPacket(test.pcLocal._pc, "rtp", 0);
+ await waitForSendPacket(test.pcLocal._pc, "rtcp", 0);
+ await waitForSendPacket(test.pcLocal._pc, "srtp", 0);
+ await waitForSendPacket(test.pcLocal._pc, "srtcp", 0);
+
+ await waitForRecvPacket(test.pcLocal._pc, "rtp", 1);
+ await waitForRecvPacket(test.pcLocal._pc, "rtcp", 1);
+ await waitForRecvPacket(test.pcLocal._pc, "srtp", 1);
+ await waitForRecvPacket(test.pcLocal._pc, "srtcp", 1);
+ await waitForSendPacket(test.pcLocal._pc, "rtp", 1);
+ await waitForSendPacket(test.pcLocal._pc, "rtcp", 1);
+ await waitForSendPacket(test.pcLocal._pc, "srtp", 1);
+ await waitForSendPacket(test.pcLocal._pc, "srtcp", 1);
+ },
+ async function PC_REMOTE_CHECK_PACKET_DUMP_HOOKS() {
+ await waitForRecvPacket(test.pcRemote._pc, "rtp", 0);
+ await waitForRecvPacket(test.pcRemote._pc, "rtcp", 0);
+ await waitForRecvPacket(test.pcRemote._pc, "srtp", 0);
+ await waitForRecvPacket(test.pcRemote._pc, "srtcp", 0);
+ await waitForSendPacket(test.pcRemote._pc, "rtp", 0);
+ await waitForSendPacket(test.pcRemote._pc, "rtcp", 0);
+ await waitForSendPacket(test.pcRemote._pc, "srtp", 0);
+ await waitForSendPacket(test.pcRemote._pc, "srtcp", 0);
+
+ await waitForRecvPacket(test.pcRemote._pc, "rtp", 1);
+ await waitForRecvPacket(test.pcRemote._pc, "rtcp", 1);
+ await waitForRecvPacket(test.pcRemote._pc, "srtp", 1);
+ await waitForRecvPacket(test.pcRemote._pc, "srtcp", 1);
+ await waitForSendPacket(test.pcRemote._pc, "rtp", 1);
+ await waitForSendPacket(test.pcRemote._pc, "rtcp", 1);
+ await waitForSendPacket(test.pcRemote._pc, "srtp", 1);
+ await waitForSendPacket(test.pcRemote._pc, "srtcp", 1);
+ }
+ ]);
+ test.run();
+ });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -61,16 +61,24 @@ interface PeerConnectionImpl {
void closeStreams();
sequence<MediaStream> getLocalStreams();
sequence<MediaStream> getRemoteStreams();
void addRIDExtension(MediaStreamTrack recvTrack, unsigned short extensionId);
void addRIDFilter(MediaStreamTrack recvTrack, DOMString rid);
+ void enablePacketDump(unsigned long level,
+ mozPacketDumpType type,
+ boolean sending);
+
+ void disablePacketDump(unsigned long level,
+ mozPacketDumpType type,
+ boolean sending);
+
/* As the ICE candidates roll in this one should be called each time
* in order to keep the candidate list up-to-date for the next SDP-related
* call PeerConnectionImpl does not parse ICE candidates, just sticks them
* into the SDP.
*/
[Throws]
void addIceCandidate(DOMString candidate, DOMString mid, unsigned short level);
--- a/dom/webidl/PeerConnectionObserver.webidl
+++ b/dom/webidl/PeerConnectionObserver.webidl
@@ -42,9 +42,13 @@ interface PeerConnectionObserver
/* Changes to MediaStreamTracks */
void onAddStream(MediaStream stream);
void onRemoveStream(MediaStream stream);
void onAddTrack(MediaStreamTrack track, sequence<MediaStream> streams);
void onRemoveTrack(MediaStreamTrack track);
/* DTMF callback */
void onDTMFToneChange(DOMString trackId, DOMString tone);
+
+ /* Packet dump callback */
+ void onPacket(unsigned long level, mozPacketDumpType type, boolean sending,
+ ArrayBuffer packet);
};
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -31,16 +31,28 @@ enum RTCIceConnectionState {
"checking",
"connected",
"completed",
"failed",
"disconnected",
"closed"
};
+enum mozPacketDumpType {
+ "rtp", // dump unencrypted rtp as the MediaPipeline sees it
+ "srtp", // dump encrypted rtp as the MediaPipeline sees it
+ "rtcp", // dump unencrypted rtcp as the MediaPipeline sees it
+ "srtcp" // dump encrypted rtcp as the MediaPipeline sees it
+};
+
+callback mozPacketCallback = void (unsigned long level,
+ mozPacketDumpType type,
+ boolean sending,
+ ArrayBuffer packet);
+
dictionary RTCDataChannelInit {
boolean ordered = true;
unsigned short maxPacketLifeTime;
unsigned short maxRetransmits;
DOMString protocol = "";
boolean negotiated = false;
unsigned short id;
@@ -118,16 +130,26 @@ interface RTCPeerConnection : EventTarge
sequence<RTCRtpSender> getSenders();
sequence<RTCRtpReceiver> getReceivers();
[ChromeOnly]
void mozAddRIDExtension(RTCRtpReceiver receiver, unsigned short extensionId);
[ChromeOnly]
void mozAddRIDFilter(RTCRtpReceiver receiver, DOMString rid);
+ [ChromeOnly]
+ void mozSetPacketCallback(mozPacketCallback callback);
+ [ChromeOnly]
+ void mozEnablePacketDump(unsigned long level,
+ mozPacketDumpType type,
+ boolean sending);
+ [ChromeOnly]
+ void mozDisablePacketDump(unsigned long level,
+ mozPacketDumpType type,
+ boolean sending);
void close ();
attribute EventHandler onnegotiationneeded;
attribute EventHandler onicecandidate;
attribute EventHandler onsignalingstatechange;
attribute EventHandler onaddstream; // obsolete
attribute EventHandler onaddtrack; // obsolete
attribute EventHandler ontrack; // replaces onaddtrack and onaddstream.
--- a/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
+++ b/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp
@@ -350,17 +350,17 @@ class TestAgentSend : public TestAgent {
ConfigureSendMediaCodec(&audio_config_);
EXPECT_EQ(mozilla::kMediaConduitNoError, err);
audio_stream_track_ = new FakeAudioStreamTrack();
}
virtual void CreatePipelines_s(bool aIsRtcpMux) {
- std::string test_pc("PC");
+ std::string test_pc;
if (aIsRtcpMux) {
ASSERT_FALSE(audio_rtcp_transport_.flow_);
}
RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_);
RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_);
@@ -400,17 +400,17 @@ class TestAgentReceive : public TestAgen
mozilla::MediaConduitErrorCode err =
static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get())->
ConfigureRecvMediaCodecs(codecs);
EXPECT_EQ(mozilla::kMediaConduitNoError, err);
}
virtual void CreatePipelines_s(bool aIsRtcpMux) {
- std::string test_pc("PC");
+ std::string test_pc;
if (aIsRtcpMux) {
ASSERT_FALSE(audio_rtcp_transport_.flow_);
}
audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(
test_pc,
nullptr,
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -594,16 +594,17 @@ MediaPipeline::MediaPipeline(const std::
MediaPipeline::~MediaPipeline() {
ASSERT_ON_THREAD(main_thread_);
MOZ_MTLOG(ML_INFO, "Destroying MediaPipeline: " << description_);
}
nsresult MediaPipeline::Init() {
ASSERT_ON_THREAD(main_thread_);
+ packet_dumper_ = new PacketDumper(pc_);
if (direction_ == RECEIVE) {
conduit_->SetReceiverTransport(transport_);
} else {
conduit_->SetTransmitterTransport(transport_);
}
RUN_ON_THREAD(sts_thread_,
@@ -629,16 +630,19 @@ void
MediaPipeline::DetachTransport_s()
{
ASSERT_ON_THREAD(sts_thread_);
disconnect_all();
transport_->Detach();
rtp_.Detach();
rtcp_.Detach();
+
+ // Make sure any cycles are broken
+ packet_dumper_ = nullptr;
}
nsresult
MediaPipeline::AttachTransport_s()
{
ASSERT_ON_THREAD(sts_thread_);
nsresult res;
MOZ_ASSERT(rtp_.transport_);
@@ -686,17 +690,20 @@ MediaPipeline::UpdateTransport_s(int lev
bool rtcp_mux = false;
if (!rtcp_transport) {
rtcp_transport = rtp_transport;
rtcp_mux = true;
}
if ((rtp_transport != rtp_.transport_) ||
(rtcp_transport != rtcp_.transport_)) {
- DetachTransport_s();
+ disconnect_all();
+ transport_->Detach();
+ rtp_.Detach();
+ rtcp_.Detach();
rtp_ = TransportInfo(rtp_transport, rtcp_mux ? MUX : RTP);
rtcp_ = TransportInfo(rtcp_transport, rtcp_mux ? MUX : RTCP);
AttachTransport_s();
}
level_ = level;
if (filter_ && filter) {
@@ -1085,16 +1092,19 @@ void MediaPipeline::RtpPacketReceived(Tr
csrc_stats_.insert(std::make_pair(header.arrOfCSRCs[i],
RtpCSRCStats(header.arrOfCSRCs[i],now)));
} else {
csrcInfo->second.SetTimestamp(now);
}
}
}
+ packet_dumper_->Dump(
+ level_, dom::mozPacketDumpType::Srtp, false, data, len);
+
// Make a copy rather than cast away constness
auto inner_data = MakeUnique<unsigned char[]>(len);
memcpy(inner_data.get(), data, len);
int out_len = 0;
nsresult res = rtp_.recv_srtp_->UnprotectRtp(inner_data.get(),
len, len, &out_len);
if (!NS_SUCCEEDED(res)) {
char tmp[16];
@@ -1110,16 +1120,19 @@ void MediaPipeline::RtpPacketReceived(Tr
return;
}
MOZ_MTLOG(ML_DEBUG, description_ << " received RTP packet.");
increment_rtp_packets_received(out_len);
RtpLogger::LogPacket(inner_data.get(), out_len, true, true, header.headerLength,
description_);
+ packet_dumper_->Dump(
+ level_, dom::mozPacketDumpType::Rtp, false, inner_data.get(), out_len);
+
(void)conduit_->ReceivedRTPPacket(inner_data.get(), out_len, header.ssrc); // Ignore error codes
}
void MediaPipeline::RtcpPacketReceived(TransportLayer *layer,
const unsigned char *data,
size_t len) {
if (!transport_->pipeline()) {
MOZ_MTLOG(ML_DEBUG, "Discarding incoming packet; transport disconnected");
@@ -1155,16 +1168,19 @@ void MediaPipeline::RtcpPacketReceived(T
// TODO bug 1279153: remove SR check for reduced size RTCP
if (filter_ && direction_ == RECEIVE) {
if (!filter_->FilterSenderReport(data, len)) {
MOZ_MTLOG(ML_NOTICE, "Dropping incoming RTCP packet; filtered out");
return;
}
}
+ packet_dumper_->Dump(
+ level_, dom::mozPacketDumpType::Srtcp, false, data, len);
+
// Make a copy rather than cast away constness
auto inner_data = MakeUnique<unsigned char[]>(len);
memcpy(inner_data.get(), data, len);
int out_len;
nsresult res = rtcp_.recv_srtp_->UnprotectRtcp(inner_data.get(),
len,
len,
@@ -1173,16 +1189,19 @@ void MediaPipeline::RtcpPacketReceived(T
if (!NS_SUCCEEDED(res))
return;
MOZ_MTLOG(ML_DEBUG, description_ << " received RTCP packet.");
increment_rtcp_packets_received();
RtpLogger::LogPacket(inner_data.get(), out_len, true, false, 0, description_);
+ packet_dumper_->Dump(
+ level_, dom::mozPacketDumpType::Rtcp, false, data, len);
+
MOZ_ASSERT(rtcp_.recv_srtp_); // This should never happen
(void)conduit_->ReceivedRTCPPacket(inner_data.get(), out_len); // Ignore error codes
}
bool MediaPipeline::IsRtp(const unsigned char *data, size_t len) {
if (len < 2)
return false;
@@ -1677,38 +1696,50 @@ nsresult MediaPipeline::PipelineTranspor
}
RtpLogger::LogPacket(data->data(), data->len(), false, is_rtp, header_len,
pipeline_->description_);
}
int out_len;
nsresult res;
if (is_rtp) {
+ pipeline_->packet_dumper_->Dump(
+ pipeline_->level(), dom::mozPacketDumpType::Rtp, true, data->data(), data->len());
+
res = transport.send_srtp_->ProtectRtp(data->data(),
data->len(),
data->capacity(),
&out_len);
} else {
+ pipeline_->packet_dumper_->Dump(
+ pipeline_->level(), dom::mozPacketDumpType::Rtcp, true, data->data(), data->len());
+
res = transport.send_srtp_->ProtectRtcp(data->data(),
data->len(),
data->capacity(),
&out_len);
}
if (!NS_SUCCEEDED(res)) {
return res;
}
// paranoia; don't have uninitialized bytes included in data->len()
data->SetLength(out_len);
MOZ_MTLOG(ML_DEBUG, pipeline_->description_ << " sending " <<
(is_rtp ? "RTP" : "RTCP") << " packet");
if (is_rtp) {
+ pipeline_->packet_dumper_->Dump(
+ pipeline_->level(), dom::mozPacketDumpType::Srtp, true, data->data(), out_len);
+
pipeline_->increment_rtp_packets_sent(out_len);
} else {
+ pipeline_->packet_dumper_->Dump(
+ pipeline_->level(), dom::mozPacketDumpType::Srtcp, true, data->data(), out_len);
+
pipeline_->increment_rtcp_packets_sent();
}
return pipeline_->SendPacket(transport.transport_, data->data(), out_len);
}
nsresult MediaPipeline::PipelineTransport::SendRtcpPacket(
const uint8_t* data, size_t len) {
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -16,16 +16,17 @@
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Atomics.h"
#include "SrtpFlow.h"
#include "databuffer.h"
#include "runnable_utils.h"
#include "transportflow.h"
#include "AudioPacketizer.h"
#include "StreamTracks.h"
+#include "signaling/src/peerconnection/PacketDumper.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
// Should come from MediaEngine.h, but that's a pain to include here
// because of the MOZILLA_EXTERNAL_LINKAGE stuff.
#define WEBRTC_DEFAULT_SAMPLE_RATE 32000
class nsIPrincipal;
@@ -317,16 +318,18 @@ class MediaPipeline : public sigslot::ha
// Written on Init. Read on STS thread.
std::string pc_;
std::string description_;
// Written on Init, all following accesses are on the STS thread.
nsAutoPtr<MediaPipelineFilter> filter_;
nsAutoPtr<webrtc::RtpHeaderParser> rtp_parser_;
+ nsAutoPtr<PacketDumper> packet_dumper_;
+
private:
// Gets the current time as a DOMHighResTimeStamp
static DOMHighResTimeStamp GetNow();
nsresult Init_s();
bool IsRtp(const unsigned char *data, size_t len);
};
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/peerconnection/PacketDumper.cpp
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "signaling/src/peerconnection/PacketDumper.h"
+#include "signaling/src/peerconnection/PeerConnectionImpl.h"
+#include "mozilla/media/MediaUtils.h" // NewRunnableFrom
+#include "nsThreadUtils.h" // NS_DispatchToMainThread
+
+namespace mozilla {
+
+PacketDumper::PacketDumper(PeerConnectionImpl* aPc) :
+ mPc(aPc)
+{
+}
+
+PacketDumper::PacketDumper(const std::string& aPcHandle)
+{
+ if (!aPcHandle.empty()) {
+ PeerConnectionWrapper pcw(aPcHandle);
+ mPc = pcw.impl();
+ }
+}
+
+PacketDumper::~PacketDumper()
+{
+ RefPtr<Runnable> pcDisposeRunnable =
+ media::NewRunnableFrom(
+ std::bind(
+ [](RefPtr<PeerConnectionImpl> pc) {
+ return NS_OK;
+ },
+ mPc.forget()));
+ NS_DispatchToMainThread(pcDisposeRunnable);
+}
+
+void
+PacketDumper::Dump(size_t level, dom::mozPacketDumpType type, bool sending,
+ const void* data, size_t size)
+{
+ // Optimization; avoids making a copy of the buffer, but we need to lock a
+ // mutex and check the flags. Could be optimized further, if we really want to
+ if (!mPc || !mPc->ShouldDumpPacket(level, type, sending)) {
+ return;
+ }
+
+ RefPtr<PeerConnectionImpl> pc = mPc;
+
+ UniquePtr<uint8_t[]> ownedPacket = MakeUnique<uint8_t[]>(size);
+ memcpy(ownedPacket.get(), data, size);
+
+ RefPtr<Runnable> dumpRunnable =
+ media::NewRunnableFrom(
+ std::bind(
+ [pc, level, type, sending, size](UniquePtr<uint8_t[]>& packet)
+ -> nsresult
+ {
+ pc->DumpPacket_m(level, type, sending, packet, size);
+ return NS_OK;
+ },
+ Move(ownedPacket)));
+
+ NS_DispatchToMainThread(dumpRunnable);
+}
+
+} //namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/peerconnection/PacketDumper.h
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PACKET_DUMPER_H_
+#define _PACKET_DUMPER_H_
+
+#include "nsISupportsImpl.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/RTCPeerConnectionBinding.h"
+
+namespace mozilla {
+class PeerConnectionImpl;
+
+class PacketDumper
+{
+ public:
+ explicit PacketDumper(PeerConnectionImpl* aPc);
+ explicit PacketDumper(const std::string& aPcHandle);
+ PacketDumper(const PacketDumper&) = delete;
+ ~PacketDumper();
+
+ PacketDumper& operator=(const PacketDumper&) = delete;
+
+ void Dump(size_t level, dom::mozPacketDumpType type, bool sending,
+ const void* data, size_t size);
+
+ private:
+ RefPtr<PeerConnectionImpl> mPc;
+};
+
+} // namespace mozilla
+
+#endif // _PACKET_DUMPER_H_
+
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -312,16 +312,18 @@ PeerConnectionImpl::PeerConnectionImpl(c
, mUuidGen(MakeUnique<PCUuidGenerator>())
, mHaveConfiguredCodecs(false)
, mHaveDataStream(false)
, mAddCandidateErrorCount(0)
, mTrickle(true) // TODO(ekr@rtfm.com): Use pref
, mNegotiationNeeded(false)
, mPrivateWindow(false)
, mActiveOnWindow(false)
+ , mPacketDumpEnabled(false)
+ , mPacketDumpFlagsMutex("Packet dump flags mutex")
{
MOZ_ASSERT(NS_IsMainThread());
auto log = RLogConnector::CreateInstance();
if (aGlobal) {
mWindow = do_QueryInterface(aGlobal->GetAsSupports());
if (IsPrivateBrowsing(mWindow)) {
mPrivateWindow = true;
log->EnterPrivateMode();
@@ -2323,16 +2325,79 @@ PeerConnectionImpl::GetStreamId(const DO
void
PeerConnectionImpl::OnMediaError(const std::string& aError)
{
CSFLogError(logTag, "Encountered media error! %s", aError.c_str());
// TODO: Let content know about this somehow.
}
+bool
+PeerConnectionImpl::ShouldDumpPacket(size_t level, dom::mozPacketDumpType type,
+ bool sending) const
+{
+ if (!mPacketDumpEnabled) {
+ return false;
+ }
+
+ MutexAutoLock lock(mPacketDumpFlagsMutex);
+
+ const std::vector<unsigned>* packetDumpFlags;
+
+ if (sending) {
+ packetDumpFlags = &mSendPacketDumpFlags;
+ } else {
+ packetDumpFlags = &mRecvPacketDumpFlags;
+ }
+
+ if (level < packetDumpFlags->size()) {
+ unsigned flag = 1 << (unsigned)type;
+ return flag & packetDumpFlags->at(level);
+ }
+
+ return false;
+}
+
+void
+PeerConnectionImpl::DumpPacket_m(size_t level, dom::mozPacketDumpType type,
+ bool sending, UniquePtr<uint8_t[]>& packet,
+ size_t size)
+{
+ if (IsClosed()) {
+ return;
+ }
+
+ if (!ShouldDumpPacket(level, type, sending)) {
+ return;
+ }
+
+ RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
+ if (!pco) {
+ return;
+ }
+
+ // TODO: Is this efficient? Should we try grabbing our JS ctx from somewhere
+ // else?
+ AutoJSAPI jsapi;
+ if(!jsapi.Init(GetWindow())) {
+ return;
+ }
+
+ JS::Rooted<JSObject*> jsobj(jsapi.cx(),
+ JS_NewArrayBufferWithContents(jsapi.cx(), size, packet.release()));
+
+ RootedSpiderMonkeyInterface<ArrayBuffer> arrayBuffer(jsapi.cx());
+ if (!arrayBuffer.Init(jsobj)) {
+ return;
+ }
+
+ JSErrorResult jrv;
+ pco->OnPacket(level, type, sending, arrayBuffer, jrv);
+}
+
nsresult
PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
const Sequence<OwningNonNull<DOMMediaStream>>& aStreams)
{
PC_AUTO_ENTER_API_CALL(true);
if (!aStreams.Length()) {
CSFLogError(logTag, "%s: At least one stream arg required", __FUNCTION__);
@@ -2417,16 +2482,62 @@ PeerConnectionImpl::AddRIDFilter(MediaSt
{
RefPtr<MediaPipeline> pipeline = GetMediaPipelineForTrack(aRecvTrack);
if (pipeline) {
pipeline->AddRIDFilter_m(NS_ConvertUTF16toUTF8(aRid).get());
}
return NS_OK;
}
+nsresult
+PeerConnectionImpl::EnablePacketDump(unsigned long level,
+ dom::mozPacketDumpType type,
+ bool sending)
+{
+ mPacketDumpEnabled = true;
+ std::vector<unsigned>* packetDumpFlags;
+ if (sending) {
+ packetDumpFlags = &mSendPacketDumpFlags;
+ } else {
+ packetDumpFlags = &mRecvPacketDumpFlags;
+ }
+
+ unsigned flag = 1 << (unsigned)type;
+
+ MutexAutoLock lock(mPacketDumpFlagsMutex);
+ if (level >= packetDumpFlags->size()) {
+ packetDumpFlags->resize(level + 1);
+ }
+
+ (*packetDumpFlags)[level] |= flag;
+ return NS_OK;
+}
+
+nsresult
+PeerConnectionImpl::DisablePacketDump(unsigned long level,
+ dom::mozPacketDumpType type,
+ bool sending)
+{
+ std::vector<unsigned>* packetDumpFlags;
+ if (sending) {
+ packetDumpFlags = &mSendPacketDumpFlags;
+ } else {
+ packetDumpFlags = &mRecvPacketDumpFlags;
+ }
+
+ unsigned flag = 1 << (unsigned)type;
+
+ MutexAutoLock lock(mPacketDumpFlagsMutex);
+ if (level < packetDumpFlags->size()) {
+ (*packetDumpFlags)[level] &= ~flag;
+ }
+
+ return NS_OK;
+}
+
NS_IMETHODIMP
PeerConnectionImpl::RemoveTrack(MediaStreamTrack& aTrack) {
PC_AUTO_ENTER_API_CALL(true);
std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
nsString wideTrackId;
aTrack.GetId(wideTrackId);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -19,23 +19,25 @@
#include "IPeerConnection.h"
#include "sigslot.h"
#include "nricectx.h"
#include "nricemediastream.h"
#include "nsComponentManagerUtils.h"
#include "nsPIDOMWindow.h"
#include "nsIUUIDGenerator.h"
#include "nsIThread.h"
+#include "mozilla/Mutex.h"
#include "signaling/src/jsep/JsepSession.h"
#include "signaling/src/jsep/JsepSessionImpl.h"
#include "signaling/src/sdp/SdpMediaSection.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
+#include "mozilla/dom/RTCPeerConnectionBinding.h" // mozPacketDumpType, maybe move?
#include "PrincipalChangeObserver.h"
#include "StreamTracks.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/net/DataChannel.h"
#include "VideoUtils.h"
#include "VideoSegment.h"
#include "mozilla/dom/RTCStatsReportBinding.h"
@@ -461,16 +463,34 @@ public:
// test-only: called from simulcast mochitests.
NS_IMETHODIMP_TO_ERRORRESULT(AddRIDFilter, ErrorResult& rv,
dom::MediaStreamTrack& aRecvTrack,
const nsAString& aRid)
{
rv = AddRIDFilter(aRecvTrack, aRid);
}
+ // test-only
+ NS_IMETHODIMP_TO_ERRORRESULT(EnablePacketDump, ErrorResult& rv,
+ unsigned long level,
+ dom::mozPacketDumpType type,
+ bool sending)
+ {
+ rv = EnablePacketDump(level, type, sending);
+ }
+
+ // test-only
+ NS_IMETHODIMP_TO_ERRORRESULT(DisablePacketDump, ErrorResult& rv,
+ unsigned long level,
+ dom::mozPacketDumpType type,
+ bool sending)
+ {
+ rv = DisablePacketDump(level, type, sending);
+ }
+
nsresult GetPeerIdentity(nsAString& peerIdentity)
{
if (mPeerIdentity) {
peerIdentity = mPeerIdentity->ToString();
return NS_OK;
}
peerIdentity.SetIsVoid(true);
@@ -621,16 +641,22 @@ public:
// PeerConnectionMedia can't do it because it doesn't know about principals
virtual void PrincipalChanged(dom::MediaStreamTrack* aTrack) override;
static std::string GetStreamId(const DOMMediaStream& aStream);
static std::string GetTrackId(const dom::MediaStreamTrack& track);
void OnMediaError(const std::string& aError);
+ bool ShouldDumpPacket(size_t level, dom::mozPacketDumpType type,
+ bool sending) const;
+
+ void DumpPacket_m(size_t level, dom::mozPacketDumpType type, bool sending,
+ UniquePtr<uint8_t[]>& packet, size_t size);
+
private:
virtual ~PeerConnectionImpl();
PeerConnectionImpl(const PeerConnectionImpl&rhs);
PeerConnectionImpl& operator=(PeerConnectionImpl);
nsresult CalculateFingerprint(const std::string& algorithm,
std::vector<uint8_t>* fingerprint) const;
nsresult ConfigureJsepSessionCodecs();
@@ -807,16 +833,21 @@ private:
uint32_t mInterToneGap;
};
static void
DTMFSendTimerCallback_m(nsITimer* timer, void*);
nsTArray<DTMFState> mDTMFStates;
+ std::vector<unsigned> mSendPacketDumpFlags;
+ std::vector<unsigned> mRecvPacketDumpFlags;
+ Atomic<bool> mPacketDumpEnabled;
+ mutable Mutex mPacketDumpFlagsMutex;
+
public:
//these are temporary until the DataChannel Listen/Connect API is removed
unsigned short listenPort;
unsigned short connectPort;
char *connectStr; // XXX ownership/free
};
// This is what is returned when you acquire on a handle
--- a/media/webrtc/signaling/src/peerconnection/moz.build
+++ b/media/webrtc/signaling/src/peerconnection/moz.build
@@ -19,15 +19,16 @@ LOCAL_INCLUDES += [
'/media/webrtc/signaling/src/mediapipeline',
'/media/webrtc/trunk',
]
# Multiple uses of logTag
SOURCES += [
'MediaPipelineFactory.cpp',
'MediaStreamList.cpp',
+ 'PacketDumper.cpp',
'PeerConnectionCtx.cpp',
'PeerConnectionImpl.cpp',
'PeerConnectionMedia.cpp',
'WebrtcGlobalInformation.cpp',
]
FINAL_LIBRARY = 'xul'