Adding "adapter" ORTC objects on top of ChannelManager/BaseChannel/etc.
This CL adds the following interfaces:
* RtpTransportController
* RtpTransport
* RtpSender
* RtpReceiver
They're implemented on top of the "BaseChannel" object, which is normally used
in a PeerConnection, and roughly corresponds to an SDP "m=" section. As a result
of this, there are several limitations:
* You can only have one of each type of sender and receiver (audio/video) on top
of the same transport controller.
* The sender/receiver with the same media type must use the same RTP transport.
* You can't change the transport after creating the sender or receiver.
* Some of the parameters aren't supported.
Later, these "adapter" objects will be gradually replaced by real objects that don't
have these limitations, as "BaseChannel", "MediaChannel" and related code is
restructured. In this CL, we essentially have:
ORTC adapter objects -> BaseChannel -> Media engine
PeerConnection -> BaseChannel -> Media engine
And later we hope to have simply:
PeerConnection -> "Real" ORTC objects -> Media engine
See the linked bug for more context.
BUG=webrtc:7013
TBR=stefan@webrtc.org
Review-Url: https://codereview.webrtc.org/2675173003
Cr-Original-Commit-Position: refs/heads/master@{#16842}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: e814a0dee015fe06b53673f6b021825130413b56
diff --git a/BUILD.gn b/BUILD.gn
index 6853f8e..9223732 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -258,6 +258,7 @@
"media",
"modules",
"modules/video_capture:video_capture_internal_impl",
+ "ortc",
"p2p",
"pc",
"sdk",
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 78079b3..716f8c9 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -56,7 +56,6 @@
"mediatypes.cc",
"mediatypes.h",
"notifier.h",
- "ortcfactoryinterface.h",
"peerconnectionfactoryproxy.h",
"peerconnectioninterface.h",
"peerconnectionproxy.h",
@@ -70,7 +69,6 @@
"statstypes.cc",
"statstypes.h",
"streamcollection.h",
- "udptransportinterface.h",
"umametrics.h",
"videosourceproxy.h",
"videotracksource.h",
@@ -87,6 +85,27 @@
]
}
+rtc_source_set("ortc_api") {
+ check_includes = false # TODO(deadbeef): Remove (bugs.webrtc.org/6828)
+ sources = [
+ "ortc/ortcfactoryinterface.h",
+ "ortc/ortcrtpreceiverinterface.h",
+ "ortc/ortcrtpsenderinterface.h",
+ "ortc/packettransportinterface.h",
+ "ortc/rtptransportcontrollerinterface.h",
+ "ortc/rtptransportinterface.h",
+ "ortc/udptransportinterface.h",
+ ]
+
+ # For mediastreaminterface.h, etc.
+ # TODO(deadbeef): Create a separate target for the common things ORTC and
+ # PeerConnection code shares, so that ortc_api can depend on that instead of
+ # libjingle_peerconnection_api.
+ public_deps = [
+ ":libjingle_peerconnection_api",
+ ]
+}
+
# TODO(ossu): Remove once downstream projects have updated.
rtc_source_set("libjingle_peerconnection") {
public_deps = [
diff --git a/api/mediatypes.cc b/api/mediatypes.cc
index 97b0189..fbcb53d 100644
--- a/api/mediatypes.cc
+++ b/api/mediatypes.cc
@@ -9,27 +9,39 @@
*/
#include "webrtc/api/mediatypes.h"
+
+#include "webrtc/api/mediastreaminterface.h"
#include "webrtc/base/checks.h"
+namespace {
+static const char* kMediaTypeData = "data";
+} // namespace
+
namespace cricket {
std::string MediaTypeToString(MediaType type) {
- std::string type_str;
switch (type) {
case MEDIA_TYPE_AUDIO:
- type_str = "audio";
- break;
+ return webrtc::MediaStreamTrackInterface::kAudioKind;
case MEDIA_TYPE_VIDEO:
- type_str = "video";
- break;
+ return webrtc::MediaStreamTrackInterface::kVideoKind;
case MEDIA_TYPE_DATA:
- type_str = "data";
- break;
- default:
- RTC_NOTREACHED();
- break;
+ return kMediaTypeData;
}
- return type_str;
+ // Not reachable; avoids compile warning.
+ FATAL();
+}
+
+MediaType MediaTypeFromString(const std::string& type_str) {
+ if (type_str == webrtc::MediaStreamTrackInterface::kAudioKind) {
+ return MEDIA_TYPE_AUDIO;
+ } else if (type_str == webrtc::MediaStreamTrackInterface::kVideoKind) {
+ return MEDIA_TYPE_VIDEO;
+ } else if (type_str == kMediaTypeData) {
+ return MEDIA_TYPE_DATA;
+ } else {
+ FATAL();
+ }
}
} // namespace cricket
diff --git a/api/mediatypes.h b/api/mediatypes.h
index 19acf4b..ec3a70a 100644
--- a/api/mediatypes.h
+++ b/api/mediatypes.h
@@ -22,6 +22,9 @@
};
std::string MediaTypeToString(MediaType type);
+// Aborts on invalid string. Only expected to be used on strings that are
+// guaranteed to be valid, such as MediaStreamTrackInterface::kind().
+MediaType MediaTypeFromString(const std::string& type_str);
} // namespace cricket
diff --git a/api/ortc/ortcfactoryinterface.h b/api/ortc/ortcfactoryinterface.h
new file mode 100644
index 0000000..855e3b0
--- /dev/null
+++ b/api/ortc/ortcfactoryinterface.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_API_ORTC_ORTCFACTORYINTERFACE_H_
+#define WEBRTC_API_ORTC_ORTCFACTORYINTERFACE_H_
+
+#include <memory>
+#include <string>
+#include <utility> // For std::move.
+
+#include "webrtc/api/mediaconstraintsinterface.h"
+#include "webrtc/api/mediastreaminterface.h"
+#include "webrtc/api/mediatypes.h"
+#include "webrtc/api/ortc/ortcrtpreceiverinterface.h"
+#include "webrtc/api/ortc/ortcrtpsenderinterface.h"
+#include "webrtc/api/ortc/packettransportinterface.h"
+#include "webrtc/api/ortc/rtptransportcontrollerinterface.h"
+#include "webrtc/api/ortc/rtptransportinterface.h"
+#include "webrtc/api/ortc/udptransportinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/rtpparameters.h"
+#include "webrtc/base/network.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/p2p/base/packetsocketfactory.h"
+
+namespace webrtc {
+
+// TODO(deadbeef): This should be part of /api/, but currently it's not and
+// including its header violates checkdeps rules.
+class AudioDeviceModule;
+
+// WARNING: This is experimental/under development, so use at your own risk; no
+// guarantee about API stability is guaranteed here yet.
+//
+// This class is the ORTC analog of PeerConnectionFactory. It acts as a factory
+// for ORTC objects that can be connected to each other.
+//
+// Some of these objects may not be represented by the ORTC specification, but
+// follow the same general principles.
+//
+// If one of the factory methods takes another object as an argument, it MUST
+// have been created by the same OrtcFactory.
+//
+// On object lifetimes: objects should be destroyed in this order:
+// 1. Objects created by the factory.
+// 2. The factory itself.
+// 3. Objects passed into OrtcFactoryInterface::Create.
+class OrtcFactoryInterface {
+ public:
+ // |network_thread| is the thread on which packets are sent and received.
+ // If null, a new rtc::Thread with a default socket server is created.
+ //
+ // |signaling_thread| is used for callbacks to the consumer of the API. If
+ // null, the current thread will be used, which assumes that the API consumer
+ // is running a message loop on this thread (either using an existing
+ // rtc::Thread, or by calling rtc::Thread::Current()->ProcessMessages).
+ //
+ // |network_manager| is used to determine which network interfaces are
+ // available. This is used for ICE, for example. If null, a default
+ // implementation will be used. Only accessed on |network_thread|.
+ //
+ // |socket_factory| is used (on the network thread) for creating sockets. If
+ // it's null, a default implementation will be used, which assumes
+ // |network_thread| is a normal rtc::Thread.
+ //
+ // |adm| is optional, and allows a different audio device implementation to
+ // be injected; otherwise a platform-specific module will be used that will
+ // use the default audio input.
+ //
+ // Note that the OrtcFactoryInterface does not take ownership of any of the
+ // objects passed in, and as previously stated, these objects can't be
+ // destroyed before the factory is.
+ static RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> Create(
+ rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm);
+
+ // Constructor for convenience which uses default implementations of
+ // everything (though does still require that the current thread runs a
+ // message loop; see above).
+ static RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> Create() {
+ return Create(nullptr, nullptr, nullptr, nullptr, nullptr);
+ }
+
+ virtual ~OrtcFactoryInterface() {}
+
+ // Creates an RTP transport controller, which is used in calls to
+ // CreateRtpTransport methods. If your application has some notion of a
+ // "call", you should create one transport controller per call.
+ //
+ // However, if you only are using one RtpTransport object, this doesn't need
+ // to be called explicitly; CreateRtpTransport will create one automatically
+ // if |rtp_transport_controller| is null. See below.
+ //
+ // TODO(deadbeef): Add MediaConfig and RtcEventLog arguments?
+ virtual RTCErrorOr<std::unique_ptr<RtpTransportControllerInterface>>
+ CreateRtpTransportController() = 0;
+
+ // Creates an RTP transport using the provided packet transports and
+ // transport controller.
+ //
+ // |rtp| will be used for sending RTP packets, and |rtcp| for RTCP packets.
+ //
+ // |rtp| can't be null. |rtcp| must be non-null if and only if
+ // |rtcp_parameters.mux| is false, indicating that RTCP muxing isn't used.
+ // Note that if RTCP muxing isn't enabled initially, it can still enabled
+ // later through SetRtcpParameters.
+ //
+ // If |transport_controller| is null, one will automatically be created, and
+ // its lifetime managed by the returned RtpTransport. This should only be
+ // done if a single RtpTransport is being used to communicate with the remote
+ // endpoint.
+ virtual RTCErrorOr<std::unique_ptr<RtpTransportInterface>> CreateRtpTransport(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerInterface* transport_controller) = 0;
+
+ // Returns the capabilities of an RTP sender of type |kind|. These
+ // capabilities can be used to determine what RtpParameters to use to create
+ // an RtpSender.
+ //
+ // If for some reason you pass in MEDIA_TYPE_DATA, returns an empty structure.
+ virtual RtpCapabilities GetRtpSenderCapabilities(
+ cricket::MediaType kind) const = 0;
+
+ // Creates an RTP sender with |track|. Will not start sending until Send is
+ // called. This is provided as a convenience; it's equivalent to calling
+ // CreateRtpSender with a kind (see below), followed by SetTrack.
+ //
+ // |track| and |transport| must not be null.
+ virtual RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> CreateRtpSender(
+ rtc::scoped_refptr<MediaStreamTrackInterface> track,
+ RtpTransportInterface* transport) = 0;
+
+ // Overload of CreateRtpSender allows creating the sender without a track.
+ //
+ // |kind| must be MEDIA_TYPE_AUDIO or MEDIA_TYPE_VIDEO.
+ virtual RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> CreateRtpSender(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport) = 0;
+
+ // Returns the capabilities of an RTP receiver of type |kind|. These
+ // capabilities can be used to determine what RtpParameters to use to create
+ // an RtpReceiver.
+ //
+ // If for some reason you pass in MEDIA_TYPE_DATA, returns an empty structure.
+ virtual RtpCapabilities GetRtpReceiverCapabilities(
+ cricket::MediaType kind) const = 0;
+
+ // Creates an RTP receiver of type |kind|. Will not start receiving media
+ // until Receive is called.
+ //
+ // |kind| must be MEDIA_TYPE_AUDIO or MEDIA_TYPE_VIDEO.
+ //
+ // |transport| must not be null.
+ virtual RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>
+ CreateRtpReceiver(cricket::MediaType kind,
+ RtpTransportInterface* transport) = 0;
+
+ // Create a UDP transport with IP address family |family|, using a port
+ // within the specified range.
+ //
+ // |family| must be AF_INET or AF_INET6.
+ //
+ // |min_port|/|max_port| values of 0 indicate no range restriction.
+ //
+ // Returns an error if the transport wasn't successfully created.
+ virtual RTCErrorOr<std::unique_ptr<UdpTransportInterface>>
+ CreateUdpTransport(int family, uint16_t min_port, uint16_t max_port) = 0;
+
+ // Method for convenience that has no port range restrictions.
+ RTCErrorOr<std::unique_ptr<UdpTransportInterface>> CreateUdpTransport(
+ int family) {
+ return CreateUdpTransport(family, 0, 0);
+ }
+
+ // NOTE: The methods below to create tracks/sources return scoped_refptrs
+ // rather than unique_ptrs, because these interfaces are also used with
+ // PeerConnection, where everything is ref-counted.
+
+ // Creates a audio source representing the default microphone input.
+ // |options| decides audio processing settings.
+ virtual rtc::scoped_refptr<AudioSourceInterface> CreateAudioSource(
+ const cricket::AudioOptions& options) = 0;
+
+ // Version of the above method that uses default options.
+ rtc::scoped_refptr<AudioSourceInterface> CreateAudioSource() {
+ return CreateAudioSource(cricket::AudioOptions());
+ }
+
+ // Creates a video source object wrapping and taking ownership of |capturer|.
+ //
+ // |constraints| can be used for selection of resolution and frame rate, and
+ // may be null if no constraints are desired.
+ virtual rtc::scoped_refptr<VideoTrackSourceInterface> CreateVideoSource(
+ std::unique_ptr<cricket::VideoCapturer> capturer,
+ const MediaConstraintsInterface* constraints) = 0;
+
+ // Version of the above method that omits |constraints|.
+ rtc::scoped_refptr<VideoTrackSourceInterface> CreateVideoSource(
+ std::unique_ptr<cricket::VideoCapturer> capturer) {
+ return CreateVideoSource(std::move(capturer), nullptr);
+ }
+
+ // Creates a new local video track wrapping |source|. The same |source| can
+ // be used in several tracks.
+ virtual rtc::scoped_refptr<VideoTrackInterface> CreateVideoTrack(
+ const std::string& id,
+ VideoTrackSourceInterface* source) = 0;
+
+ // Creates an new local audio track wrapping |source|.
+ virtual rtc::scoped_refptr<AudioTrackInterface> CreateAudioTrack(
+ const std::string& id,
+ AudioSourceInterface* source) = 0;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_API_ORTC_ORTCFACTORYINTERFACE_H_
diff --git a/api/ortc/ortcrtpreceiverinterface.h b/api/ortc/ortcrtpreceiverinterface.h
new file mode 100644
index 0000000..1fad29c
--- /dev/null
+++ b/api/ortc/ortcrtpreceiverinterface.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This file contains interfaces for RtpReceivers:
+// http://publications.ortc.org/2016/20161202/#rtcrtpreceiver*
+//
+// However, underneath the RtpReceiver is an RtpTransport, rather than a
+// DtlsTransport. This is to allow different types of RTP transports (besides
+// DTLS-SRTP) to be used.
+
+#ifndef WEBRTC_API_ORTC_ORTCRTPRECEIVERINTERFACE_H_
+#define WEBRTC_API_ORTC_ORTCRTPRECEIVERINTERFACE_H_
+
+#include "webrtc/api/mediastreaminterface.h"
+#include "webrtc/api/mediatypes.h"
+#include "webrtc/api/ortc/rtptransportinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/rtpparameters.h"
+
+namespace webrtc {
+
+// Note: Since receiver capabilities may depend on how the OrtcFactory was
+// created, instead of a static "GetCapabilities" method on this interface,
+// there is a "GetRtpReceiverCapabilities" method on the OrtcFactory.
+class OrtcRtpReceiverInterface {
+ public:
+ virtual ~OrtcRtpReceiverInterface() {}
+
+ // Returns a track representing the media received by this receiver.
+ //
+ // Currently, this will return null until Receive has been successfully
+ // called. Also, a new track will be created every time the primary SSRC
+ // changes.
+ //
+ // If encodings are removed, GetTrack will return null. Though deactivating
+ // an encoding (setting |active| to false) will not do this.
+ //
+ // In the future, these limitations will be fixed, and GetTrack will return
+ // the same track for the lifetime of the RtpReceiver. So it's not
+ // recommended to write code that depends on this non-standard behavior.
+ virtual rtc::scoped_refptr<MediaStreamTrackInterface> GetTrack() const = 0;
+
+ // Once supported, will switch to receiving media on a new transport.
+ // However, this is not currently supported and will always return an error.
+ virtual RTCError SetTransport(RtpTransportInterface* transport) = 0;
+ // Returns previously set (or constructed-with) transport.
+ virtual RtpTransportInterface* GetTransport() const = 0;
+
+ // Start receiving media with |parameters| (if |parameters| contains an
+ // active encoding).
+ //
+ // There are no limitations to how the parameters can be changed after the
+ // initial call to Receive, as long as they're valid (for example, they can't
+ // use the same payload type for two codecs).
+ virtual RTCError Receive(const RtpParameters& parameters) = 0;
+ // Returns parameters that were last successfully passed into Receive, or
+ // empty parameters if that hasn't yet occurred.
+ //
+ // Note that for parameters that are described as having an "implementation
+ // default" value chosen, GetParameters() will return those chosen defaults,
+ // with the exception of SSRCs which have special behavior. See
+ // rtpparameters.h for more details.
+ virtual RtpParameters GetParameters() const = 0;
+
+ // Audio or video receiver?
+ //
+ // Once GetTrack() starts always returning a track, this method will be
+ // redundant, as one can call "GetTrack()->kind()". However, it's still a
+ // nice convenience, and is symmetric with OrtcRtpSenderInterface::GetKind.
+ virtual cricket::MediaType GetKind() const = 0;
+
+ // TODO(deadbeef): GetContributingSources
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_API_ORTC_ORTCRTPRECEIVERINTERFACE_H_
diff --git a/api/ortc/ortcrtpsenderinterface.h b/api/ortc/ortcrtpsenderinterface.h
new file mode 100644
index 0000000..e369b53
--- /dev/null
+++ b/api/ortc/ortcrtpsenderinterface.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This file contains interfaces for RtpSenders:
+// http://publications.ortc.org/2016/20161202/#rtcrtpsender*
+//
+// However, underneath the RtpSender is an RtpTransport, rather than a
+// DtlsTransport. This is to allow different types of RTP transports (besides
+// DTLS-SRTP) to be used.
+
+#ifndef WEBRTC_API_ORTC_ORTCRTPSENDERINTERFACE_H_
+#define WEBRTC_API_ORTC_ORTCRTPSENDERINTERFACE_H_
+
+#include "webrtc/api/mediatypes.h"
+#include "webrtc/api/mediastreaminterface.h"
+#include "webrtc/api/ortc/rtptransportinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/rtpparameters.h"
+
+namespace webrtc {
+
+// Note: Since sender capabilities may depend on how the OrtcFactory was
+// created, instead of a static "GetCapabilities" method on this interface,
+// there is a "GetRtpSenderCapabilities" method on the OrtcFactory.
+class OrtcRtpSenderInterface {
+ public:
+ virtual ~OrtcRtpSenderInterface() {}
+
+ // Sets the source of media that will be sent by this sender.
+ //
+ // If Send has already been called, will immediately switch to sending this
+ // track. If |track| is null, will stop sending media.
+ //
+ // Returns INVALID_PARAMETER error if an audio track is set on a video
+ // RtpSender, or vice-versa.
+ virtual RTCError SetTrack(MediaStreamTrackInterface* track) = 0;
+ // Returns previously set (or constructed-with) track.
+ virtual rtc::scoped_refptr<MediaStreamTrackInterface> GetTrack() const = 0;
+
+ // Once supported, will switch to sending media on a new transport. However,
+ // this is not currently supported and will always return an error.
+ virtual RTCError SetTransport(RtpTransportInterface* transport) = 0;
+ // Returns previously set (or constructed-with) transport.
+ virtual RtpTransportInterface* GetTransport() const = 0;
+
+ // Start sending media with |parameters| (if |parameters| contains an active
+ // encoding).
+ //
+ // There are no limitations to how the parameters can be changed after the
+ // initial call to Send, as long as they're valid (for example, they can't
+ // use the same payload type for two codecs).
+ virtual RTCError Send(const RtpParameters& parameters) = 0;
+ // Returns parameters that were last successfully passed into Send, or empty
+ // parameters if that hasn't yet occurred.
+ //
+ // Note that for parameters that are described as having an "implementation
+ // default" value chosen, GetParameters() will return those chosen defaults,
+ // with the exception of SSRCs which have special behavior. See
+ // rtpparameters.h for more details.
+ virtual RtpParameters GetParameters() const = 0;
+
+ // Audio or video sender?
+ virtual cricket::MediaType GetKind() const = 0;
+
+ // TODO(deadbeef): SSRC conflict signal.
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_API_ORTC_ORTCRTPSENDERINTERFACE_H_
diff --git a/api/ortc/packettransportinterface.h b/api/ortc/packettransportinterface.h
new file mode 100644
index 0000000..2677ce6
--- /dev/null
+++ b/api/ortc/packettransportinterface.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_API_ORTC_PACKETTRANSPORTINTERFACE_H_
+#define WEBRTC_API_ORTC_PACKETTRANSPORTINTERFACE_H_
+
+namespace rtc {
+
+class PacketTransportInternal;
+
+} // namespace rtc
+
+namespace webrtc {
+
+// Base class for different packet-based transports.
+class PacketTransportInterface {
+ public:
+ virtual ~PacketTransportInterface() {}
+
+ protected:
+ // Only for internal use. Returns a pointer to an internal interface, for use
+ // by the implementation.
+ virtual rtc::PacketTransportInternal* GetInternal() = 0;
+
+ // Classes that can use this internal interface.
+ friend class RtpTransportControllerAdapter;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_API_ORTC_PACKETTRANSPORTINTERFACE_H_
diff --git a/api/ortc/rtptransportcontrollerinterface.h b/api/ortc/rtptransportcontrollerinterface.h
new file mode 100644
index 0000000..d1d0e44
--- /dev/null
+++ b/api/ortc/rtptransportcontrollerinterface.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_API_ORTC_RTPTRANSPORTCONTROLLERINTERFACE_H_
+#define WEBRTC_API_ORTC_RTPTRANSPORTCONTROLLERINTERFACE_H_
+
+#include <vector>
+
+#include "webrtc/api/ortc/rtptransportinterface.h"
+
+namespace webrtc {
+
+class RtpTransportControllerAdapter;
+
+// Used to group RTP transports between a local endpoint and the same remote
+// endpoint, for the purpose of sharing bandwidth estimation and other things.
+//
+// Comparing this to the PeerConnection model, non-budled audio/video would use
+// two RtpTransports with a single RtpTransportController, whereas bundled
+// media would use a single RtpTransport, and two PeerConnections would use
+// independent RtpTransportControllers.
+//
+// RtpTransports are associated with this controller when they're created, by
+// passing the controller into OrtcFactory's relevant "CreateRtpTransport"
+// method. When a transport is destroyed, it's automatically disassociated.
+// GetTransports returns all currently associated transports.
+//
+// This is the RTP equivalent of "IceTransportController" in ORTC; RtpTransport
+// is to RtpTransportController as IceTransport is to IceTransportController.
+class RtpTransportControllerInterface {
+ public:
+ virtual ~RtpTransportControllerInterface() {}
+
+ // Returns all transports associated with this controller (see explanation
+ // above). No ordering is guaranteed.
+ virtual std::vector<RtpTransportInterface*> GetTransports() const = 0;
+
+ protected:
+ // Only for internal use. Returns a pointer to an internal interface, for use
+ // by the implementation.
+ virtual RtpTransportControllerAdapter* GetInternal() = 0;
+
+ // Classes that can use this internal interface.
+ friend class OrtcFactory;
+ friend class RtpTransportAdapter;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_API_ORTC_RTPTRANSPORTCONTROLLERINTERFACE_H_
diff --git a/api/ortc/rtptransportinterface.h b/api/ortc/rtptransportinterface.h
new file mode 100644
index 0000000..942dc50
--- /dev/null
+++ b/api/ortc/rtptransportinterface.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_API_ORTC_RTPTRANSPORTINTERFACE_H_
+#define WEBRTC_API_ORTC_RTPTRANSPORTINTERFACE_H_
+
+#include <string>
+
+#include "webrtc/api/ortc/packettransportinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/base/optional.h"
+
+namespace webrtc {
+
+class RtpTransportAdapter;
+
+struct RtcpParameters {
+ // The SSRC to be used in the "SSRC of packet sender" field. If not set, one
+ // will be chosen by the implementation.
+ // TODO(deadbeef): Not implemented.
+ rtc::Optional<uint32_t> ssrc;
+
+ // The Canonical Name (CNAME) used by RTCP (e.g. in SDES messages).
+ //
+ // If empty in the construction of the RtpTransport, one will be generated by
+ // the implementation, and returned in GetRtcpParameters. Multiple
+ // RtpTransports created by the same OrtcFactory will use the same generated
+ // CNAME.
+ //
+ // If empty when passed into SetRtcpParameters, the CNAME simply won't be
+ // modified.
+ std::string cname;
+
+ // Send reduced-size RTCP?
+ bool reduced_size = false;
+
+ // Send RTCP multiplexed on the RTP transport?
+ bool mux = true;
+
+ bool operator==(const RtcpParameters& o) const {
+ return ssrc == o.ssrc && cname == o.cname &&
+ reduced_size == o.reduced_size && mux == o.mux;
+ }
+ bool operator!=(const RtcpParameters& o) const { return !(*this == o); }
+};
+
+// Base class for different types of RTP transports that can be created by an
+// OrtcFactory. Used by RtpSenders/RtpReceivers.
+//
+// This is not present in the standard ORTC API, but exists here for a few
+// reasons. Firstly, it allows different types of RTP transports to be used:
+// DTLS-SRTP (which is required for the web), but also SDES-SRTP and
+// unencrypted RTP. It also simplifies the handling of RTCP muxing, and
+// provides a better API point for it.
+//
+// Note that Edge's implementation of ORTC provides a similar API point, called
+// RTCSrtpSdesTransport:
+// https://msdn.microsoft.com/en-us/library/mt502527(v=vs.85).aspx
+class RtpTransportInterface {
+ public:
+ virtual ~RtpTransportInterface() {}
+
+ // Returns packet transport that's used to send RTP packets.
+ virtual PacketTransportInterface* GetRtpPacketTransport() const = 0;
+
+ // Returns separate packet transport that's used to send RTCP packets. If
+ // RTCP multiplexing is being used, returns null.
+ virtual PacketTransportInterface* GetRtcpPacketTransport() const = 0;
+
+ // Set/get RTCP params. Can be used to enable RTCP muxing or reduced-size
+ // RTCP if initially not enabled.
+ //
+ // Changing |mux| from "true" to "false" is not allowed, and changing the
+ // CNAME is currently unsupported.
+ virtual RTCError SetRtcpParameters(const RtcpParameters& parameters) = 0;
+ // Returns last set or constructed-with parameters. If |cname| was empty in
+ // construction, the generated CNAME will be present in the returned
+ // parameters (see above).
+ virtual RtcpParameters GetRtcpParameters() const = 0;
+
+ protected:
+ // Only for internal use. Returns a pointer to an internal interface, for use
+ // by the implementation.
+ virtual RtpTransportAdapter* GetInternal() = 0;
+
+ // Classes that can use this internal interface.
+ friend class OrtcFactory;
+ friend class OrtcRtpSenderAdapter;
+ friend class OrtcRtpReceiverAdapter;
+ friend class RtpTransportControllerAdapter;
+ friend class RtpTransportAdapter;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_API_ORTC_RTPTRANSPORTINTERFACE_H_
diff --git a/api/udptransportinterface.h b/api/ortc/udptransportinterface.h
similarity index 71%
rename from api/udptransportinterface.h
rename to api/ortc/udptransportinterface.h
index 30858e6..2781076 100644
--- a/api/udptransportinterface.h
+++ b/api/ortc/udptransportinterface.h
@@ -8,9 +8,10 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#ifndef WEBRTC_API_UDPTRANSPORTINTERFACE_H_
-#define WEBRTC_API_UDPTRANSPORTINTERFACE_H_
+#ifndef WEBRTC_API_ORTC_UDPTRANSPORTINTERFACE_H_
+#define WEBRTC_API_ORTC_UDPTRANSPORTINTERFACE_H_
+#include "webrtc/api/ortc/packettransportinterface.h"
#include "webrtc/api/proxy.h"
#include "webrtc/base/socketaddress.h"
@@ -26,10 +27,8 @@
//
// Calling SetRemoteAddress sets the destination of outgoing packets; without a
// destination, packets can't be sent, but they can be received.
-class UdpTransportInterface {
+class UdpTransportInterface : virtual public PacketTransportInterface {
public:
- virtual ~UdpTransportInterface() {}
-
// Get the address of the socket allocated for this transport.
virtual rtc::SocketAddress GetLocalAddress() const = 0;
@@ -45,15 +44,6 @@
virtual rtc::SocketAddress GetRemoteAddress() const = 0;
};
-// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
-// are called on is an implementation detail.
-BEGIN_OWNED_PROXY_MAP(UdpTransport)
- PROXY_WORKER_THREAD_DESTRUCTOR()
- PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetLocalAddress)
- PROXY_WORKER_METHOD1(bool, SetRemoteAddress, const rtc::SocketAddress&)
- PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetRemoteAddress)
-END_PROXY_MAP()
-
} // namespace webrtc
-#endif // WEBRTC_API_UDPTRANSPORTINTERFACE_H_
+#endif // WEBRTC_API_ORTC_UDPTRANSPORTINTERFACE_H_
diff --git a/api/ortcfactoryinterface.h b/api/ortcfactoryinterface.h
deleted file mode 100644
index 8d46d68..0000000
--- a/api/ortcfactoryinterface.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2017 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef WEBRTC_API_ORTCFACTORYINTERFACE_H_
-#define WEBRTC_API_ORTCFACTORYINTERFACE_H_
-
-#include <memory>
-
-#include "webrtc/api/udptransportinterface.h"
-#include "webrtc/base/network.h"
-#include "webrtc/base/thread.h"
-#include "webrtc/p2p/base/packetsocketfactory.h"
-
-namespace webrtc {
-
-// WARNING: This is experimental/under development, so use at your own risk; no
-// guarantee about API stability is guaranteed here yet.
-//
-// This class is the ORTC analog of PeerConnectionFactory. It acts as a factory
-// for ORTC objects that can be connected to each other.
-//
-// Some of these objects may not be represented by the ORTC specification, but
-// follow the same general principles.
-//
-// On object lifetimes: The factory must not be destroyed before destroying the
-// objects it created, and the objects passed into the factory must not be
-// destroyed before destroying the factory.
-class OrtcFactoryInterface {
- public:
- // |network_thread| is the thread on which packets are sent and received.
- // If null, a new rtc::Thread with a default socket server is created.
- //
- // |signaling_thread| is used for callbacks to the consumer of the API. If
- // null, the current thread will be used, which assumes that the API consumer
- // is running a message loop on this thread (either using an existing
- // rtc::Thread, or by calling rtc::Thread::Current()->ProcessMessages).
- //
- // |network_manager| is used to determine which network interfaces are
- // available. This is used for ICE, for example. If null, a default
- // implementation will be used. Only accessed on |network_thread|.
- //
- // |socket_factory| is used (on the network thread) for creating sockets. If
- // it's null, a default implementation will be used, which assumes
- // |network_thread| is a normal rtc::Thread.
- //
- // Note that the OrtcFactoryInterface does not take ownership of any of the
- // objects
- // passed in, and as previously stated, these objects can't be destroyed
- // before the factory is.
- static std::unique_ptr<OrtcFactoryInterface> Create(
- rtc::Thread* network_thread,
- rtc::Thread* signaling_thread,
- rtc::NetworkManager* network_manager,
- rtc::PacketSocketFactory* socket_factory);
- // Constructor for convenience which uses default implementations of
- // everything (though does still require that the current thread runs a
- // message loop; see above).
- static std::unique_ptr<OrtcFactoryInterface> Create() {
- return Create(nullptr, nullptr, nullptr, nullptr);
- }
-
- virtual ~OrtcFactoryInterface() {}
-
- virtual std::unique_ptr<UdpTransportInterface>
- CreateUdpTransport(int family, uint16_t min_port, uint16_t max_port) = 0;
- // Method for convenience that has no port range restrictions.
- std::unique_ptr<UdpTransportInterface> CreateUdpTransport(int family) {
- return CreateUdpTransport(family, 0, 0);
- }
-};
-
-} // namespace webrtc
-
-#endif // WEBRTC_API_ORTCFACTORYINTERFACE_H_
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index a2e5e1b..69bae29 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -68,7 +68,6 @@
#define WEBRTC_API_PEERCONNECTIONINTERFACE_H_
#include <memory>
-#include <ostream>
#include <string>
#include <utility>
#include <vector>
@@ -901,7 +900,7 @@
virtual rtc::scoped_refptr<MediaStreamInterface>
CreateLocalMediaStream(const std::string& label) = 0;
- // Creates a AudioSourceInterface.
+ // Creates an AudioSourceInterface.
// |options| decides audio processing settings.
virtual rtc::scoped_refptr<AudioSourceInterface> CreateAudioSource(
const cricket::AudioOptions& options) = 0;
diff --git a/api/proxy.h b/api/proxy.h
index 5634cfe..46c424d 100644
--- a/api/proxy.h
+++ b/api/proxy.h
@@ -353,18 +353,18 @@
// Helper macros to reduce code duplication.
-#define PROXY_MAP_BOILERPLATE(c) \
- template <class INTERNAL_CLASS> \
- class c##ProxyWithInternal; \
- typedef c##ProxyWithInternal<c##Interface> c##Proxy; \
- template <class INTERNAL_CLASS> \
- class c##ProxyWithInternal : public c##Interface { \
- protected: \
- typedef c##Interface C; \
- \
- public: \
- const INTERNAL_CLASS* internal() const { return c_.get(); } \
- INTERNAL_CLASS* internal() { return c_.get(); }
+#define PROXY_MAP_BOILERPLATE(c) \
+ template <class INTERNAL_CLASS> \
+ class c##ProxyWithInternal; \
+ typedef c##ProxyWithInternal<c##Interface> c##Proxy; \
+ template <class INTERNAL_CLASS> \
+ class c##ProxyWithInternal : public c##Interface { \
+ protected: \
+ typedef c##Interface C; \
+ \
+ public: \
+ const INTERNAL_CLASS* internal() const { return c_; } \
+ INTERNAL_CLASS* internal() { return c_; }
#define END_PROXY_MAP() \
};
@@ -403,6 +403,11 @@
void DestroyInternal() { c_ = nullptr; } \
rtc::scoped_refptr<INTERNAL_CLASS> c_;
+// Note: This doesn't use a unique_ptr, because it intends to handle a corner
+// case where an object's deletion triggers a callback that calls back into
+// this proxy object. If relying on a unique_ptr to delete the object, its
+// inner pointer would be set to null before this reentrant callback would have
+// a chance to run, resulting in a segfault.
#define OWNED_PROXY_MAP_BOILERPLATE(c) \
public: \
~c##ProxyWithInternal() { \
@@ -412,8 +417,8 @@
} \
\
private: \
- void DestroyInternal() { c_.reset(nullptr); } \
- std::unique_ptr<INTERNAL_CLASS> c_;
+ void DestroyInternal() { delete c_; } \
+ INTERNAL_CLASS* c_;
#define BEGIN_SIGNALING_PROXY_MAP(c) \
PROXY_MAP_BOILERPLATE(c) \
@@ -438,16 +443,16 @@
worker_thread, c); \
}
-#define BEGIN_OWNED_PROXY_MAP(c) \
- PROXY_MAP_BOILERPLATE(c) \
- WORKER_PROXY_MAP_BOILERPLATE(c) \
- OWNED_PROXY_MAP_BOILERPLATE(c) \
- public: \
- static std::unique_ptr<c##ProxyWithInternal> Create( \
- rtc::Thread* signaling_thread, rtc::Thread* worker_thread, \
- INTERNAL_CLASS* c) { \
- return std::unique_ptr<c##ProxyWithInternal>( \
- new c##ProxyWithInternal(signaling_thread, worker_thread, c)); \
+#define BEGIN_OWNED_PROXY_MAP(c) \
+ PROXY_MAP_BOILERPLATE(c) \
+ WORKER_PROXY_MAP_BOILERPLATE(c) \
+ OWNED_PROXY_MAP_BOILERPLATE(c) \
+ public: \
+ static std::unique_ptr<c##Interface> Create( \
+ rtc::Thread* signaling_thread, rtc::Thread* worker_thread, \
+ std::unique_ptr<INTERNAL_CLASS> c) { \
+ return std::unique_ptr<c##Interface>(new c##ProxyWithInternal( \
+ signaling_thread, worker_thread, c.release())); \
}
#define PROXY_SIGNALING_THREAD_DESTRUCTOR() \
@@ -464,95 +469,109 @@
#define PROXY_METHOD0(r, method) \
r method() override { \
- MethodCall0<C, r> call(c_.get(), &C::method); \
+ MethodCall0<C, r> call(c_, &C::method); \
return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
#define PROXY_CONSTMETHOD0(r, method) \
r method() const override { \
- ConstMethodCall0<C, r> call(c_.get(), &C::method); \
+ ConstMethodCall0<C, r> call(c_, &C::method); \
return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
-#define PROXY_METHOD1(r, method, t1) \
- r method(t1 a1) override { \
- MethodCall1<C, r, t1> call(c_.get(), &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_METHOD1(r, method, t1) \
+ r method(t1 a1) override { \
+ MethodCall1<C, r, t1> call(c_, &C::method, std::move(a1)); \
+ return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
-#define PROXY_CONSTMETHOD1(r, method, t1) \
- r method(t1 a1) const override { \
- ConstMethodCall1<C, r, t1> call(c_.get(), &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_CONSTMETHOD1(r, method, t1) \
+ r method(t1 a1) const override { \
+ ConstMethodCall1<C, r, t1> call(c_, &C::method, std::move(a1)); \
+ return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
-#define PROXY_METHOD2(r, method, t1, t2) \
- r method(t1 a1, t2 a2) override { \
- MethodCall2<C, r, t1, t2> call(c_.get(), &C::method, std::move(a1), \
- std::move(a2)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_METHOD2(r, method, t1, t2) \
+ r method(t1 a1, t2 a2) override { \
+ MethodCall2<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
+ std::move(a2)); \
+ return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
-#define PROXY_METHOD3(r, method, t1, t2, t3) \
- r method(t1 a1, t2 a2, t3 a3) override { \
- MethodCall3<C, r, t1, t2, t3> call(c_.get(), &C::method, std::move(a1), \
- std::move(a2), std::move(a3)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_METHOD3(r, method, t1, t2, t3) \
+ r method(t1 a1, t2 a2, t3 a3) override { \
+ MethodCall3<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
+ std::move(a2), std::move(a3)); \
+ return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
#define PROXY_METHOD4(r, method, t1, t2, t3, t4) \
r method(t1 a1, t2 a2, t3 a3, t4 a4) override { \
- MethodCall4<C, r, t1, t2, t3, t4> call(c_.get(), &C::method, \
- std::move(a1), std::move(a2), \
- std::move(a3), std::move(a4)); \
+ MethodCall4<C, r, t1, t2, t3, t4> call(c_, &C::method, std::move(a1), \
+ std::move(a2), std::move(a3), \
+ std::move(a4)); \
return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
-#define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5) \
- r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override { \
- MethodCall5<C, r, t1, t2, t3, t4, t5> call( \
- c_.get(), &C::method, std::move(a1), std::move(a2), std::move(a3), \
- std::move(a4), std::move(a5)); \
- return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
+#define PROXY_METHOD5(r, method, t1, t2, t3, t4, t5) \
+ r method(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) override { \
+ MethodCall5<C, r, t1, t2, t3, t4, t5> call(c_, &C::method, std::move(a1), \
+ std::move(a2), std::move(a3), \
+ std::move(a4), std::move(a5)); \
+ return call.Marshal(RTC_FROM_HERE, signaling_thread_); \
}
// Define methods which should be invoked on the worker thread.
#define PROXY_WORKER_METHOD0(r, method) \
r method() override { \
- MethodCall0<C, r> call(c_.get(), &C::method); \
+ MethodCall0<C, r> call(c_, &C::method); \
return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
#define PROXY_WORKER_CONSTMETHOD0(r, method) \
r method() const override { \
- ConstMethodCall0<C, r> call(c_.get(), &C::method); \
+ ConstMethodCall0<C, r> call(c_, &C::method); \
return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
-#define PROXY_WORKER_METHOD1(r, method, t1) \
- r method(t1 a1) override { \
- MethodCall1<C, r, t1> call(c_.get(), &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+#define PROXY_WORKER_METHOD1(r, method, t1) \
+ r method(t1 a1) override { \
+ MethodCall1<C, r, t1> call(c_, &C::method, std::move(a1)); \
+ return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
-#define PROXY_WORKER_CONSTMETHOD1(r, method, t1) \
- r method(t1 a1) const override { \
- ConstMethodCall1<C, r, t1> call(c_.get(), &C::method, std::move(a1)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+#define PROXY_WORKER_CONSTMETHOD1(r, method, t1) \
+ r method(t1 a1) const override { \
+ ConstMethodCall1<C, r, t1> call(c_, &C::method, std::move(a1)); \
+ return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
-#define PROXY_WORKER_METHOD2(r, method, t1, t2) \
- r method(t1 a1, t2 a2) override { \
- MethodCall2<C, r, t1, t2> call(c_.get(), &C::method, std::move(a1), \
- std::move(a2)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+#define PROXY_WORKER_METHOD2(r, method, t1, t2) \
+ r method(t1 a1, t2 a2) override { \
+ MethodCall2<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
+ std::move(a2)); \
+ return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
-#define PROXY_WORKER_CONSTMETHOD2(r, method, t1, t2) \
- r method(t1 a1, t2 a2) const override { \
- ConstMethodCall2<C, r, t1, t2> call(c_.get(), &C::method, std::move(a1), \
- std::move(a2)); \
- return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+#define PROXY_WORKER_CONSTMETHOD2(r, method, t1, t2) \
+ r method(t1 a1, t2 a2) const override { \
+ ConstMethodCall2<C, r, t1, t2> call(c_, &C::method, std::move(a1), \
+ std::move(a2)); \
+ return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ }
+
+#define PROXY_WORKER_METHOD3(r, method, t1, t2, t3) \
+ r method(t1 a1, t2 a2, t3 a3) override { \
+ MethodCall3<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
+ std::move(a2), std::move(a3)); \
+ return call.Marshal(RTC_FROM_HERE, worker_thread_); \
+ }
+
+#define PROXY_WORKER_CONSTMETHOD3(r, method, t1, t2) \
+ r method(t1 a1, t2 a2, t3 a3) const override { \
+ ConstMethodCall3<C, r, t1, t2, t3> call(c_, &C::method, std::move(a1), \
+ std::move(a2), std::move(a3)); \
+ return call.Marshal(RTC_FROM_HERE, worker_thread_); \
}
} // namespace webrtc
diff --git a/api/rtcerror.h b/api/rtcerror.h
index 1c130c0..2ba7837 100644
--- a/api/rtcerror.h
+++ b/api/rtcerror.h
@@ -224,7 +224,7 @@
// NOTE: Not explicit - we want to use RTCErrorOr<T> as a return type
// so it is convenient and sensible to be able to do 'return T()'
// when the return type is RTCErrorOr<T>.
- RTCErrorOr(T value) : value_(std::move(value)) {}
+ RTCErrorOr(T&& value) : value_(std::move(value)) {}
// Delete the copy constructor and assignment operator; there aren't any use
// cases where you should need to copy an RTCErrorOr, as opposed to moving
diff --git a/api/rtpparameters.h b/api/rtpparameters.h
index f506c40..e4fe47b 100644
--- a/api/rtpparameters.h
+++ b/api/rtpparameters.h
@@ -16,6 +16,7 @@
#include <vector>
#include "webrtc/api/mediatypes.h"
+#include "webrtc/config.h"
#include "webrtc/base/optional.h"
namespace webrtc {
@@ -47,14 +48,13 @@
// Used in RtcpFeedback struct.
enum class RtcpFeedbackType {
- ACK,
CCM,
NACK,
REMB, // "goog-remb"
TRANSPORT_CC,
};
-// Used in RtcpFeedback struct when type is ACK, NACK or CCM.
+// Used in RtcpFeedback struct when type is NACK or CCM.
enum class RtcpFeedbackMessageType {
// Equivalent to {type: "nack", parameter: undefined} in ORTC.
GENERIC_NACK,
@@ -76,7 +76,7 @@
enum class PriorityType { VERY_LOW, LOW, MEDIUM, HIGH };
struct RtcpFeedback {
- RtcpFeedbackType type = RtcpFeedbackType::ACK;
+ RtcpFeedbackType type = RtcpFeedbackType::CCM;
// Equivalent to ORTC "parameter" field with slight differences:
// 1. It's an enum instead of a string.
@@ -84,6 +84,12 @@
// rather than an unset "parameter" value.
rtc::Optional<RtcpFeedbackMessageType> message_type;
+ // Constructors for convenience.
+ RtcpFeedback() {}
+ explicit RtcpFeedback(RtcpFeedbackType type) : type(type) {}
+ RtcpFeedback(RtcpFeedbackType type, RtcpFeedbackMessageType message_type)
+ : type(type), message_type(message_type) {}
+
bool operator==(const RtcpFeedback& o) const {
return type == o.type && message_type == o.message_type;
}
@@ -126,7 +132,12 @@
std::vector<RtcpFeedback> rtcp_feedback;
// Codec-specific parameters that must be signaled to the remote party.
+ //
// Corresponds to "a=fmtp" parameters in SDP.
+ //
+ // Contrary to ORTC, these parameters are named using all lowercase strings.
+ // This helps make the mapping to SDP simpler, if an application is using
+ // SDP. Boolean values are represented by the string "1".
std::unordered_map<std::string, std::string> parameters;
// Codec-specific parameters that may optionally be signaled to the remote
@@ -184,6 +195,12 @@
// TODO(deadbeef): Not implemented.
bool preferred_encrypt = false;
+ // Constructors for convenience.
+ RtpHeaderExtensionCapability() = default;
+ explicit RtpHeaderExtensionCapability(const std::string& uri) : uri(uri) {}
+ RtpHeaderExtensionCapability(const std::string& uri, int preferred_id)
+ : uri(uri), preferred_id(preferred_id) {}
+
bool operator==(const RtpHeaderExtensionCapability& o) const {
return uri == o.uri && preferred_id == o.preferred_id &&
preferred_encrypt == o.preferred_encrypt;
@@ -193,33 +210,23 @@
}
};
-// Used in RtpParameters; represents a specific configuration of a header
-// extension.
-struct RtpHeaderExtensionParameters {
- // URI of this extension, as defined in RFC5285.
- std::string uri;
-
- // ID value that goes in the packet.
- int id = 0;
-
- // If true, the value in the header is encrypted.
- // TODO(deadbeef): Not implemented.
- bool encrypt = false;
-
- bool operator==(const RtpHeaderExtensionParameters& o) const {
- return uri == o.uri && id == o.id && encrypt == o.encrypt;
- }
- bool operator!=(const RtpHeaderExtensionParameters& o) const {
- return !(*this == o);
- }
-};
+// See webrtc/config.h. Has "uri" and "id" fields.
+// TODO(deadbeef): This is missing the "encrypt" flag, which is unimplemented.
+typedef RtpExtension RtpHeaderExtensionParameters;
struct RtpFecParameters {
// If unset, a value is chosen by the implementation.
+ // Works just like RtpEncodingParameters::ssrc.
rtc::Optional<uint32_t> ssrc;
FecMechanism mechanism = FecMechanism::RED;
+ // Constructors for convenience.
+ RtpFecParameters() = default;
+ explicit RtpFecParameters(FecMechanism mechanism) : mechanism(mechanism) {}
+ RtpFecParameters(FecMechanism mechanism, uint32_t ssrc)
+ : ssrc(ssrc), mechanism(mechanism) {}
+
bool operator==(const RtpFecParameters& o) const {
return ssrc == o.ssrc && mechanism == o.mechanism;
}
@@ -228,33 +235,48 @@
struct RtpRtxParameters {
// If unset, a value is chosen by the implementation.
+ // Works just like RtpEncodingParameters::ssrc.
rtc::Optional<uint32_t> ssrc;
+ // Constructors for convenience.
+ RtpRtxParameters() = default;
+ explicit RtpRtxParameters(uint32_t ssrc) : ssrc(ssrc) {}
+
bool operator==(const RtpRtxParameters& o) const { return ssrc == o.ssrc; }
bool operator!=(const RtpRtxParameters& o) const { return !(*this == o); }
};
struct RtpEncodingParameters {
// If unset, a value is chosen by the implementation.
+ //
+ // Note that the chosen value is NOT returned by GetParameters, because it
+ // may change due to an SSRC conflict, in which case the conflict is handled
+ // internally without any event. Another way of looking at this is that an
+ // unset SSRC acts as a "wildcard" SSRC.
rtc::Optional<uint32_t> ssrc;
// Can be used to reference a codec in the |codecs| member of the
// RtpParameters that contains this RtpEncodingParameters. If unset, the
- // implementation will choose the first possible codec.
- // TODO(deadbeef): Not implemented.
+ // implementation will choose the first possible codec (if a sender), or
+ // prepare to receive any codec (for a receiver).
+ // TODO(deadbeef): Not implemented. Implementation of RtpSender will always
+ // choose the first codec from the list.
rtc::Optional<int> codec_payload_type;
// Specifies the FEC mechanism, if set.
- // TODO(deadbeef): Not implemented.
+ // TODO(deadbeef): Not implemented. Current implementation will use whatever
+ // FEC codecs are available, including red+ulpfec.
rtc::Optional<RtpFecParameters> fec;
// Specifies the RTX parameters, if set.
- // TODO(deadbeef): Not implemented.
+ // TODO(deadbeef): Not implemented with PeerConnection senders/receivers.
rtc::Optional<RtpRtxParameters> rtx;
// Only used for audio. If set, determines whether or not discontinuous
// transmission will be used, if an available codec supports it. If not
// set, the implementation default setting will be used.
+ // TODO(deadbeef): Not implemented. Current implementation will use a CN
+ // codec as long as it's present.
rtc::Optional<DtxStatus> dtx;
// The relative priority of this encoding.
@@ -264,7 +286,13 @@
// If set, this represents the Transport Independent Application Specific
// maximum bandwidth defined in RFC3890. If unset, there is no maximum
// bitrate.
+ //
// Just called "maxBitrate" in ORTC spec.
+ //
+ // TODO(deadbeef): With ORTC RtpSenders, this currently sets the total
+ // bandwidth for the entire bandwidth estimator (audio and video). This is
+ // just always how "b=AS" was handled, but it's not correct and should be
+ // fixed.
rtc::Optional<int> max_bitrate_bps;
// TODO(deadbeef): Not implemented.
@@ -281,7 +309,7 @@
// For an RtpSender, set to true to cause this encoding to be sent, and false
// for it not to be sent. For an RtpReceiver, set to true to cause the
// encoding to be decoded, and false for it to be ignored.
- // TODO(deadbeef): RtpReceiver part is not implemented.
+ // TODO(deadbeef): Not implemented for PeerConnection RtpReceivers.
bool active = true;
// Value to use for RID RTP header extension.
@@ -320,7 +348,7 @@
cricket::MediaType kind = cricket::MEDIA_TYPE_AUDIO;
// Payload type used to identify this codec in RTP packets.
- // This MUST always be present, and must be unique across all codecs using
+ // This must always be present, and must be unique across all codecs using
// the same transport.
int payload_type = 0;
@@ -329,7 +357,9 @@
// The number of audio channels used. Unset for video codecs. If unset for
// audio, the implementation default is used.
- // TODO(deadbeef): The "implementation default" part is unimplemented.
+ // TODO(deadbeef): The "implementation default" part isn't fully implemented.
+ // Only defaults to 1, even though some codecs (such as opus) should really
+ // default to 2.
rtc::Optional<int> num_channels;
// The maximum packetization time to be used by an RtpSender.
@@ -343,12 +373,18 @@
rtc::Optional<int> ptime;
// Feedback mechanisms to be used for this codec.
- // TODO(deadbeef): Not implemented.
+ // TODO(deadbeef): Not implemented with PeerConnection senders/receivers.
std::vector<RtcpFeedback> rtcp_feedback;
// Codec-specific parameters that must be signaled to the remote party.
+ //
// Corresponds to "a=fmtp" parameters in SDP.
- // TODO(deadbeef): Not implemented.
+ //
+ // Contrary to ORTC, these parameters are named using all lowercase strings.
+ // This helps make the mapping to SDP simpler, if an application is using
+ // SDP. Boolean values are represented by the string "1".
+ //
+ // TODO(deadbeef): Not implemented with PeerConnection senders/receivers.
std::unordered_map<std::string, std::string> parameters;
bool operator==(const RtpCodecParameters& o) const {
@@ -370,7 +406,9 @@
// Supported RTP header extensions.
std::vector<RtpHeaderExtensionCapability> header_extensions;
- // Supported Forward Error Correction (FEC) mechanisms.
+ // Supported Forward Error Correction (FEC) mechanisms. Note that the RED,
+ // ulpfec and flexfec codecs used by these mechanisms will still appear in
+ // |codecs|.
std::vector<FecMechanism> fec;
bool operator==(const RtpCapabilities& o) const {
@@ -380,8 +418,8 @@
bool operator!=(const RtpCapabilities& o) const { return !(*this == o); }
};
-// Note that unlike in ORTC, an RtcpParameters is not included in
-// RtpParameters, because our API will include an additional "RtpTransport"
+// Note that unlike in ORTC, an RtcpParameters structure is not included in
+// RtpParameters, because our API includes an additional "RtpTransport"
// abstraction on which RTCP parameters are set.
struct RtpParameters {
// Used when calling getParameters/setParameters with a PeerConnection
@@ -397,7 +435,7 @@
std::vector<RtpCodecParameters> codecs;
- // TODO(deadbeef): Not implemented.
+ // TODO(deadbeef): Not implemented with PeerConnection senders/receivers.
std::vector<RtpHeaderExtensionParameters> header_extensions;
std::vector<RtpEncodingParameters> encodings;
diff --git a/config.cc b/config.cc
index 6ffd1c3..e0c490d 100644
--- a/config.cc
+++ b/config.cc
@@ -72,6 +72,9 @@
"http://www.webrtc.org/experiments/rtp-hdrext/playout-delay";
const int RtpExtension::kPlayoutDelayDefaultId = 6;
+const int RtpExtension::kMinId = 1;
+const int RtpExtension::kMaxId = 14;
+
bool RtpExtension::IsSupportedForAudio(const std::string& uri) {
return uri == webrtc::RtpExtension::kAudioLevelUri ||
uri == webrtc::RtpExtension::kTransportSequenceNumberUri;
diff --git a/config.h b/config.h
index 22b279c..f8c9e8b 100644
--- a/config.h
+++ b/config.h
@@ -96,6 +96,10 @@
static const char* kPlayoutDelayUri;
static const int kPlayoutDelayDefaultId;
+ // Inclusive min and max IDs for one-byte header extensions, per RFC5285.
+ static const int kMinId;
+ static const int kMaxId;
+
std::string uri;
int id;
};
diff --git a/media/base/codec.h b/media/base/codec.h
index 0aa0d58..5e49785 100644
--- a/media/base/codec.h
+++ b/media/base/codec.h
@@ -26,6 +26,7 @@
class FeedbackParam {
public:
+ FeedbackParam() = default;
FeedbackParam(const std::string& id, const std::string& param)
: id_(id),
param_(param) {
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index d9a79cc..fc59fa7 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -206,6 +206,8 @@
return "";
return send_streams_[0].cname;
}
+ const RtcpParameters& send_rtcp_parameters() { return send_rtcp_parameters_; }
+ const RtcpParameters& recv_rtcp_parameters() { return recv_rtcp_parameters_; }
bool ready_to_send() const {
return ready_to_send_;
@@ -246,6 +248,12 @@
send_extensions_ = extensions;
return true;
}
+ void set_send_rtcp_parameters(const RtcpParameters& params) {
+ send_rtcp_parameters_ = params;
+ }
+ void set_recv_rtcp_parameters(const RtcpParameters& params) {
+ recv_rtcp_parameters_ = params;
+ }
virtual void OnPacketReceived(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time) {
rtp_packets_.push_back(std::string(packet->data<char>(), packet->size()));
@@ -278,6 +286,8 @@
std::list<std::string> rtcp_packets_;
std::vector<StreamParams> send_streams_;
std::vector<StreamParams> receive_streams_;
+ RtcpParameters send_rtcp_parameters_;
+ RtcpParameters recv_rtcp_parameters_;
std::set<uint32_t> muted_streams_;
std::map<uint32_t, webrtc::RtpParameters> rtp_send_parameters_;
std::map<uint32_t, webrtc::RtpParameters> rtp_receive_parameters_;
@@ -318,6 +328,7 @@
const AudioOptions& options() const { return options_; }
int max_bps() const { return max_bps_; }
virtual bool SetSendParameters(const AudioSendParameters& params) {
+ set_send_rtcp_parameters(params.rtcp);
return (SetSendCodecs(params.codecs) &&
SetSendRtpHeaderExtensions(params.extensions) &&
SetMaxSendBandwidth(params.max_bandwidth_bps) &&
@@ -325,6 +336,7 @@
}
virtual bool SetRecvParameters(const AudioRecvParameters& params) {
+ set_recv_rtcp_parameters(params.rtcp);
return (SetRecvCodecs(params.codecs) &&
SetRecvRtpHeaderExtensions(params.extensions));
}
@@ -519,11 +531,13 @@
}
int max_bps() const { return max_bps_; }
bool SetSendParameters(const VideoSendParameters& params) override {
+ set_send_rtcp_parameters(params.rtcp);
return (SetSendCodecs(params.codecs) &&
SetSendRtpHeaderExtensions(params.extensions) &&
SetMaxSendBandwidth(params.max_bandwidth_bps));
}
bool SetRecvParameters(const VideoRecvParameters& params) override {
+ set_recv_rtcp_parameters(params.rtcp);
return (SetRecvCodecs(params.codecs) &&
SetRecvRtpHeaderExtensions(params.extensions));
}
@@ -643,10 +657,12 @@
int max_bps() const { return max_bps_; }
virtual bool SetSendParameters(const DataSendParameters& params) {
+ set_send_rtcp_parameters(params.rtcp);
return (SetSendCodecs(params.codecs) &&
SetMaxSendBandwidth(params.max_bandwidth_bps));
}
virtual bool SetRecvParameters(const DataRecvParameters& params) {
+ set_recv_rtcp_parameters(params.rtcp);
return SetRecvCodecs(params.codecs);
}
virtual bool SetSend(bool send) { return set_sending(send); }
diff --git a/ortc/BUILD.gn b/ortc/BUILD.gn
index a919511..ebba3b8 100644
--- a/ortc/BUILD.gn
+++ b/ortc/BUILD.gn
@@ -12,18 +12,69 @@
import("//build/config/android/rules.gni")
}
+rtc_static_library("ortc") {
+ defines = []
+ sources = [
+ "ortcfactory.cc",
+ "ortcfactory.h",
+ "ortcrtpreceiveradapter.cc",
+ "ortcrtpreceiveradapter.h",
+ "ortcrtpsenderadapter.cc",
+ "ortcrtpsenderadapter.h",
+ "rtpparametersconversion.cc",
+ "rtpparametersconversion.h",
+ "rtptransportadapter.cc",
+ "rtptransportadapter.h",
+ "rtptransportcontrolleradapter.cc",
+ "rtptransportcontrolleradapter.h",
+ ]
+
+ # TODO(deadbeef): Create a separate target for the common things ORTC and
+ # PeerConnection code shares, so that ortc can depend on that instead of
+ # libjingle_peerconnection.
+ deps = [
+ "../pc:libjingle_peerconnection",
+ ]
+
+ public_deps = [
+ "../api:ortc_api",
+ ]
+
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+}
+
if (rtc_include_tests) {
rtc_test("ortc_unittests") {
testonly = true
sources = [
- "dummy_test.cc",
+ "ortcfactory_integrationtest.cc",
+ "ortcfactory_unittest.cc",
+ "ortcrtpreceiver_unittest.cc",
+ "ortcrtpsender_unittest.cc",
+ "rtpparametersconversion_unittest.cc",
+ "rtptransport_unittest.cc",
+ "rtptransportcontroller_unittest.cc",
+ "testrtpparameters.cc",
+ "testrtpparameters.h",
]
deps = [
- "../base:rtc_base_tests_main",
+ ":ortc",
+ "../base:rtc_base_tests_utils",
+ "../media:rtc_unittest_main",
+ "../pc:pc_test_utils",
+ "../system_wrappers:metrics_default",
]
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+
if (is_android) {
deps += [ "//testing/android/native_test:native_test_support" ]
}
diff --git a/ortc/DEPS b/ortc/DEPS
new file mode 100644
index 0000000..de152dd
--- /dev/null
+++ b/ortc/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+ "+webrtc/api",
+ "+webrtc/base",
+ "+webrtc/call",
+ "+webrtc/logging/rtc_event_log",
+ "+webrtc/media",
+ "+webrtc/modules/audio_coding",
+ "+webrtc/p2p",
+ "+webrtc/pc",
+
+ "+webrtc/modules/rtp_rtcp",
+ "+webrtc/system_wrappers",
+
+ "+webrtc/modules/audio_device",
+ "+webrtc/modules/video_coding",
+ "+webrtc/modules/video_render",
+]
diff --git a/ortc/OWNERS b/ortc/OWNERS
new file mode 100644
index 0000000..d51c5e4
--- /dev/null
+++ b/ortc/OWNERS
@@ -0,0 +1,6 @@
+deadbeef@webrtc.org
+
+# These are for the common case of adding or renaming files. If you're doing
+# structural changes, please get a review from a reviewer in this file.
+per-file *.gn=*
+per-file *.gni=*
diff --git a/ortc/dummy_test.cc b/ortc/dummy_test.cc
deleted file mode 100644
index 1e0dc41..0000000
--- a/ortc/dummy_test.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2017 The WebRTC Project Authors. All rights reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "webrtc/test/gtest.h"
-
-// This is just a placeholder test so that trybots can be updated to run the
-// new "ortc_unittests" target.
-TEST(DummyOrtcTest, Test) {
-}
diff --git a/ortc/ortcfactory.cc b/ortc/ortcfactory.cc
new file mode 100644
index 0000000..c0d54d1
--- /dev/null
+++ b/ortc/ortcfactory.cc
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/ortcfactory.h"
+
+#include <sstream>
+#include <vector>
+#include <utility> // For std::move.
+
+#include "webrtc/api/proxy.h"
+#include "webrtc/api/mediastreamtrackproxy.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/videosourceproxy.h"
+#include "webrtc/base/asyncpacketsocket.h"
+#include "webrtc/base/bind.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
+#include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory.h"
+#include "webrtc/ortc/ortcrtpreceiveradapter.h"
+#include "webrtc/ortc/ortcrtpsenderadapter.h"
+#include "webrtc/ortc/rtpparametersconversion.h"
+#include "webrtc/ortc/rtptransportadapter.h"
+#include "webrtc/ortc/rtptransportcontrolleradapter.h"
+#include "webrtc/p2p/base/basicpacketsocketfactory.h"
+#include "webrtc/p2p/base/udptransport.h"
+#include "webrtc/pc/channelmanager.h"
+#include "webrtc/pc/localaudiosource.h"
+#include "webrtc/pc/audiotrack.h"
+#include "webrtc/pc/videocapturertracksource.h"
+#include "webrtc/pc/videotrack.h"
+
+namespace {
+
+const int kDefaultRtcpCnameLength = 16;
+
+// Asserts that all of the built-in capabilities can be converted to
+// RtpCapabilities. If they can't, something's wrong (for example, maybe a new
+// feedback mechanism is supported, but an enum value wasn't added to
+// rtpparameters.h).
+template <typename C>
+webrtc::RtpCapabilities ToRtpCapabilitiesWithAsserts(
+ const std::vector<C>& cricket_codecs,
+ const cricket::RtpHeaderExtensions& cricket_extensions) {
+ webrtc::RtpCapabilities capabilities =
+ webrtc::ToRtpCapabilities(cricket_codecs, cricket_extensions);
+ RTC_DCHECK_EQ(capabilities.codecs.size(), cricket_codecs.size());
+ for (size_t i = 0; i < capabilities.codecs.size(); ++i) {
+ RTC_DCHECK_EQ(capabilities.codecs[i].rtcp_feedback.size(),
+ cricket_codecs[i].feedback_params.params().size());
+ }
+ RTC_DCHECK_EQ(capabilities.header_extensions.size(),
+ cricket_extensions.size());
+ return capabilities;
+}
+
+} // namespace
+
+namespace webrtc {
+
+// Note that this proxy class uses the network thread as the "worker" thread.
+BEGIN_OWNED_PROXY_MAP(OrtcFactory)
+PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_METHOD0(RTCErrorOr<std::unique_ptr<RtpTransportControllerInterface>>,
+ CreateRtpTransportController)
+PROXY_METHOD4(RTCErrorOr<std::unique_ptr<RtpTransportInterface>>,
+ CreateRtpTransport,
+ const RtcpParameters&,
+ PacketTransportInterface*,
+ PacketTransportInterface*,
+ RtpTransportControllerInterface*)
+PROXY_CONSTMETHOD1(RtpCapabilities,
+ GetRtpSenderCapabilities,
+ cricket::MediaType)
+PROXY_METHOD2(RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>,
+ CreateRtpSender,
+ rtc::scoped_refptr<MediaStreamTrackInterface>,
+ RtpTransportInterface*)
+PROXY_METHOD2(RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>,
+ CreateRtpSender,
+ cricket::MediaType,
+ RtpTransportInterface*)
+PROXY_CONSTMETHOD1(RtpCapabilities,
+ GetRtpReceiverCapabilities,
+ cricket::MediaType)
+PROXY_METHOD2(RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>,
+ CreateRtpReceiver,
+ cricket::MediaType,
+ RtpTransportInterface*)
+PROXY_WORKER_METHOD3(RTCErrorOr<std::unique_ptr<UdpTransportInterface>>,
+ CreateUdpTransport,
+ int,
+ uint16_t,
+ uint16_t)
+PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,
+ CreateAudioSource,
+ const cricket::AudioOptions&)
+PROXY_METHOD2(rtc::scoped_refptr<VideoTrackSourceInterface>,
+ CreateVideoSource,
+ std::unique_ptr<cricket::VideoCapturer>,
+ const MediaConstraintsInterface*)
+PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>,
+ CreateVideoTrack,
+ const std::string&,
+ VideoTrackSourceInterface*)
+PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,
+ CreateAudioTrack,
+ const std::string&,
+ AudioSourceInterface*)
+END_PROXY_MAP()
+
+// static
+RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> OrtcFactory::Create(
+ rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm,
+ std::unique_ptr<cricket::MediaEngineInterface> media_engine) {
+ // Hop to signaling thread if needed.
+ if (signaling_thread && !signaling_thread->IsCurrent()) {
+ return signaling_thread
+ ->Invoke<RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>>>(
+ RTC_FROM_HERE,
+ rtc::Bind(&OrtcFactory::Create_s, network_thread, signaling_thread,
+ network_manager, socket_factory, adm,
+ media_engine.release()));
+ }
+ return Create_s(network_thread, signaling_thread, network_manager,
+ socket_factory, adm, media_engine.release());
+}
+
+RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> OrtcFactoryInterface::Create(
+ rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm) {
+ return OrtcFactory::Create(network_thread, signaling_thread, network_manager,
+ socket_factory, adm, nullptr);
+}
+
+OrtcFactory::OrtcFactory(rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm)
+ : network_thread_(network_thread),
+ signaling_thread_(signaling_thread),
+ network_manager_(network_manager),
+ socket_factory_(socket_factory),
+ adm_(adm),
+ null_event_log_(RtcEventLog::CreateNull()),
+ audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()) {
+ if (!rtc::CreateRandomString(kDefaultRtcpCnameLength, &default_cname_)) {
+ LOG(LS_ERROR) << "Failed to generate CNAME?";
+ RTC_NOTREACHED();
+ }
+ if (!network_thread_) {
+ owned_network_thread_ = rtc::Thread::CreateWithSocketServer();
+ owned_network_thread_->Start();
+ network_thread_ = owned_network_thread_.get();
+ }
+
+ // The worker thread is created internally because it's an implementation
+ // detail, and consumers of the API don't need to really know about it.
+ worker_thread_ = rtc::Thread::Create();
+ worker_thread_->Start();
+
+ if (signaling_thread_) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ } else {
+ signaling_thread_ = rtc::Thread::Current();
+ if (!signaling_thread_) {
+ // If this thread isn't already wrapped by an rtc::Thread, create a
+ // wrapper and own it in this class.
+ signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread();
+ wraps_signaling_thread_ = true;
+ }
+ }
+ if (!network_manager_) {
+ owned_network_manager_.reset(new rtc::BasicNetworkManager());
+ network_manager_ = owned_network_manager_.get();
+ }
+ if (!socket_factory_) {
+ owned_socket_factory_.reset(
+ new rtc::BasicPacketSocketFactory(network_thread_));
+ socket_factory_ = owned_socket_factory_.get();
+ }
+}
+
+OrtcFactory::~OrtcFactory() {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ if (wraps_signaling_thread_) {
+ rtc::ThreadManager::Instance()->UnwrapCurrentThread();
+ }
+}
+
+RTCErrorOr<std::unique_ptr<RtpTransportControllerInterface>>
+OrtcFactory::CreateRtpTransportController() {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ return RtpTransportControllerAdapter::CreateProxied(
+ cricket::MediaConfig(), channel_manager_.get(), null_event_log_.get(),
+ signaling_thread_, worker_thread_.get());
+}
+
+RTCErrorOr<std::unique_ptr<RtpTransportInterface>>
+OrtcFactory::CreateRtpTransport(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerInterface* transport_controller) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ RtcpParameters copied_parameters = rtcp_parameters;
+ if (copied_parameters.cname.empty()) {
+ copied_parameters.cname = default_cname_;
+ }
+ if (transport_controller) {
+ return transport_controller->GetInternal()->CreateProxiedRtpTransport(
+ copied_parameters, rtp, rtcp);
+ } else {
+ // If |transport_controller| is null, create one automatically, which the
+ // returned RtpTransport will own.
+ auto controller_result = CreateRtpTransportController();
+ if (!controller_result.ok()) {
+ return controller_result.MoveError();
+ }
+ auto controller = controller_result.MoveValue();
+ auto transport_result =
+ controller->GetInternal()->CreateProxiedRtpTransport(copied_parameters,
+ rtp, rtcp);
+ // If RtpTransport was successfully created, transfer ownership of
+ // |rtp_transport_controller|. Otherwise it will go out of scope and be
+ // deleted automatically.
+ if (transport_result.ok()) {
+ transport_result.value()
+ ->GetInternal()
+ ->TakeOwnershipOfRtpTransportController(std::move(controller));
+ }
+ return transport_result;
+ }
+}
+
+RtpCapabilities OrtcFactory::GetRtpSenderCapabilities(
+ cricket::MediaType kind) const {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ switch (kind) {
+ case cricket::MEDIA_TYPE_AUDIO: {
+ cricket::AudioCodecs cricket_codecs;
+ cricket::RtpHeaderExtensions cricket_extensions;
+ channel_manager_->GetSupportedAudioSendCodecs(&cricket_codecs);
+ channel_manager_->GetSupportedAudioRtpHeaderExtensions(
+ &cricket_extensions);
+ return ToRtpCapabilitiesWithAsserts(cricket_codecs, cricket_extensions);
+ }
+ case cricket::MEDIA_TYPE_VIDEO: {
+ cricket::VideoCodecs cricket_codecs;
+ cricket::RtpHeaderExtensions cricket_extensions;
+ channel_manager_->GetSupportedVideoCodecs(&cricket_codecs);
+ channel_manager_->GetSupportedVideoRtpHeaderExtensions(
+ &cricket_extensions);
+ return ToRtpCapabilitiesWithAsserts(cricket_codecs, cricket_extensions);
+ }
+ case cricket::MEDIA_TYPE_DATA:
+ return RtpCapabilities();
+ }
+ // Not reached; avoids compile warning.
+ FATAL();
+}
+
+RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>
+OrtcFactory::CreateRtpSender(
+ rtc::scoped_refptr<MediaStreamTrackInterface> track,
+ RtpTransportInterface* transport) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ if (!track) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Cannot pass null track into CreateRtpSender.");
+ }
+ auto result =
+ CreateRtpSender(cricket::MediaTypeFromString(track->kind()), transport);
+ if (!result.ok()) {
+ return result;
+ }
+ auto err = result.value()->SetTrack(track);
+ if (!err.ok()) {
+ return std::move(err);
+ }
+ return result;
+}
+
+RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>
+OrtcFactory::CreateRtpSender(cricket::MediaType kind,
+ RtpTransportInterface* transport) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ if (kind == cricket::MEDIA_TYPE_DATA) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Cannot create data RtpSender.");
+ }
+ if (!transport) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Cannot pass null transport into CreateRtpSender.");
+ }
+ return transport->GetInternal()
+ ->rtp_transport_controller()
+ ->CreateProxiedRtpSender(kind, transport);
+}
+
+RtpCapabilities OrtcFactory::GetRtpReceiverCapabilities(
+ cricket::MediaType kind) const {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ switch (kind) {
+ case cricket::MEDIA_TYPE_AUDIO: {
+ cricket::AudioCodecs cricket_codecs;
+ cricket::RtpHeaderExtensions cricket_extensions;
+ channel_manager_->GetSupportedAudioReceiveCodecs(&cricket_codecs);
+ channel_manager_->GetSupportedAudioRtpHeaderExtensions(
+ &cricket_extensions);
+ return ToRtpCapabilitiesWithAsserts(cricket_codecs, cricket_extensions);
+ }
+ case cricket::MEDIA_TYPE_VIDEO: {
+ cricket::VideoCodecs cricket_codecs;
+ cricket::RtpHeaderExtensions cricket_extensions;
+ channel_manager_->GetSupportedVideoCodecs(&cricket_codecs);
+ channel_manager_->GetSupportedVideoRtpHeaderExtensions(
+ &cricket_extensions);
+ return ToRtpCapabilitiesWithAsserts(cricket_codecs, cricket_extensions);
+ }
+ case cricket::MEDIA_TYPE_DATA:
+ return RtpCapabilities();
+ }
+ // Not reached; avoids compile warning.
+ FATAL();
+}
+
+RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>
+OrtcFactory::CreateRtpReceiver(cricket::MediaType kind,
+ RtpTransportInterface* transport) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ if (kind == cricket::MEDIA_TYPE_DATA) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Cannot create data RtpReceiver.");
+ }
+ if (!transport) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Cannot pass null transport into CreateRtpReceiver.");
+ }
+ return transport->GetInternal()
+ ->rtp_transport_controller()
+ ->CreateProxiedRtpReceiver(kind, transport);
+}
+
+// UdpTransport expects all methods to be called on one thread, which needs to
+// be the network thread, since that's where its socket can safely be used. So
+// return a proxy to the created UdpTransport.
+BEGIN_OWNED_PROXY_MAP(UdpTransport)
+PROXY_WORKER_THREAD_DESTRUCTOR()
+PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetLocalAddress)
+PROXY_WORKER_METHOD1(bool, SetRemoteAddress, const rtc::SocketAddress&)
+PROXY_WORKER_CONSTMETHOD0(rtc::SocketAddress, GetRemoteAddress)
+protected:
+rtc::PacketTransportInternal* GetInternal() override {
+ return internal();
+}
+END_PROXY_MAP()
+
+RTCErrorOr<std::unique_ptr<UdpTransportInterface>>
+OrtcFactory::CreateUdpTransport(int family,
+ uint16_t min_port,
+ uint16_t max_port) {
+ RTC_DCHECK_RUN_ON(network_thread_);
+ if (family != AF_INET && family != AF_INET6) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Address family must be AF_INET or AF_INET6.");
+ }
+ if (min_port > max_port) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Port range invalid; minimum port must be less than "
+ "or equal to max port.");
+ }
+ std::unique_ptr<rtc::AsyncPacketSocket> socket(
+ socket_factory_->CreateUdpSocket(
+ rtc::SocketAddress(rtc::GetAnyIP(family), 0), min_port, max_port));
+ if (!socket) {
+ // Only log at warning level, because this method may be called with
+ // specific port ranges to determine if a port is available, expecting the
+ // possibility of an error.
+ LOG_AND_RETURN_ERROR_EX(RTCErrorType::RESOURCE_EXHAUSTED,
+ "Local socket allocation failure.", LS_WARNING);
+ }
+ LOG(LS_INFO) << "Created UDP socket with address "
+ << socket->GetLocalAddress().ToSensitiveString() << ".";
+ // Make a unique debug name (for logging/diagnostics only).
+ std::ostringstream oss;
+ static int udp_id = 0;
+ oss << "udp" << udp_id++;
+ return UdpTransportProxyWithInternal<cricket::UdpTransport>::Create(
+ signaling_thread_, network_thread_,
+ std::unique_ptr<cricket::UdpTransport>(
+ new cricket::UdpTransport(oss.str(), std::move(socket))));
+}
+
+rtc::scoped_refptr<AudioSourceInterface> OrtcFactory::CreateAudioSource(
+ const cricket::AudioOptions& options) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ return rtc::scoped_refptr<LocalAudioSource>(
+ LocalAudioSource::Create(&options));
+}
+
+rtc::scoped_refptr<VideoTrackSourceInterface> OrtcFactory::CreateVideoSource(
+ std::unique_ptr<cricket::VideoCapturer> capturer,
+ const MediaConstraintsInterface* constraints) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ rtc::scoped_refptr<VideoTrackSourceInterface> source(
+ VideoCapturerTrackSource::Create(
+ worker_thread_.get(), std::move(capturer), constraints, false));
+ return VideoTrackSourceProxy::Create(signaling_thread_, worker_thread_.get(),
+ source);
+}
+
+rtc::scoped_refptr<VideoTrackInterface> OrtcFactory::CreateVideoTrack(
+ const std::string& id,
+ VideoTrackSourceInterface* source) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ rtc::scoped_refptr<VideoTrackInterface> track(VideoTrack::Create(id, source));
+ return VideoTrackProxy::Create(signaling_thread_, worker_thread_.get(),
+ track);
+}
+
+rtc::scoped_refptr<AudioTrackInterface> OrtcFactory::CreateAudioTrack(
+ const std::string& id,
+ AudioSourceInterface* source) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ rtc::scoped_refptr<AudioTrackInterface> track(AudioTrack::Create(id, source));
+ return AudioTrackProxy::Create(signaling_thread_, track);
+}
+
+// static
+RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> OrtcFactory::Create_s(
+ rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm,
+ cricket::MediaEngineInterface* media_engine) {
+ // Add the unique_ptr wrapper back.
+ std::unique_ptr<cricket::MediaEngineInterface> owned_media_engine(
+ media_engine);
+ std::unique_ptr<OrtcFactory> new_factory(new OrtcFactory(
+ network_thread, signaling_thread, network_manager, socket_factory, adm));
+ RTCError err = new_factory->Initialize(std::move(owned_media_engine));
+ if (!err.ok()) {
+ return std::move(err);
+ }
+ // Return a proxy so that any calls on the returned object (including
+ // destructor) happen on the signaling thread.
+ rtc::Thread* signaling = new_factory->signaling_thread();
+ rtc::Thread* network = new_factory->network_thread();
+ return OrtcFactoryProxy::Create(signaling, network, std::move(new_factory));
+}
+
+RTCError OrtcFactory::Initialize(
+ std::unique_ptr<cricket::MediaEngineInterface> media_engine) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ // TODO(deadbeef): Get rid of requirement to hop to worker thread here.
+ if (!media_engine) {
+ media_engine =
+ worker_thread_->Invoke<std::unique_ptr<cricket::MediaEngineInterface>>(
+ RTC_FROM_HERE, rtc::Bind(&OrtcFactory::CreateMediaEngine_w, this));
+ }
+
+ channel_manager_.reset(new cricket::ChannelManager(
+ std::move(media_engine), worker_thread_.get(), network_thread_));
+ channel_manager_->SetVideoRtxEnabled(true);
+ if (!channel_manager_->Init()) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to initialize ChannelManager.");
+ }
+ return RTCError::OK();
+}
+
+std::unique_ptr<cricket::MediaEngineInterface>
+OrtcFactory::CreateMediaEngine_w() {
+ RTC_DCHECK_RUN_ON(worker_thread_.get());
+ // The null arguments are optional factories that could be passed into the
+ // OrtcFactory, but aren't yet.
+ //
+ // Note that |adm_| may be null, in which case the platform-specific default
+ // AudioDeviceModule will be used.
+ return std::unique_ptr<cricket::MediaEngineInterface>(
+ cricket::WebRtcMediaEngineFactory::Create(adm_, audio_decoder_factory_,
+ nullptr, nullptr, nullptr));
+}
+
+} // namespace webrtc
diff --git a/ortc/ortcfactory.h b/ortc/ortcfactory.h
new file mode 100644
index 0000000..71f525d
--- /dev/null
+++ b/ortc/ortcfactory.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_ORTCFACTORY_H_
+#define WEBRTC_ORTC_ORTCFACTORY_H_
+
+#include <memory>
+#include <string>
+
+#include "webrtc/api/ortc/ortcfactoryinterface.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/media/base/mediaengine.h"
+#include "webrtc/media/engine/webrtcmediaengine.h"
+#include "webrtc/pc/channelmanager.h"
+
+namespace webrtc {
+
+// Implementation of OrtcFactoryInterface.
+//
+// See ortcfactoryinterface.h for documentation.
+class OrtcFactory : public OrtcFactoryInterface {
+ public:
+ ~OrtcFactory() override;
+
+ // Internal-only Create method that allows passing in a fake media engine,
+ // for testing.
+ static RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> Create(
+ rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm,
+ std::unique_ptr<cricket::MediaEngineInterface> media_engine);
+
+ RTCErrorOr<std::unique_ptr<RtpTransportControllerInterface>>
+ CreateRtpTransportController() override;
+
+ RTCErrorOr<std::unique_ptr<RtpTransportInterface>> CreateRtpTransport(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerInterface* transport_controller) override;
+
+ RtpCapabilities GetRtpSenderCapabilities(
+ cricket::MediaType kind) const override;
+
+ RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> CreateRtpSender(
+ rtc::scoped_refptr<MediaStreamTrackInterface> track,
+ RtpTransportInterface* transport) override;
+
+ RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> CreateRtpSender(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport) override;
+
+ RtpCapabilities GetRtpReceiverCapabilities(
+ cricket::MediaType kind) const override;
+
+ RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>> CreateRtpReceiver(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport) override;
+
+ RTCErrorOr<std::unique_ptr<UdpTransportInterface>>
+ CreateUdpTransport(int family, uint16_t min_port, uint16_t max_port) override;
+
+ rtc::scoped_refptr<AudioSourceInterface> CreateAudioSource(
+ const cricket::AudioOptions& options) override;
+
+ rtc::scoped_refptr<VideoTrackSourceInterface> CreateVideoSource(
+ std::unique_ptr<cricket::VideoCapturer> capturer,
+ const MediaConstraintsInterface* constraints) override;
+
+ rtc::scoped_refptr<VideoTrackInterface> CreateVideoTrack(
+ const std::string& id,
+ VideoTrackSourceInterface* source) override;
+
+ rtc::scoped_refptr<AudioTrackInterface> CreateAudioTrack(
+ const std::string& id,
+ AudioSourceInterface* source) override;
+
+ rtc::Thread* network_thread() { return network_thread_; }
+ rtc::Thread* worker_thread() { return worker_thread_.get(); }
+ rtc::Thread* signaling_thread() { return signaling_thread_; }
+
+ private:
+ // Should only be called by OrtcFactoryInterface::Create.
+ OrtcFactory(rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm);
+
+ // Thread::Invoke doesn't support move-only arguments, so we need to remove
+ // the unique_ptr wrapper from media_engine. TODO(deadbeef): Fix this.
+ static RTCErrorOr<std::unique_ptr<OrtcFactoryInterface>> Create_s(
+ rtc::Thread* network_thread,
+ rtc::Thread* signaling_thread,
+ rtc::NetworkManager* network_manager,
+ rtc::PacketSocketFactory* socket_factory,
+ AudioDeviceModule* adm,
+ cricket::MediaEngineInterface* media_engine);
+
+ // Performs initialization that can fail. Called by factory method after
+ // construction, and if it fails, no object is returned.
+ RTCError Initialize(
+ std::unique_ptr<cricket::MediaEngineInterface> media_engine);
+ std::unique_ptr<cricket::MediaEngineInterface> CreateMediaEngine_w();
+
+ // Threads and networking objects.
+ rtc::Thread* network_thread_;
+ rtc::Thread* signaling_thread_;
+ rtc::NetworkManager* network_manager_;
+ rtc::PacketSocketFactory* socket_factory_;
+ AudioDeviceModule* adm_;
+ // If we created/own the objects above, these will be non-null and thus will
+ // be released automatically upon destruction.
+ std::unique_ptr<rtc::Thread> owned_network_thread_;
+ bool wraps_signaling_thread_ = false;
+ std::unique_ptr<rtc::NetworkManager> owned_network_manager_;
+ std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory_;
+ // We always own the worker thread.
+ std::unique_ptr<rtc::Thread> worker_thread_;
+ // Media-releated objects.
+ std::unique_ptr<RtcEventLog> null_event_log_;
+ rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory_;
+ std::unique_ptr<cricket::ChannelManager> channel_manager_;
+ // Default CNAME to use for RtpTransports if none is passed in.
+ std::string default_cname_;
+
+ friend class OrtcFactoryInterface;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(OrtcFactory);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_ORTCFACTORY_H_
diff --git a/ortc/ortcfactory_integrationtest.cc b/ortc/ortcfactory_integrationtest.cc
new file mode 100644
index 0000000..e935f06
--- /dev/null
+++ b/ortc/ortcfactory_integrationtest.cc
@@ -0,0 +1,512 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <utility> // For std::pair, std::move.
+
+#include "webrtc/api/ortc/ortcfactoryinterface.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/fakenetwork.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/virtualsocketserver.h"
+#include "webrtc/ortc/testrtpparameters.h"
+#include "webrtc/p2p/base/udptransport.h"
+#include "webrtc/pc/test/fakeaudiocapturemodule.h"
+#include "webrtc/pc/test/fakeperiodicvideocapturer.h"
+#include "webrtc/pc/test/fakevideotrackrenderer.h"
+
+namespace {
+
+const int kDefaultTimeout = 10000; // 10 seconds.
+// Default number of audio/video frames to wait for before considering a test a
+// success.
+const int kDefaultNumFrames = 3;
+const rtc::IPAddress kIPv4LocalHostAddress =
+ rtc::IPAddress(0x7F000001); // 127.0.0.1
+
+} // namespace
+
+namespace webrtc {
+
+// Used to test that things work end-to-end when using the default
+// implementations of threads/etc. provided by OrtcFactory, with the exception
+// of using a virtual network.
+//
+// By default, the virtual network manager doesn't enumerate any networks, but
+// sockets can still be created in this state.
+class OrtcFactoryIntegrationTest : public testing::Test {
+ public:
+ OrtcFactoryIntegrationTest()
+ : virtual_socket_server_(&physical_socket_server_),
+ network_thread_(&virtual_socket_server_),
+ fake_audio_capture_module1_(FakeAudioCaptureModule::Create()),
+ fake_audio_capture_module2_(FakeAudioCaptureModule::Create()) {
+ // Sockets are bound to the ANY address, so this is needed to tell the
+ // virtual network which address to use in this case.
+ virtual_socket_server_.SetDefaultRoute(kIPv4LocalHostAddress);
+ network_thread_.Start();
+ // Need to create after network thread is started.
+ ortc_factory1_ = OrtcFactoryInterface::Create(
+ &network_thread_, nullptr, &fake_network_manager_,
+ nullptr, fake_audio_capture_module1_)
+ .MoveValue();
+ ortc_factory2_ = OrtcFactoryInterface::Create(
+ &network_thread_, nullptr, &fake_network_manager_,
+ nullptr, fake_audio_capture_module2_)
+ .MoveValue();
+ }
+
+ protected:
+ typedef std::pair<std::unique_ptr<UdpTransportInterface>,
+ std::unique_ptr<UdpTransportInterface>>
+ UdpTransportPair;
+ typedef std::pair<std::unique_ptr<RtpTransportInterface>,
+ std::unique_ptr<RtpTransportInterface>>
+ RtpTransportPair;
+ typedef std::pair<std::unique_ptr<RtpTransportControllerInterface>,
+ std::unique_ptr<RtpTransportControllerInterface>>
+ RtpTransportControllerPair;
+
+ // Helper function that creates one UDP transport each for |ortc_factory1_|
+ // and |ortc_factory2_|, and connects them.
+ UdpTransportPair CreateAndConnectUdpTransportPair() {
+ auto transport1 = ortc_factory1_->CreateUdpTransport(AF_INET).MoveValue();
+ auto transport2 = ortc_factory2_->CreateUdpTransport(AF_INET).MoveValue();
+ transport1->SetRemoteAddress(
+ rtc::SocketAddress(virtual_socket_server_.GetDefaultRoute(AF_INET),
+ transport2->GetLocalAddress().port()));
+ transport2->SetRemoteAddress(
+ rtc::SocketAddress(virtual_socket_server_.GetDefaultRoute(AF_INET),
+ transport1->GetLocalAddress().port()));
+ return {std::move(transport1), std::move(transport2)};
+ }
+
+ // Creates one transport controller each for |ortc_factory1_| and
+ // |ortc_factory2_|.
+ RtpTransportControllerPair CreateRtpTransportControllerPair() {
+ return {ortc_factory1_->CreateRtpTransportController().MoveValue(),
+ ortc_factory2_->CreateRtpTransportController().MoveValue()};
+ }
+
+ // Helper function that creates a pair of RtpTransports between
+ // |ortc_factory1_| and |ortc_factory2_|. Expected to be called with the
+ // result of CreateAndConnectUdpTransportPair. |rtcp_udp_transports| can be
+ // empty if RTCP muxing is used. |transport_controllers| can be empty if
+ // these transports are being created using a default transport controller.
+ RtpTransportPair CreateRtpTransportPair(
+ const RtcpParameters& rtcp_parameters,
+ const UdpTransportPair& rtp_udp_transports,
+ const UdpTransportPair& rtcp_udp_transports,
+ const RtpTransportControllerPair& transport_controllers) {
+ auto transport_result1 = ortc_factory1_->CreateRtpTransport(
+ rtcp_parameters, rtp_udp_transports.first.get(),
+ rtcp_udp_transports.first.get(), transport_controllers.first.get());
+ auto transport_result2 = ortc_factory2_->CreateRtpTransport(
+ rtcp_parameters, rtp_udp_transports.second.get(),
+ rtcp_udp_transports.second.get(), transport_controllers.second.get());
+ return {transport_result1.MoveValue(), transport_result2.MoveValue()};
+ }
+
+ // For convenience when |rtcp_udp_transports| and |transport_controllers|
+ // aren't needed.
+ RtpTransportPair CreateRtpTransportPair(
+ const RtcpParameters& rtcp_parameters,
+ const UdpTransportPair& rtp_udp_transports) {
+ return CreateRtpTransportPair(rtcp_parameters, rtp_udp_transports,
+ UdpTransportPair(),
+ RtpTransportControllerPair());
+ }
+
+ // Ends up using fake audio capture module, which was passed into OrtcFactory
+ // on creation.
+ rtc::scoped_refptr<webrtc::AudioTrackInterface> CreateLocalAudioTrack(
+ const std::string& id,
+ OrtcFactoryInterface* ortc_factory) {
+ // Disable echo cancellation to make test more efficient.
+ cricket::AudioOptions options;
+ options.echo_cancellation.emplace(true);
+ rtc::scoped_refptr<webrtc::AudioSourceInterface> source =
+ ortc_factory->CreateAudioSource(options);
+ return ortc_factory->CreateAudioTrack(id, source);
+ }
+
+ // Stores created capturer in |fake_video_capturers_|.
+ rtc::scoped_refptr<webrtc::VideoTrackInterface>
+ CreateLocalVideoTrackAndFakeCapturer(const std::string& id,
+ OrtcFactoryInterface* ortc_factory) {
+ cricket::FakeVideoCapturer* fake_capturer =
+ new webrtc::FakePeriodicVideoCapturer();
+ fake_video_capturers_.push_back(fake_capturer);
+ rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> source =
+ ortc_factory->CreateVideoSource(
+ std::unique_ptr<cricket::VideoCapturer>(fake_capturer));
+ return rtc::scoped_refptr<webrtc::VideoTrackInterface>(
+ ortc_factory->CreateVideoTrack(id, source));
+ }
+
+ rtc::PhysicalSocketServer physical_socket_server_;
+ rtc::VirtualSocketServer virtual_socket_server_;
+ rtc::Thread network_thread_;
+ rtc::FakeNetworkManager fake_network_manager_;
+ rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module1_;
+ rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module2_;
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory1_;
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory2_;
+ // Actually owned by video tracks.
+ std::vector<cricket::FakeVideoCapturer*> fake_video_capturers_;
+};
+
+// Very basic end-to-end test with a single pair of audio RTP sender and
+// receiver.
+//
+// Uses muxed RTCP, and minimal parameters with a hard-coded config that's
+// known to work.
+TEST_F(OrtcFactoryIntegrationTest, BasicOneWayAudioRtpSenderAndReceiver) {
+ auto udp_transports = CreateAndConnectUdpTransportPair();
+ auto rtp_transports =
+ CreateRtpTransportPair(MakeRtcpMuxParameters(), udp_transports);
+
+ auto sender_result = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transports.first.get());
+ auto receiver_result = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transports.second.get());
+ ASSERT_TRUE(sender_result.ok());
+ ASSERT_TRUE(receiver_result.ok());
+ auto sender = sender_result.MoveValue();
+ auto receiver = receiver_result.MoveValue();
+
+ RTCError error =
+ sender->SetTrack(CreateLocalAudioTrack("audio", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+
+ RtpParameters opus_parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(receiver->Receive(opus_parameters).ok());
+ EXPECT_TRUE(sender->Send(opus_parameters).ok());
+ // Sender and receiver are connected and configured; audio frames should be
+ // able to flow at this point.
+ EXPECT_TRUE_WAIT(
+ fake_audio_capture_module2_->frames_received() > kDefaultNumFrames,
+ kDefaultTimeout);
+}
+
+// Very basic end-to-end test with a single pair of video RTP sender and
+// receiver.
+//
+// Uses muxed RTCP, and minimal parameters with a hard-coded config that's
+// known to work.
+TEST_F(OrtcFactoryIntegrationTest, BasicOneWayVideoRtpSenderAndReceiver) {
+ auto udp_transports = CreateAndConnectUdpTransportPair();
+ auto rtp_transports =
+ CreateRtpTransportPair(MakeRtcpMuxParameters(), udp_transports);
+
+ auto sender_result = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.first.get());
+ auto receiver_result = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.second.get());
+ ASSERT_TRUE(sender_result.ok());
+ ASSERT_TRUE(receiver_result.ok());
+ auto sender = sender_result.MoveValue();
+ auto receiver = receiver_result.MoveValue();
+
+ RTCError error = sender->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+
+ RtpParameters vp8_parameters = MakeMinimalVp8Parameters();
+ EXPECT_TRUE(receiver->Receive(vp8_parameters).ok());
+ EXPECT_TRUE(sender->Send(vp8_parameters).ok());
+ FakeVideoTrackRenderer fake_renderer(
+ static_cast<VideoTrackInterface*>(receiver->GetTrack().get()));
+ // Sender and receiver are connected and configured; video frames should be
+ // able to flow at this point.
+ EXPECT_TRUE_WAIT(fake_renderer.num_rendered_frames() > kDefaultNumFrames,
+ kDefaultTimeout);
+}
+
+// Test that if the track is changed while sending, the sender seamlessly
+// transitions to sending it and frames are received end-to-end.
+//
+// Only doing this for video, since given that audio is sourced from a single
+// fake audio capture module, the audio track is just a dummy object.
+// TODO(deadbeef): Change this when possible.
+TEST_F(OrtcFactoryIntegrationTest, SetTrackWhileSending) {
+ auto udp_transports = CreateAndConnectUdpTransportPair();
+ auto rtp_transports =
+ CreateRtpTransportPair(MakeRtcpMuxParameters(), udp_transports);
+
+ auto sender_result = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.first.get());
+ auto receiver_result = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.second.get());
+ ASSERT_TRUE(sender_result.ok());
+ ASSERT_TRUE(receiver_result.ok());
+ auto sender = sender_result.MoveValue();
+ auto receiver = receiver_result.MoveValue();
+
+ RTCError error = sender->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video_1", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+ RtpParameters vp8_parameters = MakeMinimalVp8Parameters();
+ EXPECT_TRUE(receiver->Receive(vp8_parameters).ok());
+ EXPECT_TRUE(sender->Send(vp8_parameters).ok());
+ FakeVideoTrackRenderer fake_renderer(
+ static_cast<VideoTrackInterface*>(receiver->GetTrack().get()));
+ // Expect for some initial number of frames to be received.
+ EXPECT_TRUE_WAIT(fake_renderer.num_rendered_frames() > kDefaultNumFrames,
+ kDefaultTimeout);
+ // Stop the old capturer, set a new track, and verify new frames are received
+ // from the new track. Stopping the old capturer ensures that we aren't
+ // actually still getting frames from it.
+ fake_video_capturers_[0]->Stop();
+ int prev_num_frames = fake_renderer.num_rendered_frames();
+ error = sender->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video_2", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+ EXPECT_TRUE_WAIT(
+ fake_renderer.num_rendered_frames() > kDefaultNumFrames + prev_num_frames,
+ kDefaultTimeout);
+}
+
+// End-to-end test with two pairs of RTP senders and receivers, for audio and
+// video.
+//
+// Uses muxed RTCP, and minimal parameters with hard-coded configs that are
+// known to work.
+TEST_F(OrtcFactoryIntegrationTest,
+ BasicTwoWayAudioVideoRtpSendersAndReceivers) {
+ auto udp_transports = CreateAndConnectUdpTransportPair();
+ auto rtp_transports =
+ CreateRtpTransportPair(MakeRtcpMuxParameters(), udp_transports);
+
+ // Create all the senders and receivers (four per endpoint).
+ auto audio_sender_result1 = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transports.first.get());
+ auto video_sender_result1 = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.first.get());
+ auto audio_receiver_result1 = ortc_factory1_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transports.first.get());
+ auto video_receiver_result1 = ortc_factory1_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.first.get());
+ ASSERT_TRUE(audio_sender_result1.ok());
+ ASSERT_TRUE(video_sender_result1.ok());
+ ASSERT_TRUE(audio_receiver_result1.ok());
+ ASSERT_TRUE(video_receiver_result1.ok());
+ auto audio_sender1 = audio_sender_result1.MoveValue();
+ auto video_sender1 = video_sender_result1.MoveValue();
+ auto audio_receiver1 = audio_receiver_result1.MoveValue();
+ auto video_receiver1 = video_receiver_result1.MoveValue();
+
+ auto audio_sender_result2 = ortc_factory2_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transports.second.get());
+ auto video_sender_result2 = ortc_factory2_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.second.get());
+ auto audio_receiver_result2 = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transports.second.get());
+ auto video_receiver_result2 = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transports.second.get());
+ ASSERT_TRUE(audio_sender_result2.ok());
+ ASSERT_TRUE(video_sender_result2.ok());
+ ASSERT_TRUE(audio_receiver_result2.ok());
+ ASSERT_TRUE(video_receiver_result2.ok());
+ auto audio_sender2 = audio_sender_result2.MoveValue();
+ auto video_sender2 = video_sender_result2.MoveValue();
+ auto audio_receiver2 = audio_receiver_result2.MoveValue();
+ auto video_receiver2 = video_receiver_result2.MoveValue();
+
+ // Add fake tracks.
+ RTCError error = audio_sender1->SetTrack(
+ CreateLocalAudioTrack("audio", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+ error = video_sender1->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+ error = audio_sender2->SetTrack(
+ CreateLocalAudioTrack("audio", ortc_factory2_.get()));
+ EXPECT_TRUE(error.ok());
+ error = video_sender2->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory2_.get()));
+ EXPECT_TRUE(error.ok());
+
+ // "sent_X_parameters1" are the parameters that endpoint 1 sends with and
+ // endpoint 2 receives with.
+ RtpParameters sent_opus_parameters1 =
+ MakeMinimalOpusParametersWithSsrc(0xdeadbeef);
+ RtpParameters sent_vp8_parameters1 =
+ MakeMinimalVp8ParametersWithSsrc(0xbaadfeed);
+ RtpParameters sent_opus_parameters2 =
+ MakeMinimalOpusParametersWithSsrc(0x13333337);
+ RtpParameters sent_vp8_parameters2 =
+ MakeMinimalVp8ParametersWithSsrc(0x12345678);
+
+ // Configure the senders' and receivers' parameters.
+ EXPECT_TRUE(audio_receiver1->Receive(sent_opus_parameters2).ok());
+ EXPECT_TRUE(video_receiver1->Receive(sent_vp8_parameters2).ok());
+ EXPECT_TRUE(audio_receiver2->Receive(sent_opus_parameters1).ok());
+ EXPECT_TRUE(video_receiver2->Receive(sent_vp8_parameters1).ok());
+ EXPECT_TRUE(audio_sender1->Send(sent_opus_parameters1).ok());
+ EXPECT_TRUE(video_sender1->Send(sent_vp8_parameters1).ok());
+ EXPECT_TRUE(audio_sender2->Send(sent_opus_parameters2).ok());
+ EXPECT_TRUE(video_sender2->Send(sent_vp8_parameters2).ok());
+
+ FakeVideoTrackRenderer fake_video_renderer1(
+ static_cast<VideoTrackInterface*>(video_receiver1->GetTrack().get()));
+ FakeVideoTrackRenderer fake_video_renderer2(
+ static_cast<VideoTrackInterface*>(video_receiver2->GetTrack().get()));
+
+ // Senders and receivers are connected and configured; audio and video frames
+ // should be able to flow at this point.
+ EXPECT_TRUE_WAIT(
+ fake_audio_capture_module1_->frames_received() > kDefaultNumFrames &&
+ fake_video_renderer1.num_rendered_frames() > kDefaultNumFrames &&
+ fake_audio_capture_module2_->frames_received() > kDefaultNumFrames &&
+ fake_video_renderer2.num_rendered_frames() > kDefaultNumFrames,
+ kDefaultTimeout);
+}
+
+// End-to-end test with two pairs of RTP senders and receivers, for audio and
+// video. Unlike the test above, this attempts to make the parameters as
+// complex as possible.
+//
+// Uses non-muxed RTCP, with separate audio/video transports, and a full set of
+// parameters, as would normally be used in a PeerConnection.
+//
+// TODO(deadbeef): Update this test as more audio/video features become
+// supported.
+TEST_F(OrtcFactoryIntegrationTest, FullTwoWayAudioVideoRtpSendersAndReceivers) {
+ // We want four pairs of UDP transports for this test, for audio/video and
+ // RTP/RTCP.
+ auto audio_rtp_udp_transports = CreateAndConnectUdpTransportPair();
+ auto audio_rtcp_udp_transports = CreateAndConnectUdpTransportPair();
+ auto video_rtp_udp_transports = CreateAndConnectUdpTransportPair();
+ auto video_rtcp_udp_transports = CreateAndConnectUdpTransportPair();
+
+ // Since we have multiple RTP transports on each side, we need an RTP
+ // transport controller.
+ auto transport_controllers = CreateRtpTransportControllerPair();
+
+ RtcpParameters audio_rtcp_parameters;
+ audio_rtcp_parameters.mux = false;
+ auto audio_rtp_transports =
+ CreateRtpTransportPair(audio_rtcp_parameters, audio_rtp_udp_transports,
+ audio_rtcp_udp_transports, transport_controllers);
+
+ RtcpParameters video_rtcp_parameters;
+ video_rtcp_parameters.mux = false;
+ video_rtcp_parameters.reduced_size = true;
+ auto video_rtp_transports =
+ CreateRtpTransportPair(video_rtcp_parameters, video_rtp_udp_transports,
+ video_rtcp_udp_transports, transport_controllers);
+
+ // Create all the senders and receivers (four per endpoint).
+ auto audio_sender_result1 = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.first.get());
+ auto video_sender_result1 = ortc_factory1_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.first.get());
+ auto audio_receiver_result1 = ortc_factory1_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.first.get());
+ auto video_receiver_result1 = ortc_factory1_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.first.get());
+ ASSERT_TRUE(audio_sender_result1.ok());
+ ASSERT_TRUE(video_sender_result1.ok());
+ ASSERT_TRUE(audio_receiver_result1.ok());
+ ASSERT_TRUE(video_receiver_result1.ok());
+ auto audio_sender1 = audio_sender_result1.MoveValue();
+ auto video_sender1 = video_sender_result1.MoveValue();
+ auto audio_receiver1 = audio_receiver_result1.MoveValue();
+ auto video_receiver1 = video_receiver_result1.MoveValue();
+
+ auto audio_sender_result2 = ortc_factory2_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.second.get());
+ auto video_sender_result2 = ortc_factory2_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.second.get());
+ auto audio_receiver_result2 = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transports.second.get());
+ auto video_receiver_result2 = ortc_factory2_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transports.second.get());
+ ASSERT_TRUE(audio_sender_result2.ok());
+ ASSERT_TRUE(video_sender_result2.ok());
+ ASSERT_TRUE(audio_receiver_result2.ok());
+ ASSERT_TRUE(video_receiver_result2.ok());
+ auto audio_sender2 = audio_sender_result2.MoveValue();
+ auto video_sender2 = video_sender_result2.MoveValue();
+ auto audio_receiver2 = audio_receiver_result2.MoveValue();
+ auto video_receiver2 = video_receiver_result2.MoveValue();
+
+ RTCError error = audio_sender1->SetTrack(
+ CreateLocalAudioTrack("audio", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+ error = video_sender1->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory1_.get()));
+ EXPECT_TRUE(error.ok());
+ error = audio_sender2->SetTrack(
+ CreateLocalAudioTrack("audio", ortc_factory2_.get()));
+ EXPECT_TRUE(error.ok());
+ error = video_sender2->SetTrack(
+ CreateLocalVideoTrackAndFakeCapturer("video", ortc_factory2_.get()));
+ EXPECT_TRUE(error.ok());
+
+ // Use different codecs in different directions for extra challenge.
+ RtpParameters opus_send_parameters = MakeFullOpusParameters();
+ RtpParameters isac_send_parameters = MakeFullIsacParameters();
+ RtpParameters vp8_send_parameters = MakeFullVp8Parameters();
+ RtpParameters vp9_send_parameters = MakeFullVp9Parameters();
+
+ // Remove "payload_type" from receive parameters. Receiver will need to
+ // discern the payload type from packets received.
+ RtpParameters opus_receive_parameters = opus_send_parameters;
+ RtpParameters isac_receive_parameters = isac_send_parameters;
+ RtpParameters vp8_receive_parameters = vp8_send_parameters;
+ RtpParameters vp9_receive_parameters = vp9_send_parameters;
+ opus_receive_parameters.encodings[0].codec_payload_type.reset();
+ isac_receive_parameters.encodings[0].codec_payload_type.reset();
+ vp8_receive_parameters.encodings[0].codec_payload_type.reset();
+ vp9_receive_parameters.encodings[0].codec_payload_type.reset();
+
+ // Configure the senders' and receivers' parameters.
+ //
+ // Note: Intentionally, the top codec in the receive parameters does not
+ // match the codec sent by the other side. If "Receive" is called with a list
+ // of codecs, the receiver should be prepared to receive any of them, not
+ // just the one on top.
+ EXPECT_TRUE(audio_receiver1->Receive(opus_receive_parameters).ok());
+ EXPECT_TRUE(video_receiver1->Receive(vp8_receive_parameters).ok());
+ EXPECT_TRUE(audio_receiver2->Receive(isac_receive_parameters).ok());
+ EXPECT_TRUE(video_receiver2->Receive(vp9_receive_parameters).ok());
+ EXPECT_TRUE(audio_sender1->Send(opus_send_parameters).ok());
+ EXPECT_TRUE(video_sender1->Send(vp8_send_parameters).ok());
+ EXPECT_TRUE(audio_sender2->Send(isac_send_parameters).ok());
+ EXPECT_TRUE(video_sender2->Send(vp9_send_parameters).ok());
+
+ FakeVideoTrackRenderer fake_video_renderer1(
+ static_cast<VideoTrackInterface*>(video_receiver1->GetTrack().get()));
+ FakeVideoTrackRenderer fake_video_renderer2(
+ static_cast<VideoTrackInterface*>(video_receiver2->GetTrack().get()));
+
+ // Senders and receivers are connected and configured; audio and video frames
+ // should be able to flow at this point.
+ EXPECT_TRUE_WAIT(
+ fake_audio_capture_module1_->frames_received() > kDefaultNumFrames &&
+ fake_video_renderer1.num_rendered_frames() > kDefaultNumFrames &&
+ fake_audio_capture_module2_->frames_received() > kDefaultNumFrames &&
+ fake_video_renderer2.num_rendered_frames() > kDefaultNumFrames,
+ kDefaultTimeout);
+}
+
+// TODO(deadbeef): End-to-end test for multiple senders/receivers of the same
+// media type, once that's supported. Currently, it is not because the
+// BaseChannel model relies on there being a single VoiceChannel and
+// VideoChannel, and these only support a single set of codecs/etc. per
+// send/receive direction.
+
+// TODO(deadbeef): End-to-end test for simulcast, once that's supported by this
+// API.
+
+} // namespace webrtc
diff --git a/ortc/ortcfactory_unittest.cc b/ortc/ortcfactory_unittest.cc
new file mode 100644
index 0000000..80e679b
--- /dev/null
+++ b/ortc/ortcfactory_unittest.cc
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/fakenetwork.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/virtualsocketserver.h"
+#include "webrtc/media/base/fakemediaengine.h"
+#include "webrtc/ortc/ortcfactory.h"
+#include "webrtc/ortc/testrtpparameters.h"
+#include "webrtc/p2p/base/fakepackettransport.h"
+
+namespace webrtc {
+
+// This test uses a virtual network and fake media engine, in order to test the
+// OrtcFactory at only an API level. Any end-to-end test should go in
+// ortcfactory_integrationtest.cc instead.
+class OrtcFactoryTest : public testing::Test {
+ public:
+ OrtcFactoryTest()
+ : virtual_socket_server_(&physical_socket_server_),
+ socket_server_scope_(&virtual_socket_server_),
+ fake_packet_transport_("fake transport") {
+ ortc_factory_ =
+ OrtcFactory::Create(nullptr, nullptr, &fake_network_manager_, nullptr,
+ nullptr,
+ std::unique_ptr<cricket::MediaEngineInterface>(
+ new cricket::FakeMediaEngine()))
+ .MoveValue();
+ }
+
+ protected:
+ // Uses a single pre-made FakePacketTransport, so shouldn't be called twice in
+ // the same test.
+ std::unique_ptr<RtpTransportInterface>
+ CreateRtpTransportWithFakePacketTransport() {
+ return ortc_factory_
+ ->CreateRtpTransport(MakeRtcpMuxParameters(), &fake_packet_transport_,
+ nullptr, nullptr)
+ .MoveValue();
+ }
+
+ rtc::PhysicalSocketServer physical_socket_server_;
+ rtc::VirtualSocketServer virtual_socket_server_;
+ rtc::SocketServerScope socket_server_scope_;
+ rtc::FakeNetworkManager fake_network_manager_;
+ rtc::FakePacketTransport fake_packet_transport_;
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory_;
+};
+
+TEST_F(OrtcFactoryTest, CanCreateMultipleRtpTransportControllers) {
+ auto controller_result1 = ortc_factory_->CreateRtpTransportController();
+ EXPECT_TRUE(controller_result1.ok());
+ auto controller_result2 = ortc_factory_->CreateRtpTransportController();
+ EXPECT_TRUE(controller_result1.ok());
+}
+
+// Simple test for the successful cases of CreateRtpTransport.
+TEST_F(OrtcFactoryTest, CreateRtpTransportWithAndWithoutMux) {
+ rtc::FakePacketTransport rtp("rtp");
+ rtc::FakePacketTransport rtcp("rtcp");
+ // With muxed RTCP.
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto result = ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp,
+ nullptr, nullptr);
+ EXPECT_TRUE(result.ok());
+ result.MoveValue().reset();
+ // With non-muxed RTCP.
+ rtcp_parameters.mux = false;
+ result =
+ ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp, &rtcp, nullptr);
+ EXPECT_TRUE(result.ok());
+}
+
+// If no CNAME is provided, one should be generated and returned by
+// GetRtpParameters.
+TEST_F(OrtcFactoryTest, CreateRtpTransportGeneratesCname) {
+ rtc::FakePacketTransport rtp("rtp");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto result = ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp,
+ nullptr, nullptr);
+ ASSERT_TRUE(result.ok());
+ EXPECT_FALSE(result.value()->GetRtcpParameters().cname.empty());
+}
+
+// Extension of the above test; multiple transports created by the same factory
+// should use the same generated CNAME.
+TEST_F(OrtcFactoryTest, MultipleRtpTransportsUseSameGeneratedCname) {
+ rtc::FakePacketTransport packet_transport1("1");
+ rtc::FakePacketTransport packet_transport2("2");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ // Sanity check.
+ ASSERT_TRUE(rtcp_parameters.cname.empty());
+ auto result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &packet_transport1, nullptr, nullptr);
+ ASSERT_TRUE(result.ok());
+ auto rtp_transport1 = result.MoveValue();
+ result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &packet_transport2, nullptr, nullptr);
+ ASSERT_TRUE(result.ok());
+ auto rtp_transport2 = result.MoveValue();
+ RtcpParameters params1 = rtp_transport1->GetRtcpParameters();
+ RtcpParameters params2 = rtp_transport2->GetRtcpParameters();
+ EXPECT_FALSE(params1.cname.empty());
+ EXPECT_EQ(params1.cname, params2.cname);
+}
+
+TEST_F(OrtcFactoryTest, CreateRtpTransportWithNoPacketTransport) {
+ auto result = ortc_factory_->CreateRtpTransport(MakeRtcpMuxParameters(),
+ nullptr, nullptr, nullptr);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+// If the |mux| member of the RtcpParameters is false, both an RTP and RTCP
+// packet transport are needed.
+TEST_F(OrtcFactoryTest, CreateRtpTransportWithMissingRtcpTransport) {
+ rtc::FakePacketTransport rtp("rtp");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = false;
+ auto result = ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp,
+ nullptr, nullptr);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+// If the |mux| member of the RtcpParameters is true, only an RTP packet
+// transport is necessary. So, passing in an RTCP transport is most likely
+// an accident, and thus should be treated as an error.
+TEST_F(OrtcFactoryTest, CreateRtpTransportWithExtraneousRtcpTransport) {
+ rtc::FakePacketTransport rtp("rtp");
+ rtc::FakePacketTransport rtcp("rtcp");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto result =
+ ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp, &rtcp, nullptr);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+// Basic test that CreateUdpTransport works with AF_INET and AF_INET6.
+TEST_F(OrtcFactoryTest, CreateUdpTransport) {
+ auto result = ortc_factory_->CreateUdpTransport(AF_INET);
+ EXPECT_TRUE(result.ok());
+ result = ortc_factory_->CreateUdpTransport(AF_INET6);
+ EXPECT_TRUE(result.ok());
+}
+
+// Test CreateUdpPort with the |min_port| and |max_port| arguments.
+TEST_F(OrtcFactoryTest, CreateUdpTransportWithPortRange) {
+ auto socket_result1 = ortc_factory_->CreateUdpTransport(AF_INET, 2000, 2002);
+ ASSERT_TRUE(socket_result1.ok());
+ EXPECT_EQ(2000, socket_result1.value()->GetLocalAddress().port());
+ auto socket_result2 = ortc_factory_->CreateUdpTransport(AF_INET, 2000, 2002);
+ ASSERT_TRUE(socket_result2.ok());
+ EXPECT_EQ(2001, socket_result2.value()->GetLocalAddress().port());
+ auto socket_result3 = ortc_factory_->CreateUdpTransport(AF_INET, 2000, 2002);
+ ASSERT_TRUE(socket_result3.ok());
+ EXPECT_EQ(2002, socket_result3.value()->GetLocalAddress().port());
+
+ // All sockets in the range have been exhausted, so the next call should
+ // fail.
+ auto failed_result = ortc_factory_->CreateUdpTransport(AF_INET, 2000, 2002);
+ EXPECT_EQ(RTCErrorType::RESOURCE_EXHAUSTED, failed_result.error().type());
+
+ // If one socket is destroyed, that port should be freed up again.
+ socket_result2.MoveValue().reset();
+ auto socket_result4 = ortc_factory_->CreateUdpTransport(AF_INET, 2000, 2002);
+ ASSERT_TRUE(socket_result4.ok());
+ EXPECT_EQ(2001, socket_result4.value()->GetLocalAddress().port());
+}
+
+// Basic test that CreateUdpTransport works with AF_INET and AF_INET6.
+TEST_F(OrtcFactoryTest, CreateUdpTransportWithInvalidAddressFamily) {
+ auto result = ortc_factory_->CreateUdpTransport(12345);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+TEST_F(OrtcFactoryTest, CreateUdpTransportWithInvalidPortRange) {
+ auto result = ortc_factory_->CreateUdpTransport(AF_INET, 3000, 2000);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+}
+
+// Just sanity check that each "GetCapabilities" method returns some codecs.
+TEST_F(OrtcFactoryTest, GetSenderAndReceiverCapabilities) {
+ RtpCapabilities audio_send_caps =
+ ortc_factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO);
+ EXPECT_GT(audio_send_caps.codecs.size(), 0u);
+ RtpCapabilities video_send_caps =
+ ortc_factory_->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO);
+ EXPECT_GT(video_send_caps.codecs.size(), 0u);
+ RtpCapabilities audio_receive_caps =
+ ortc_factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_AUDIO);
+ EXPECT_GT(audio_receive_caps.codecs.size(), 0u);
+ RtpCapabilities video_receive_caps =
+ ortc_factory_->GetRtpReceiverCapabilities(cricket::MEDIA_TYPE_VIDEO);
+ EXPECT_GT(video_receive_caps.codecs.size(), 0u);
+}
+
+// Calling CreateRtpSender with a null track should fail, since that makes it
+// impossible to know whether to create an audio or video sender. The
+// application should be using the method that takes a cricket::MediaType
+// instead.
+TEST_F(OrtcFactoryTest, CreateSenderWithNullTrack) {
+ auto rtp_transport = CreateRtpTransportWithFakePacketTransport();
+ auto result = ortc_factory_->CreateRtpSender(nullptr, rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+// Calling CreateRtpSender or CreateRtpReceiver with MEDIA_TYPE_DATA should
+// fail.
+TEST_F(OrtcFactoryTest, CreateSenderOrReceieverWithInvalidKind) {
+ auto rtp_transport = CreateRtpTransportWithFakePacketTransport();
+ auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_DATA,
+ rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, sender_result.error().type());
+ auto receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_DATA, rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, receiver_result.error().type());
+}
+
+TEST_F(OrtcFactoryTest, CreateSendersOrReceieversWithNullTransport) {
+ auto sender_result =
+ ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, nullptr);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, sender_result.error().type());
+ auto receiver_result =
+ ortc_factory_->CreateRtpReceiver(cricket::MEDIA_TYPE_AUDIO, nullptr);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, receiver_result.error().type());
+}
+
+} // namespace webrtc
diff --git a/ortc/ortcrtpreceiver_unittest.cc b/ortc/ortcrtpreceiver_unittest.cc
new file mode 100644
index 0000000..1764af0
--- /dev/null
+++ b/ortc/ortcrtpreceiver_unittest.cc
@@ -0,0 +1,547 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/media/base/fakemediaengine.h"
+#include "webrtc/p2p/base/fakepackettransport.h"
+#include "webrtc/ortc/ortcfactory.h"
+#include "webrtc/ortc/testrtpparameters.h"
+#include "webrtc/pc/test/fakevideotracksource.h"
+
+namespace webrtc {
+
+// This test uses an individual RtpReceiver using only the public interface,
+// and verifies that it behaves as designed at an API level. Also tests that
+// parameters are applied to the audio/video engines as expected. Network and
+// media interfaces are faked to isolate what's being tested.
+//
+// This test shouldn't result any any actual media being sent. That sort of
+// test should go in ortcfactory_integrationtest.cc.
+class OrtcRtpReceiverTest : public testing::Test {
+ public:
+ OrtcRtpReceiverTest() : fake_packet_transport_("fake") {
+ fake_media_engine_ = new cricket::FakeMediaEngine();
+ // Note: This doesn't need to use fake network classes, since we already
+ // use FakePacketTransport.
+ auto ortc_factory_result = OrtcFactory::Create(
+ nullptr, nullptr, nullptr, nullptr, nullptr,
+ std::unique_ptr<cricket::MediaEngineInterface>(fake_media_engine_));
+ ortc_factory_ = ortc_factory_result.MoveValue();
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport_, nullptr, nullptr);
+ rtp_transport_ = rtp_transport_result.MoveValue();
+ }
+
+ protected:
+ // Owned by |ortc_factory_|.
+ cricket::FakeMediaEngine* fake_media_engine_;
+ rtc::FakePacketTransport fake_packet_transport_;
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory_;
+ std::unique_ptr<RtpTransportInterface> rtp_transport_;
+};
+
+// See ortcrtpreceiverinterface.h for the current expectations of what GetTrack
+// will return after calls to Receive.
+// TODO(deadbeef): Replace this test when the non-standard behavior is fixed
+// and GetTrack starts returning the same track for the lifetime of the
+// receiver.
+TEST_F(OrtcRtpReceiverTest, GetTrack) {
+ auto receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ ASSERT_TRUE(receiver_result.ok());
+ auto receiver = receiver_result.MoveValue();
+
+ // Track initially expected to be null.
+ EXPECT_EQ(nullptr, receiver_result.value().get());
+
+ EXPECT_TRUE(receiver->Receive(MakeMinimalVp8ParametersWithNoSsrc()).ok());
+ auto initial_track = receiver->GetTrack();
+ EXPECT_NE(nullptr, initial_track);
+
+ // Codec changing but SSRC (or lack thereof) isn't; shouldn't create new track
+ EXPECT_TRUE(receiver->Receive(MakeMinimalVp9ParametersWithNoSsrc()).ok());
+ EXPECT_EQ(initial_track, receiver->GetTrack());
+
+ // Explicitly set SSRC and expect a different track.
+ EXPECT_TRUE(
+ receiver->Receive(MakeMinimalVp9ParametersWithSsrc(0xdeadbeef)).ok());
+ auto next_track = receiver->GetTrack();
+ EXPECT_NE(next_track, initial_track);
+
+ // Deactivating the encoding shouldn't change the track.
+ RtpParameters inactive_encoding =
+ MakeMinimalVp9ParametersWithSsrc(0xdeadbeef);
+ inactive_encoding.encodings[0].active = false;
+ EXPECT_TRUE(receiver->Receive(inactive_encoding).ok());
+ EXPECT_EQ(next_track, receiver->GetTrack());
+
+ // Removing all encodings *is* expected to clear the track.
+ RtpParameters no_encodings = MakeMinimalVp9ParametersWithSsrc(0xdeadbeef);
+ no_encodings.encodings.clear();
+ EXPECT_TRUE(receiver->Receive(no_encodings).ok());
+ EXPECT_EQ(nullptr, receiver->GetTrack());
+}
+
+// Currently SetTransport isn't supported. When it is, replace this test with a
+// test/tests for it.
+TEST_F(OrtcRtpReceiverTest, SetTransportFails) {
+ rtc::FakePacketTransport fake_packet_transport("another_transport");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport, nullptr, nullptr);
+ auto rtp_transport = rtp_transport_result.MoveValue();
+
+ auto receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto receiver = receiver_result.MoveValue();
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ receiver->SetTransport(rtp_transport.get()).type());
+}
+
+TEST_F(OrtcRtpReceiverTest, GetTransport) {
+ auto result = ortc_factory_->CreateRtpReceiver(cricket::MEDIA_TYPE_AUDIO,
+ rtp_transport_.get());
+ EXPECT_EQ(rtp_transport_.get(), result.value()->GetTransport());
+}
+
+// Test that "Receive" causes the expected parameters to be applied to the media
+// engine level, for an audio receiver.
+TEST_F(OrtcRtpReceiverTest, ReceiveAppliesAudioParametersToMediaEngine) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+
+ // First, create parameters with all the bells and whistles.
+ RtpParameters parameters;
+
+ RtpCodecParameters opus_codec;
+ opus_codec.name = "opus";
+ opus_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ opus_codec.payload_type = 120;
+ opus_codec.clock_rate.emplace(48000);
+ opus_codec.num_channels.emplace(2);
+ opus_codec.parameters["minptime"] = "10";
+ opus_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ parameters.codecs.push_back(std::move(opus_codec));
+
+ // Add two codecs, expecting the first to be used.
+ // TODO(deadbeef): Once "codec_payload_type" is supported, use it to select a
+ // codec that's not at the top of the list.
+ RtpCodecParameters isac_codec;
+ isac_codec.name = "ISAC";
+ isac_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ isac_codec.payload_type = 110;
+ isac_codec.clock_rate.emplace(16000);
+ parameters.codecs.push_back(std::move(isac_codec));
+
+ RtpEncodingParameters encoding;
+ encoding.ssrc.emplace(0xdeadbeef);
+ parameters.encodings.push_back(std::move(encoding));
+
+ parameters.header_extensions.emplace_back(
+ "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 3);
+
+ EXPECT_TRUE(audio_receiver->Receive(parameters).ok());
+
+ // Now verify that the parameters were applied to the fake media engine layer
+ // that exists below BaseChannel.
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->playout());
+
+ // Verify codec parameters.
+ ASSERT_GT(fake_voice_channel->recv_codecs().size(), 0u);
+ const cricket::AudioCodec& top_codec = fake_voice_channel->recv_codecs()[0];
+ EXPECT_EQ("opus", top_codec.name);
+ EXPECT_EQ(120, top_codec.id);
+ EXPECT_EQ(48000, top_codec.clockrate);
+ EXPECT_EQ(2u, top_codec.channels);
+ ASSERT_NE(top_codec.params.end(), top_codec.params.find("minptime"));
+ EXPECT_EQ("10", top_codec.params.at("minptime"));
+
+ // Verify encoding parameters.
+ ASSERT_EQ(1u, fake_voice_channel->recv_streams().size());
+ const cricket::StreamParams& recv_stream =
+ fake_voice_channel->recv_streams()[0];
+ EXPECT_EQ(1u, recv_stream.ssrcs.size());
+ EXPECT_EQ(0xdeadbeef, recv_stream.first_ssrc());
+
+ // Verify header extensions.
+ ASSERT_EQ(1u, fake_voice_channel->recv_extensions().size());
+ const RtpExtension& extension = fake_voice_channel->recv_extensions()[0];
+ EXPECT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", extension.uri);
+ EXPECT_EQ(3, extension.id);
+}
+
+// Test that "Receive" causes the expected parameters to be applied to the media
+// engine level, for a video receiver.
+TEST_F(OrtcRtpReceiverTest, ReceiveAppliesVideoParametersToMediaEngine) {
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_receiver = video_receiver_result.MoveValue();
+
+ // First, create parameters with all the bells and whistles.
+ RtpParameters parameters;
+
+ RtpCodecParameters vp8_codec;
+ vp8_codec.name = "VP8";
+ vp8_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_codec.payload_type = 99;
+ // Try a couple types of feedback params. "Generic NACK" is a bit of a
+ // special case, so test it here.
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM,
+ RtcpFeedbackMessageType::FIR);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::GENERIC_NACK);
+ parameters.codecs.push_back(std::move(vp8_codec));
+
+ RtpCodecParameters vp8_rtx_codec;
+ vp8_rtx_codec.name = "rtx";
+ vp8_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_rtx_codec.payload_type = 100;
+ vp8_rtx_codec.parameters["apt"] = "99";
+ parameters.codecs.push_back(std::move(vp8_rtx_codec));
+
+ // Add two codecs, expecting the first to be used.
+ // TODO(deadbeef): Once "codec_payload_type" is supported, use it to select a
+ // codec that's not at the top of the list.
+ RtpCodecParameters vp9_codec;
+ vp9_codec.name = "VP9";
+ vp9_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp9_codec.payload_type = 102;
+ parameters.codecs.push_back(std::move(vp9_codec));
+
+ RtpCodecParameters vp9_rtx_codec;
+ vp9_rtx_codec.name = "rtx";
+ vp9_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp9_rtx_codec.payload_type = 103;
+ vp9_rtx_codec.parameters["apt"] = "102";
+ parameters.codecs.push_back(std::move(vp9_rtx_codec));
+
+ RtpEncodingParameters encoding;
+ encoding.ssrc.emplace(0xdeadbeef);
+ encoding.rtx.emplace(0xbaadfeed);
+ parameters.encodings.push_back(std::move(encoding));
+
+ parameters.header_extensions.emplace_back("urn:3gpp:video-orientation", 4);
+ parameters.header_extensions.emplace_back(
+ "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", 6);
+
+ EXPECT_TRUE(video_receiver->Receive(parameters).ok());
+
+ // Now verify that the parameters were applied to the fake media engine layer
+ // that exists below BaseChannel.
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+
+ // Verify codec parameters.
+ ASSERT_GE(fake_video_channel->recv_codecs().size(), 2u);
+ const cricket::VideoCodec& top_codec = fake_video_channel->recv_codecs()[0];
+ EXPECT_EQ("VP8", top_codec.name);
+ EXPECT_EQ(99, top_codec.id);
+ EXPECT_TRUE(top_codec.feedback_params.Has({"ccm", "fir"}));
+ EXPECT_TRUE(top_codec.feedback_params.Has(cricket::FeedbackParam("nack")));
+
+ const cricket::VideoCodec& rtx_codec = fake_video_channel->recv_codecs()[1];
+ EXPECT_EQ("rtx", rtx_codec.name);
+ EXPECT_EQ(100, rtx_codec.id);
+ ASSERT_NE(rtx_codec.params.end(), rtx_codec.params.find("apt"));
+ EXPECT_EQ("99", rtx_codec.params.at("apt"));
+
+ // Verify encoding parameters.
+ ASSERT_EQ(1u, fake_video_channel->recv_streams().size());
+ const cricket::StreamParams& recv_stream =
+ fake_video_channel->recv_streams()[0];
+ EXPECT_EQ(2u, recv_stream.ssrcs.size());
+ EXPECT_EQ(0xdeadbeef, recv_stream.first_ssrc());
+ uint32_t rtx_ssrc = 0u;
+ EXPECT_TRUE(recv_stream.GetFidSsrc(recv_stream.first_ssrc(), &rtx_ssrc));
+ EXPECT_EQ(0xbaadfeed, rtx_ssrc);
+
+ // Verify header extensions.
+ ASSERT_EQ(2u, fake_video_channel->recv_extensions().size());
+ const RtpExtension& extension1 = fake_video_channel->recv_extensions()[0];
+ EXPECT_EQ("urn:3gpp:video-orientation", extension1.uri);
+ EXPECT_EQ(4, extension1.id);
+ const RtpExtension& extension2 = fake_video_channel->recv_extensions()[1];
+ EXPECT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
+ extension2.uri);
+ EXPECT_EQ(6, extension2.id);
+}
+
+// Test changing both the receive codec and SSRC at the same time, and verify
+// that the new parameters are applied to the media engine level.
+TEST_F(OrtcRtpReceiverTest, CallingReceiveTwiceChangesParameters) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ RTCError error =
+ audio_receiver->Receive(MakeMinimalOpusParametersWithSsrc(0x11111111));
+ EXPECT_TRUE(error.ok());
+ error =
+ audio_receiver->Receive(MakeMinimalIsacParametersWithSsrc(0x22222222));
+ EXPECT_TRUE(error.ok());
+
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ ASSERT_GT(fake_voice_channel->recv_codecs().size(), 0u);
+ EXPECT_EQ("ISAC", fake_voice_channel->recv_codecs()[0].name);
+ ASSERT_EQ(1u, fake_voice_channel->recv_streams().size());
+ EXPECT_EQ(0x22222222u, fake_voice_channel->recv_streams()[0].first_ssrc());
+
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_receiver = video_receiver_result.MoveValue();
+ error = video_receiver->Receive(MakeMinimalVp8ParametersWithSsrc(0x33333333));
+ EXPECT_TRUE(error.ok());
+ error = video_receiver->Receive(MakeMinimalVp9ParametersWithSsrc(0x44444444));
+ EXPECT_TRUE(error.ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ ASSERT_GT(fake_video_channel->recv_codecs().size(), 0u);
+ EXPECT_EQ("VP9", fake_video_channel->recv_codecs()[0].name);
+ ASSERT_EQ(1u, fake_video_channel->recv_streams().size());
+ EXPECT_EQ(0x44444444u, fake_video_channel->recv_streams()[0].first_ssrc());
+}
+
+// Ensure that if the |active| flag of RtpEncodingParameters is set to false,
+// playout stops at the media engine level. Note that this is only applicable
+// to audio (at least currently).
+TEST_F(OrtcRtpReceiverTest, DeactivatingEncodingStopsPlayout) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ RtpParameters parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(audio_receiver->Receive(parameters).ok());
+
+ // Expect "playout" flag to initially be true.
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->playout());
+
+ // Deactivate encoding and expect it to change to false.
+ parameters.encodings[0].active = false;
+ EXPECT_TRUE(audio_receiver->Receive(parameters).ok());
+ EXPECT_FALSE(fake_voice_channel->playout());
+}
+
+// Ensure that calling Receive with an empty list of encodings causes receive
+// streams at the media engine level to be cleared.
+TEST_F(OrtcRtpReceiverTest,
+ CallingReceiveWithEmptyEncodingsClearsReceiveStreams) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ RtpParameters parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(audio_receiver->Receive(parameters).ok());
+ parameters.encodings.clear();
+ EXPECT_TRUE(audio_receiver->Receive(parameters).ok());
+
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->recv_streams().empty());
+
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_receiver = video_receiver_result.MoveValue();
+ parameters = MakeMinimalVp8Parameters();
+ EXPECT_TRUE(video_receiver->Receive(parameters).ok());
+ parameters.encodings.clear();
+ EXPECT_TRUE(video_receiver->Receive(parameters).ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->recv_streams().empty());
+}
+
+// These errors should be covered by rtpparametersconversion_unittest.cc, but
+// we should at least test that those errors are propogated from calls to
+// Receive, with a few examples.
+TEST_F(OrtcRtpReceiverTest, ReceiveReturnsErrorOnInvalidParameters) {
+ auto result = ortc_factory_->CreateRtpReceiver(cricket::MEDIA_TYPE_AUDIO,
+ rtp_transport_.get());
+ auto receiver = result.MoveValue();
+ // CCM feedback missing message type.
+ RtpParameters invalid_feedback = MakeMinimalOpusParameters();
+ invalid_feedback.codecs[0].rtcp_feedback.emplace_back(RtcpFeedbackType::CCM);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ receiver->Receive(invalid_feedback).type());
+ // Payload type greater than 127.
+ RtpParameters invalid_pt = MakeMinimalOpusParameters();
+ invalid_pt.codecs[0].payload_type = 128;
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, receiver->Receive(invalid_pt).type());
+ // Duplicate header extension IDs.
+ RtpParameters duplicate_ids = MakeMinimalOpusParameters();
+ duplicate_ids.header_extensions.emplace_back("foo", 5);
+ duplicate_ids.header_extensions.emplace_back("bar", 5);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ receiver->Receive(duplicate_ids).type());
+}
+
+// Two receivers using the same transport shouldn't be able to use the same
+// payload type to refer to different codecs, same header extension IDs to
+// refer to different extensions, or same SSRC.
+TEST_F(OrtcRtpReceiverTest, ReceiveReturnsErrorOnIdConflicts) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ auto video_receiver = video_receiver_result.MoveValue();
+
+ // First test payload type conflict.
+ RtpParameters audio_parameters = MakeMinimalOpusParameters();
+ RtpParameters video_parameters = MakeMinimalVp8Parameters();
+ audio_parameters.codecs[0].payload_type = 100;
+ video_parameters.codecs[0].payload_type = 100;
+ EXPECT_TRUE(audio_receiver->Receive(audio_parameters).ok());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_receiver->Receive(video_parameters).type());
+
+ // Test header extension ID conflict.
+ video_parameters.codecs[0].payload_type = 110;
+ audio_parameters.header_extensions.emplace_back("foo", 4);
+ video_parameters.header_extensions.emplace_back("bar", 4);
+ EXPECT_TRUE(audio_receiver->Receive(audio_parameters).ok());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_receiver->Receive(video_parameters).type());
+
+ // Test SSRC conflict. Have an RTX SSRC that conflicts with a primary SSRC
+ // for extra challenge.
+ video_parameters.header_extensions[0].uri = "foo";
+ audio_parameters.encodings[0].ssrc.emplace(0xabbaabba);
+ audio_parameters.encodings[0].rtx.emplace(0xdeadbeef);
+ video_parameters.encodings[0].ssrc.emplace(0xdeadbeef);
+ EXPECT_TRUE(audio_receiver->Receive(audio_parameters).ok());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_receiver->Receive(video_parameters).type());
+
+ // Sanity check that parameters can be set if the conflicts are all resolved.
+ video_parameters.encodings[0].ssrc.emplace(0xbaadf00d);
+ EXPECT_TRUE(video_receiver->Receive(video_parameters).ok());
+}
+
+// Ensure that deleting a receiver causes receive streams at the media engine
+// level to be cleared.
+TEST_F(OrtcRtpReceiverTest, DeletingReceiverClearsReceiveStreams) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ EXPECT_TRUE(audio_receiver->Receive(MakeMinimalOpusParameters()).ok());
+
+ // Also create an audio sender, to prevent the voice channel from being
+ // completely deleted.
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok());
+
+ audio_receiver.reset(nullptr);
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->recv_streams().empty());
+
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_receiver = video_receiver_result.MoveValue();
+ EXPECT_TRUE(video_receiver->Receive(MakeMinimalVp8Parameters()).ok());
+
+ // Also create an video sender, to prevent the video channel from being
+ // completely deleted.
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok());
+
+ video_receiver.reset(nullptr);
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->recv_streams().empty());
+}
+
+// If Receive hasn't been called, GetParameters should return empty parameters.
+TEST_F(OrtcRtpReceiverTest, GetDefaultParameters) {
+ auto result = ortc_factory_->CreateRtpReceiver(cricket::MEDIA_TYPE_AUDIO,
+ rtp_transport_.get());
+ EXPECT_EQ(RtpParameters(), result.value()->GetParameters());
+ result = ortc_factory_->CreateRtpReceiver(cricket::MEDIA_TYPE_VIDEO,
+ rtp_transport_.get());
+ EXPECT_EQ(RtpParameters(), result.value()->GetParameters());
+}
+
+// Test that GetParameters returns the last parameters passed into Receive,
+// along with the implementation-default values filled in where they were left
+// unset.
+TEST_F(OrtcRtpReceiverTest,
+ GetParametersReturnsLastSetParametersWithDefaultsFilled) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+
+ RtpParameters opus_parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(audio_receiver->Receive(opus_parameters).ok());
+ EXPECT_EQ(opus_parameters, audio_receiver->GetParameters());
+
+ RtpParameters isac_parameters = MakeMinimalIsacParameters();
+ // Sanity check that num_channels actually is left unset.
+ ASSERT_FALSE(isac_parameters.codecs[0].num_channels);
+ EXPECT_TRUE(audio_receiver->Receive(isac_parameters).ok());
+ // Should be filled with a default "num channels" of 1.
+ // TODO(deadbeef): This should actually default to 2 for some codecs. Update
+ // this test once that's implemented.
+ isac_parameters.codecs[0].num_channels.emplace(1);
+ EXPECT_EQ(isac_parameters, audio_receiver->GetParameters());
+
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_receiver = video_receiver_result.MoveValue();
+
+ RtpParameters vp8_parameters = MakeMinimalVp8Parameters();
+ // Sanity check that clock_rate actually is left unset.
+ EXPECT_TRUE(video_receiver->Receive(vp8_parameters).ok());
+ // Should be filled with a default clock rate of 90000.
+ vp8_parameters.codecs[0].clock_rate.emplace(90000);
+ EXPECT_EQ(vp8_parameters, video_receiver->GetParameters());
+
+ RtpParameters vp9_parameters = MakeMinimalVp9Parameters();
+ // Sanity check that clock_rate actually is left unset.
+ EXPECT_TRUE(video_receiver->Receive(vp9_parameters).ok());
+ // Should be filled with a default clock rate of 90000.
+ vp9_parameters.codecs[0].clock_rate.emplace(90000);
+ EXPECT_EQ(vp9_parameters, video_receiver->GetParameters());
+}
+
+TEST_F(OrtcRtpReceiverTest, GetKind) {
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ auto video_receiver = video_receiver_result.MoveValue();
+ EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, audio_receiver->GetKind());
+ EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, video_receiver->GetKind());
+}
+
+} // namespace webrtc
diff --git a/ortc/ortcrtpreceiveradapter.cc b/ortc/ortcrtpreceiveradapter.cc
new file mode 100644
index 0000000..aefa4d3
--- /dev/null
+++ b/ortc/ortcrtpreceiveradapter.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/ortcrtpreceiveradapter.h"
+
+#include <utility>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/helpers.h" // For "CreateRandomX".
+#include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/ortc/rtptransportadapter.h"
+
+namespace {
+
+void FillAudioReceiverParameters(webrtc::RtpParameters* parameters) {
+ for (webrtc::RtpCodecParameters& codec : parameters->codecs) {
+ if (!codec.num_channels) {
+ codec.num_channels = rtc::Optional<int>(1);
+ }
+ }
+}
+
+void FillVideoReceiverParameters(webrtc::RtpParameters* parameters) {
+ for (webrtc::RtpCodecParameters& codec : parameters->codecs) {
+ if (!codec.clock_rate) {
+ codec.clock_rate = rtc::Optional<int>(cricket::kVideoCodecClockrate);
+ }
+ }
+}
+
+} // namespace
+
+namespace webrtc {
+
+BEGIN_OWNED_PROXY_MAP(OrtcRtpReceiver)
+PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_CONSTMETHOD0(rtc::scoped_refptr<MediaStreamTrackInterface>, GetTrack)
+PROXY_METHOD1(RTCError, SetTransport, RtpTransportInterface*)
+PROXY_CONSTMETHOD0(RtpTransportInterface*, GetTransport)
+PROXY_METHOD1(RTCError, Receive, const RtpParameters&)
+PROXY_CONSTMETHOD0(RtpParameters, GetParameters)
+PROXY_CONSTMETHOD0(cricket::MediaType, GetKind)
+END_PROXY_MAP()
+
+// static
+std::unique_ptr<OrtcRtpReceiverInterface> OrtcRtpReceiverAdapter::CreateProxy(
+ std::unique_ptr<OrtcRtpReceiverAdapter> wrapped_receiver) {
+ RTC_DCHECK(wrapped_receiver);
+ rtc::Thread* signaling =
+ wrapped_receiver->rtp_transport_controller_->signaling_thread();
+ rtc::Thread* worker =
+ wrapped_receiver->rtp_transport_controller_->worker_thread();
+ return OrtcRtpReceiverProxy::Create(signaling, worker,
+ std::move(wrapped_receiver));
+}
+
+OrtcRtpReceiverAdapter::~OrtcRtpReceiverAdapter() {
+ internal_receiver_ = nullptr;
+ SignalDestroyed();
+}
+
+rtc::scoped_refptr<MediaStreamTrackInterface> OrtcRtpReceiverAdapter::GetTrack()
+ const {
+ return internal_receiver_ ? internal_receiver_->track() : nullptr;
+}
+
+RTCError OrtcRtpReceiverAdapter::SetTransport(
+ RtpTransportInterface* transport) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::UNSUPPORTED_OPERATION,
+ "Changing the transport of an RtpReceiver is not yet supported.");
+}
+
+RtpTransportInterface* OrtcRtpReceiverAdapter::GetTransport() const {
+ return transport_;
+}
+
+RTCError OrtcRtpReceiverAdapter::Receive(const RtpParameters& parameters) {
+ RtpParameters filled_parameters = parameters;
+ RTCError err;
+ switch (kind_) {
+ case cricket::MEDIA_TYPE_AUDIO:
+ FillAudioReceiverParameters(&filled_parameters);
+ err = rtp_transport_controller_->ValidateAndApplyAudioReceiverParameters(
+ filled_parameters);
+ if (!err.ok()) {
+ return err;
+ }
+ break;
+ case cricket::MEDIA_TYPE_VIDEO:
+ FillVideoReceiverParameters(&filled_parameters);
+ err = rtp_transport_controller_->ValidateAndApplyVideoReceiverParameters(
+ filled_parameters);
+ if (!err.ok()) {
+ return err;
+ }
+ break;
+ case cricket::MEDIA_TYPE_DATA:
+ RTC_NOTREACHED();
+ return webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR);
+ }
+ last_applied_parameters_ = filled_parameters;
+
+ // Now that parameters were applied, can create (or recreate) the internal
+ // receiver.
+ //
+ // This is analogous to a PeerConnection creating a receiver after
+ // SetRemoteDescription is successful.
+ MaybeRecreateInternalReceiver();
+ return RTCError::OK();
+}
+
+RtpParameters OrtcRtpReceiverAdapter::GetParameters() const {
+ return last_applied_parameters_;
+}
+
+cricket::MediaType OrtcRtpReceiverAdapter::GetKind() const {
+ return kind_;
+}
+
+OrtcRtpReceiverAdapter::OrtcRtpReceiverAdapter(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport,
+ RtpTransportControllerAdapter* rtp_transport_controller)
+ : kind_(kind),
+ transport_(transport),
+ rtp_transport_controller_(rtp_transport_controller) {}
+
+void OrtcRtpReceiverAdapter::MaybeRecreateInternalReceiver() {
+ if (last_applied_parameters_.encodings.empty()) {
+ internal_receiver_ = nullptr;
+ return;
+ }
+ // An SSRC of 0 is valid; this is used to identify "the default SSRC" (which
+ // is the first one seen by the underlying media engine).
+ uint32_t ssrc = 0;
+ if (last_applied_parameters_.encodings[0].ssrc) {
+ ssrc = *last_applied_parameters_.encodings[0].ssrc;
+ }
+ if (internal_receiver_ && ssrc == internal_receiver_->ssrc()) {
+ // SSRC not changing; nothing to do.
+ return;
+ }
+ internal_receiver_ = nullptr;
+ switch (kind_) {
+ case cricket::MEDIA_TYPE_AUDIO:
+ internal_receiver_ =
+ new AudioRtpReceiver(rtc::CreateRandomUuid(), ssrc,
+ rtp_transport_controller_->voice_channel());
+ break;
+ case cricket::MEDIA_TYPE_VIDEO:
+ internal_receiver_ = new VideoRtpReceiver(
+ rtc::CreateRandomUuid(), rtp_transport_controller_->worker_thread(),
+ ssrc, rtp_transport_controller_->video_channel());
+ break;
+ case cricket::MEDIA_TYPE_DATA:
+ RTC_NOTREACHED();
+ }
+}
+
+} // namespace webrtc
diff --git a/ortc/ortcrtpreceiveradapter.h b/ortc/ortcrtpreceiveradapter.h
new file mode 100644
index 0000000..8e081e7
--- /dev/null
+++ b/ortc/ortcrtpreceiveradapter.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_ORTCRTPRECEIVERADAPTER_H_
+#define WEBRTC_ORTC_ORTCRTPRECEIVERADAPTER_H_
+
+#include <memory>
+
+#include "webrtc/api/ortc/ortcrtpreceiverinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/rtpparameters.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/ortc/rtptransportcontrolleradapter.h"
+#include "webrtc/pc/rtpreceiver.h"
+
+namespace webrtc {
+
+// Implementation of OrtcRtpReceiverInterface that works with
+// RtpTransportAdapter, and wraps a VideoRtpReceiver/AudioRtpReceiver that's
+// normally used with the PeerConnection.
+//
+// TODO(deadbeef): When BaseChannel is split apart into separate
+// "RtpReceiver"/"RtpTransceiver"/"RtpReceiver"/"RtpReceiver" objects, this
+// adapter object can be removed.
+class OrtcRtpReceiverAdapter : public OrtcRtpReceiverInterface {
+ public:
+ // Wraps |wrapped_receiver| in a proxy that will safely call methods on the
+ // correct thread.
+ static std::unique_ptr<OrtcRtpReceiverInterface> CreateProxy(
+ std::unique_ptr<OrtcRtpReceiverAdapter> wrapped_receiver);
+
+ // Should only be called by RtpTransportControllerAdapter.
+ OrtcRtpReceiverAdapter(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport,
+ RtpTransportControllerAdapter* rtp_transport_controller);
+ ~OrtcRtpReceiverAdapter() override;
+
+ // OrtcRtpReceiverInterface implementation.
+ rtc::scoped_refptr<MediaStreamTrackInterface> GetTrack() const override;
+
+ RTCError SetTransport(RtpTransportInterface* transport) override;
+ RtpTransportInterface* GetTransport() const override;
+
+ RTCError Receive(const RtpParameters& parameters) override;
+ RtpParameters GetParameters() const override;
+
+ cricket::MediaType GetKind() const override;
+
+ // Used so that the RtpTransportControllerAdapter knows when it can
+ // deallocate resources allocated for this object.
+ sigslot::signal0<> SignalDestroyed;
+
+ private:
+ void MaybeRecreateInternalReceiver();
+
+ cricket::MediaType kind_;
+ RtpTransportInterface* transport_;
+ RtpTransportControllerAdapter* rtp_transport_controller_;
+ // Scoped refptr due to ref-counted interface, but we should be the only
+ // reference holder.
+ rtc::scoped_refptr<RtpReceiverInternal> internal_receiver_;
+ RtpParameters last_applied_parameters_;
+
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(OrtcRtpReceiverAdapter);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_ORTCRTPRECEIVERADAPTER_H_
diff --git a/ortc/ortcrtpsender_unittest.cc b/ortc/ortcrtpsender_unittest.cc
new file mode 100644
index 0000000..954b997
--- /dev/null
+++ b/ortc/ortcrtpsender_unittest.cc
@@ -0,0 +1,667 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/media/base/fakemediaengine.h"
+#include "webrtc/p2p/base/fakepackettransport.h"
+#include "webrtc/ortc/ortcfactory.h"
+#include "webrtc/ortc/testrtpparameters.h"
+#include "webrtc/pc/test/fakevideotracksource.h"
+
+namespace webrtc {
+
+// This test uses an individual RtpSender using only the public interface, and
+// verifies that its behaves as designed at an API level. Also tests that
+// parameters are applied to the audio/video engines as expected. Network and
+// media interfaces are faked to isolate what's being tested.
+//
+// This test shouldn't result any any actual media being sent. That sort of
+// test should go in ortcfactory_integrationtest.cc.
+class OrtcRtpSenderTest : public testing::Test {
+ public:
+ OrtcRtpSenderTest() : fake_packet_transport_("fake") {
+ // Need to set the fake packet transport to writable, in order to test that
+ // the "send" flag is applied to the media engine based on the encoding
+ // |active| flag.
+ fake_packet_transport_.SetWritable(true);
+ fake_media_engine_ = new cricket::FakeMediaEngine();
+ // Note: This doesn't need to use fake network classes, since we already
+ // use FakePacketTransport.
+ auto ortc_factory_result = OrtcFactory::Create(
+ nullptr, nullptr, nullptr, nullptr, nullptr,
+ std::unique_ptr<cricket::MediaEngineInterface>(fake_media_engine_));
+ ortc_factory_ = ortc_factory_result.MoveValue();
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport_, nullptr, nullptr);
+ rtp_transport_ = rtp_transport_result.MoveValue();
+ }
+
+ protected:
+ rtc::scoped_refptr<AudioTrackInterface> CreateAudioTrack(
+ const std::string& id) {
+ return ortc_factory_->CreateAudioTrack(id, nullptr);
+ }
+
+ rtc::scoped_refptr<VideoTrackInterface> CreateVideoTrack(
+ const std::string& id) {
+ return rtc::scoped_refptr<webrtc::VideoTrackInterface>(
+ ortc_factory_->CreateVideoTrack(id, FakeVideoTrackSource::Create()));
+ }
+
+ // Owned by |ortc_factory_|.
+ cricket::FakeMediaEngine* fake_media_engine_;
+ rtc::FakePacketTransport fake_packet_transport_;
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory_;
+ std::unique_ptr<RtpTransportInterface> rtp_transport_;
+};
+
+TEST_F(OrtcRtpSenderTest, GetAndSetTrack) {
+ // Test GetTrack with a sender constructed with a track.
+ auto audio_track = CreateAudioTrack("audio");
+ auto audio_sender_result =
+ ortc_factory_->CreateRtpSender(audio_track, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_EQ(audio_track, audio_sender->GetTrack());
+
+ // Test GetTrack after SetTrack.
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ auto video_track = CreateVideoTrack("video1");
+ EXPECT_TRUE(video_sender->SetTrack(video_track).ok());
+ EXPECT_EQ(video_track, video_sender->GetTrack());
+ video_track = CreateVideoTrack("video2");
+ EXPECT_TRUE(video_sender->SetTrack(video_track).ok());
+ EXPECT_EQ(video_track, video_sender->GetTrack());
+}
+
+// Test that track can be set when previously unset, even after Send has been
+// called.
+TEST_F(OrtcRtpSenderTest, SetTrackWhileSending) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok());
+ EXPECT_TRUE(audio_sender->SetTrack(CreateAudioTrack("audio")).ok());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok());
+ EXPECT_TRUE(video_sender->SetTrack(CreateVideoTrack("video")).ok());
+}
+
+// Test that track can be changed mid-sending. Differs from the above test in
+// that the track is set and being changed, rather than unset and being set for
+// the first time.
+TEST_F(OrtcRtpSenderTest, ChangeTrackWhileSending) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ CreateAudioTrack("audio1"), rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok());
+ EXPECT_TRUE(audio_sender->SetTrack(CreateAudioTrack("audio2")).ok());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ CreateVideoTrack("video1"), rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok());
+ EXPECT_TRUE(video_sender->SetTrack(CreateVideoTrack("video2")).ok());
+}
+
+// Test that track can be set to null while sending.
+TEST_F(OrtcRtpSenderTest, UnsetTrackWhileSending) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ CreateAudioTrack("audio"), rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok());
+ EXPECT_TRUE(audio_sender->SetTrack(nullptr).ok());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ CreateVideoTrack("video"), rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok());
+ EXPECT_TRUE(video_sender->SetTrack(nullptr).ok());
+}
+
+// Shouldn't be able to set an audio track on a video sender or vice versa.
+TEST_F(OrtcRtpSenderTest, SetTrackOfWrongKindFails) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ audio_sender->SetTrack(CreateVideoTrack("video")).type());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_sender->SetTrack(CreateAudioTrack("audio")).type());
+}
+
+// Currently SetTransport isn't supported. When it is, replace this test with a
+// test/tests for it.
+TEST_F(OrtcRtpSenderTest, SetTransportFails) {
+ rtc::FakePacketTransport fake_packet_transport("another_transport");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport, nullptr, nullptr);
+ auto rtp_transport = rtp_transport_result.MoveValue();
+
+ auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO,
+ rtp_transport_.get());
+ auto sender = sender_result.MoveValue();
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ sender->SetTransport(rtp_transport.get()).type());
+}
+
+TEST_F(OrtcRtpSenderTest, GetTransport) {
+ auto result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO,
+ rtp_transport_.get());
+ EXPECT_EQ(rtp_transport_.get(), result.value()->GetTransport());
+}
+
+// Test that "Send" causes the expected parameters to be applied to the media
+// engine level, for an audio sender.
+TEST_F(OrtcRtpSenderTest, SendAppliesAudioParametersToMediaEngine) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+
+ // First, create parameters with all the bells and whistles.
+ RtpParameters parameters;
+
+ RtpCodecParameters opus_codec;
+ opus_codec.name = "opus";
+ opus_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ opus_codec.payload_type = 120;
+ opus_codec.clock_rate.emplace(48000);
+ opus_codec.num_channels.emplace(2);
+ opus_codec.parameters["minptime"] = "10";
+ opus_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ parameters.codecs.push_back(std::move(opus_codec));
+
+ // Add two codecs, expecting the first to be used.
+ // TODO(deadbeef): Once "codec_payload_type" is supported, use it to select a
+ // codec that's not at the top of the list.
+ RtpCodecParameters isac_codec;
+ isac_codec.name = "ISAC";
+ isac_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ isac_codec.payload_type = 110;
+ isac_codec.clock_rate.emplace(16000);
+ parameters.codecs.push_back(std::move(isac_codec));
+
+ RtpEncodingParameters encoding;
+ encoding.ssrc.emplace(0xdeadbeef);
+ encoding.max_bitrate_bps.emplace(20000);
+ parameters.encodings.push_back(std::move(encoding));
+
+ parameters.header_extensions.emplace_back(
+ "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 3);
+
+ EXPECT_TRUE(audio_sender->Send(parameters).ok());
+
+ // Now verify that the parameters were applied to the fake media engine layer
+ // that exists below BaseChannel.
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->sending());
+
+ // Verify codec parameters.
+ ASSERT_GT(fake_voice_channel->send_codecs().size(), 0u);
+ const cricket::AudioCodec& top_codec = fake_voice_channel->send_codecs()[0];
+ EXPECT_EQ("opus", top_codec.name);
+ EXPECT_EQ(120, top_codec.id);
+ EXPECT_EQ(48000, top_codec.clockrate);
+ EXPECT_EQ(2u, top_codec.channels);
+ ASSERT_NE(top_codec.params.end(), top_codec.params.find("minptime"));
+ EXPECT_EQ("10", top_codec.params.at("minptime"));
+
+ // Verify encoding parameters.
+ EXPECT_EQ(20000, fake_voice_channel->max_bps());
+ ASSERT_EQ(1u, fake_voice_channel->send_streams().size());
+ const cricket::StreamParams& send_stream =
+ fake_voice_channel->send_streams()[0];
+ EXPECT_EQ(1u, send_stream.ssrcs.size());
+ EXPECT_EQ(0xdeadbeef, send_stream.first_ssrc());
+
+ // Verify header extensions.
+ ASSERT_EQ(1u, fake_voice_channel->send_extensions().size());
+ const RtpExtension& extension = fake_voice_channel->send_extensions()[0];
+ EXPECT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", extension.uri);
+ EXPECT_EQ(3, extension.id);
+}
+
+// Test that "Send" causes the expected parameters to be applied to the media
+// engine level, for a video sender.
+TEST_F(OrtcRtpSenderTest, SendAppliesVideoParametersToMediaEngine) {
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+
+ // First, create parameters with all the bells and whistles.
+ RtpParameters parameters;
+
+ RtpCodecParameters vp8_codec;
+ vp8_codec.name = "VP8";
+ vp8_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_codec.payload_type = 99;
+ // Try a couple types of feedback params. "Generic NACK" is a bit of a
+ // special case, so test it here.
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM,
+ RtcpFeedbackMessageType::FIR);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::GENERIC_NACK);
+ parameters.codecs.push_back(std::move(vp8_codec));
+
+ RtpCodecParameters vp8_rtx_codec;
+ vp8_rtx_codec.name = "rtx";
+ vp8_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_rtx_codec.payload_type = 100;
+ vp8_rtx_codec.parameters["apt"] = "99";
+ parameters.codecs.push_back(std::move(vp8_rtx_codec));
+
+ // Add two codecs, expecting the first to be used.
+ // TODO(deadbeef): Once "codec_payload_type" is supported, use it to select a
+ // codec that's not at the top of the list.
+ RtpCodecParameters vp9_codec;
+ vp9_codec.name = "VP9";
+ vp9_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp9_codec.payload_type = 102;
+ parameters.codecs.push_back(std::move(vp9_codec));
+
+ RtpCodecParameters vp9_rtx_codec;
+ vp9_rtx_codec.name = "rtx";
+ vp9_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp9_rtx_codec.payload_type = 103;
+ vp9_rtx_codec.parameters["apt"] = "102";
+ parameters.codecs.push_back(std::move(vp9_rtx_codec));
+
+ RtpEncodingParameters encoding;
+ encoding.ssrc.emplace(0xdeadbeef);
+ encoding.rtx.emplace(0xbaadfeed);
+ encoding.max_bitrate_bps.emplace(99999);
+ parameters.encodings.push_back(std::move(encoding));
+
+ parameters.header_extensions.emplace_back("urn:3gpp:video-orientation", 4);
+ parameters.header_extensions.emplace_back(
+ "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", 6);
+
+ EXPECT_TRUE(video_sender->Send(parameters).ok());
+
+ // Now verify that the parameters were applied to the fake media engine layer
+ // that exists below BaseChannel.
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->sending());
+
+ // Verify codec parameters.
+ ASSERT_GE(fake_video_channel->send_codecs().size(), 2u);
+ const cricket::VideoCodec& top_codec = fake_video_channel->send_codecs()[0];
+ EXPECT_EQ("VP8", top_codec.name);
+ EXPECT_EQ(99, top_codec.id);
+ EXPECT_TRUE(top_codec.feedback_params.Has({"ccm", "fir"}));
+ EXPECT_TRUE(top_codec.feedback_params.Has(cricket::FeedbackParam("nack")));
+
+ const cricket::VideoCodec& rtx_codec = fake_video_channel->send_codecs()[1];
+ EXPECT_EQ("rtx", rtx_codec.name);
+ EXPECT_EQ(100, rtx_codec.id);
+ ASSERT_NE(rtx_codec.params.end(), rtx_codec.params.find("apt"));
+ EXPECT_EQ("99", rtx_codec.params.at("apt"));
+
+ // Verify encoding parameters.
+ EXPECT_EQ(99999, fake_video_channel->max_bps());
+ ASSERT_EQ(1u, fake_video_channel->send_streams().size());
+ const cricket::StreamParams& send_stream =
+ fake_video_channel->send_streams()[0];
+ EXPECT_EQ(2u, send_stream.ssrcs.size());
+ EXPECT_EQ(0xdeadbeef, send_stream.first_ssrc());
+ uint32_t rtx_ssrc = 0u;
+ EXPECT_TRUE(send_stream.GetFidSsrc(send_stream.first_ssrc(), &rtx_ssrc));
+ EXPECT_EQ(0xbaadfeed, rtx_ssrc);
+
+ // Verify header extensions.
+ ASSERT_EQ(2u, fake_video_channel->send_extensions().size());
+ const RtpExtension& extension1 = fake_video_channel->send_extensions()[0];
+ EXPECT_EQ("urn:3gpp:video-orientation", extension1.uri);
+ EXPECT_EQ(4, extension1.id);
+ const RtpExtension& extension2 = fake_video_channel->send_extensions()[1];
+ EXPECT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/playout-delay",
+ extension2.uri);
+ EXPECT_EQ(6, extension2.id);
+}
+
+// Ensure that when primary or RTX SSRCs are left unset, they're generated
+// automatically.
+TEST_F(OrtcRtpSenderTest, SendGeneratesSsrcsWhenEmpty) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ RtpParameters parameters = MakeMinimalOpusParametersWithNoSsrc();
+ // Default RTX parameters, with no SSRC.
+ parameters.encodings[0].rtx.emplace();
+ EXPECT_TRUE(audio_sender->Send(parameters).ok());
+
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ ASSERT_EQ(1u, fake_voice_channel->send_streams().size());
+ const cricket::StreamParams& audio_send_stream =
+ fake_voice_channel->send_streams()[0];
+ EXPECT_NE(0u, audio_send_stream.first_ssrc());
+ uint32_t rtx_ssrc = 0u;
+ EXPECT_TRUE(
+ audio_send_stream.GetFidSsrc(audio_send_stream.first_ssrc(), &rtx_ssrc));
+ EXPECT_NE(0u, rtx_ssrc);
+ EXPECT_NE(audio_send_stream.first_ssrc(), rtx_ssrc);
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ parameters = MakeMinimalVp8ParametersWithNoSsrc();
+ // Default RTX parameters, with no SSRC.
+ parameters.encodings[0].rtx.emplace();
+ EXPECT_TRUE(video_sender->Send(parameters).ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ ASSERT_EQ(1u, fake_video_channel->send_streams().size());
+ const cricket::StreamParams& video_send_stream =
+ fake_video_channel->send_streams()[0];
+ EXPECT_NE(0u, video_send_stream.first_ssrc());
+ rtx_ssrc = 0u;
+ EXPECT_TRUE(
+ video_send_stream.GetFidSsrc(video_send_stream.first_ssrc(), &rtx_ssrc));
+ EXPECT_NE(0u, rtx_ssrc);
+ EXPECT_NE(video_send_stream.first_ssrc(), rtx_ssrc);
+ EXPECT_NE(video_send_stream.first_ssrc(), audio_send_stream.first_ssrc());
+}
+
+// Test changing both the send codec and SSRC at the same time, and verify that
+// the new parameters are applied to the media engine level.
+TEST_F(OrtcRtpSenderTest, CallingSendTwiceChangesParameters) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_TRUE(
+ audio_sender->Send(MakeMinimalOpusParametersWithSsrc(0x11111111)).ok());
+ EXPECT_TRUE(
+ audio_sender->Send(MakeMinimalIsacParametersWithSsrc(0x22222222)).ok());
+
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ ASSERT_GT(fake_voice_channel->send_codecs().size(), 0u);
+ EXPECT_EQ("ISAC", fake_voice_channel->send_codecs()[0].name);
+ ASSERT_EQ(1u, fake_voice_channel->send_streams().size());
+ EXPECT_EQ(0x22222222u, fake_voice_channel->send_streams()[0].first_ssrc());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_TRUE(
+ video_sender->Send(MakeMinimalVp8ParametersWithSsrc(0x33333333)).ok());
+ EXPECT_TRUE(
+ video_sender->Send(MakeMinimalVp9ParametersWithSsrc(0x44444444)).ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ ASSERT_GT(fake_video_channel->send_codecs().size(), 0u);
+ EXPECT_EQ("VP9", fake_video_channel->send_codecs()[0].name);
+ ASSERT_EQ(1u, fake_video_channel->send_streams().size());
+ EXPECT_EQ(0x44444444u, fake_video_channel->send_streams()[0].first_ssrc());
+}
+
+// Ensure that if the |active| flag of RtpEncodingParameters is set to false,
+// sending stops at the media engine level.
+TEST_F(OrtcRtpSenderTest, DeactivatingEncodingStopsSending) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ RtpParameters parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(audio_sender->Send(parameters).ok());
+
+ // Expect "sending" flag to initially be true.
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->sending());
+
+ // Deactivate encoding and expect it to change to false.
+ parameters.encodings[0].active = false;
+ EXPECT_TRUE(audio_sender->Send(parameters).ok());
+ EXPECT_FALSE(fake_voice_channel->sending());
+
+ // Try the same thing for video now.
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ parameters = MakeMinimalVp8Parameters();
+ EXPECT_TRUE(video_sender->Send(parameters).ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->sending());
+
+ parameters.encodings[0].active = false;
+ EXPECT_TRUE(video_sender->Send(parameters).ok());
+ EXPECT_FALSE(fake_video_channel->sending());
+}
+
+// Ensure that calling Send with an empty list of encodings causes send streams
+// at the media engine level to be cleared.
+TEST_F(OrtcRtpSenderTest, CallingSendWithEmptyEncodingsClearsSendStreams) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ RtpParameters parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(audio_sender->Send(parameters).ok());
+ parameters.encodings.clear();
+ EXPECT_TRUE(audio_sender->Send(parameters).ok());
+
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->send_streams().empty());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ parameters = MakeMinimalVp8Parameters();
+ EXPECT_TRUE(video_sender->Send(parameters).ok());
+ parameters.encodings.clear();
+ EXPECT_TRUE(video_sender->Send(parameters).ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->send_streams().empty());
+}
+
+// These errors should be covered by rtpparametersconversion_unittest.cc, but
+// we should at least test that those errors are propogated from calls to Send,
+// with a few examples.
+TEST_F(OrtcRtpSenderTest, SendReturnsErrorOnInvalidParameters) {
+ auto result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_VIDEO,
+ rtp_transport_.get());
+ auto sender = result.MoveValue();
+ // NACK feedback missing message type.
+ RtpParameters invalid_feedback = MakeMinimalVp8Parameters();
+ invalid_feedback.codecs[0].rtcp_feedback.emplace_back(RtcpFeedbackType::NACK);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ sender->Send(invalid_feedback).type());
+ // Negative payload type.
+ RtpParameters invalid_pt = MakeMinimalVp8Parameters();
+ invalid_pt.codecs[0].payload_type = -1;
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, sender->Send(invalid_pt).type());
+ // Duplicate codec payload types.
+ RtpParameters duplicate_payload_types = MakeMinimalVp8Parameters();
+ duplicate_payload_types.codecs.push_back(duplicate_payload_types.codecs[0]);
+ duplicate_payload_types.codecs.back().name = "VP9";
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ sender->Send(duplicate_payload_types).type());
+}
+
+// Two senders using the same transport shouldn't be able to use the same
+// payload type to refer to different codecs, same header extension IDs to
+// refer to different extensions, or same SSRC.
+TEST_F(OrtcRtpSenderTest, SendReturnsErrorOnIdConflicts) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ auto video_sender = video_sender_result.MoveValue();
+
+ // First test payload type conflict.
+ RtpParameters audio_parameters = MakeMinimalOpusParameters();
+ RtpParameters video_parameters = MakeMinimalVp8Parameters();
+ audio_parameters.codecs[0].payload_type = 100;
+ video_parameters.codecs[0].payload_type = 100;
+ EXPECT_TRUE(audio_sender->Send(audio_parameters).ok());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_sender->Send(video_parameters).type());
+
+ // Test header extension ID conflict.
+ video_parameters.codecs[0].payload_type = 110;
+ audio_parameters.header_extensions.emplace_back("foo", 4);
+ video_parameters.header_extensions.emplace_back("bar", 4);
+ EXPECT_TRUE(audio_sender->Send(audio_parameters).ok());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_sender->Send(video_parameters).type());
+
+ // Test SSRC conflict. Have an RTX SSRC that conflicts with a primary SSRC
+ // for extra challenge.
+ video_parameters.header_extensions[0].uri = "foo";
+ audio_parameters.encodings[0].ssrc.emplace(0xdeadbeef);
+ video_parameters.encodings[0].ssrc.emplace(0xabbaabba);
+ video_parameters.encodings[0].rtx.emplace(0xdeadbeef);
+ EXPECT_TRUE(audio_sender->Send(audio_parameters).ok());
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER,
+ video_sender->Send(video_parameters).type());
+
+ // Sanity check that parameters can be set if the conflicts are all resolved.
+ video_parameters.encodings[0].rtx->ssrc.emplace(0xbaadf00d);
+ EXPECT_TRUE(video_sender->Send(video_parameters).ok());
+}
+
+// Ensure that deleting a sender causes send streams at the media engine level
+// to be cleared.
+TEST_F(OrtcRtpSenderTest, DeletingSenderClearsSendStreams) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok());
+
+ // Also create an audio receiver, to prevent the voice channel from being
+ // completely deleted.
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto audio_receiver = audio_receiver_result.MoveValue();
+ EXPECT_TRUE(audio_receiver->Receive(MakeMinimalOpusParameters()).ok());
+
+ audio_sender.reset(nullptr);
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_TRUE(fake_voice_channel->send_streams().empty());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok());
+
+ // Also create an video receiver, to prevent the video channel from being
+ // completely deleted.
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get());
+ auto video_receiver = video_receiver_result.MoveValue();
+ EXPECT_TRUE(video_receiver->Receive(MakeMinimalVp8Parameters()).ok());
+
+ video_sender.reset(nullptr);
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->send_streams().empty());
+}
+
+// If Send hasn't been called, GetParameters should return empty parameters.
+TEST_F(OrtcRtpSenderTest, GetDefaultParameters) {
+ auto result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO,
+ rtp_transport_.get());
+ EXPECT_EQ(RtpParameters(), result.value()->GetParameters());
+ result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_VIDEO,
+ rtp_transport_.get());
+ EXPECT_EQ(RtpParameters(), result.value()->GetParameters());
+}
+
+// Test that GetParameters returns the last parameters passed into Send, along
+// with the implementation-default values filled in where they were left unset.
+TEST_F(OrtcRtpSenderTest,
+ GetParametersReturnsLastSetParametersWithDefaultsFilled) {
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ CreateAudioTrack("audio"), rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+
+ RtpParameters opus_parameters = MakeMinimalOpusParameters();
+ EXPECT_TRUE(audio_sender->Send(opus_parameters).ok());
+ EXPECT_EQ(opus_parameters, audio_sender->GetParameters());
+
+ RtpParameters isac_parameters = MakeMinimalIsacParameters();
+ // Sanity check that num_channels actually is left unset.
+ ASSERT_FALSE(isac_parameters.codecs[0].num_channels);
+ EXPECT_TRUE(audio_sender->Send(isac_parameters).ok());
+ // Should be filled with a default "num channels" of 1.
+ // TODO(deadbeef): This should actually default to 2 for some codecs. Update
+ // this test once that's implemented.
+ isac_parameters.codecs[0].num_channels.emplace(1);
+ EXPECT_EQ(isac_parameters, audio_sender->GetParameters());
+
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ CreateVideoTrack("video"), rtp_transport_.get());
+ auto video_sender = video_sender_result.MoveValue();
+
+ RtpParameters vp8_parameters = MakeMinimalVp8Parameters();
+ // Sanity check that clock_rate actually is left unset.
+ EXPECT_TRUE(video_sender->Send(vp8_parameters).ok());
+ // Should be filled with a default clock rate of 90000.
+ vp8_parameters.codecs[0].clock_rate.emplace(90000);
+ EXPECT_EQ(vp8_parameters, video_sender->GetParameters());
+
+ RtpParameters vp9_parameters = MakeMinimalVp9Parameters();
+ // Sanity check that clock_rate actually is left unset.
+ EXPECT_TRUE(video_sender->Send(vp9_parameters).ok());
+ // Should be filled with a default clock rate of 90000.
+ vp9_parameters.codecs[0].clock_rate.emplace(90000);
+ EXPECT_EQ(vp9_parameters, video_sender->GetParameters());
+}
+
+TEST_F(OrtcRtpSenderTest, GetKind) {
+ // Construct one sender from the "kind" enum and another from a track.
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get());
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ CreateVideoTrack("video"), rtp_transport_.get());
+ auto audio_sender = audio_sender_result.MoveValue();
+ auto video_sender = video_sender_result.MoveValue();
+ EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, audio_sender->GetKind());
+ EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, video_sender->GetKind());
+}
+
+} // namespace webrtc
diff --git a/ortc/ortcrtpsenderadapter.cc b/ortc/ortcrtpsenderadapter.cc
new file mode 100644
index 0000000..be2b65d
--- /dev/null
+++ b/ortc/ortcrtpsenderadapter.cc
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/ortcrtpsenderadapter.h"
+
+#include <utility>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/ortc/rtptransportadapter.h"
+
+namespace {
+
+void FillAudioSenderParameters(webrtc::RtpParameters* parameters) {
+ for (webrtc::RtpCodecParameters& codec : parameters->codecs) {
+ if (!codec.num_channels) {
+ codec.num_channels = rtc::Optional<int>(1);
+ }
+ }
+}
+
+void FillVideoSenderParameters(webrtc::RtpParameters* parameters) {
+ for (webrtc::RtpCodecParameters& codec : parameters->codecs) {
+ if (!codec.clock_rate) {
+ codec.clock_rate = rtc::Optional<int>(cricket::kVideoCodecClockrate);
+ }
+ }
+}
+
+} // namespace
+
+namespace webrtc {
+
+BEGIN_OWNED_PROXY_MAP(OrtcRtpSender)
+PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_METHOD1(RTCError, SetTrack, MediaStreamTrackInterface*)
+PROXY_CONSTMETHOD0(rtc::scoped_refptr<MediaStreamTrackInterface>, GetTrack)
+PROXY_METHOD1(RTCError, SetTransport, RtpTransportInterface*)
+PROXY_CONSTMETHOD0(RtpTransportInterface*, GetTransport)
+PROXY_METHOD1(RTCError, Send, const RtpParameters&)
+PROXY_CONSTMETHOD0(RtpParameters, GetParameters)
+PROXY_CONSTMETHOD0(cricket::MediaType, GetKind)
+END_PROXY_MAP()
+
+// static
+std::unique_ptr<OrtcRtpSenderInterface> OrtcRtpSenderAdapter::CreateProxy(
+ std::unique_ptr<OrtcRtpSenderAdapter> wrapped_sender) {
+ RTC_DCHECK(wrapped_sender);
+ rtc::Thread* signaling =
+ wrapped_sender->rtp_transport_controller_->signaling_thread();
+ rtc::Thread* worker =
+ wrapped_sender->rtp_transport_controller_->worker_thread();
+ return OrtcRtpSenderProxy::Create(signaling, worker,
+ std::move(wrapped_sender));
+}
+
+OrtcRtpSenderAdapter::~OrtcRtpSenderAdapter() {
+ internal_sender_ = nullptr;
+ SignalDestroyed();
+}
+
+RTCError OrtcRtpSenderAdapter::SetTrack(MediaStreamTrackInterface* track) {
+ if (track && cricket::MediaTypeFromString(track->kind()) != kind_) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_PARAMETER,
+ "Track kind (audio/video) doesn't match the kind of this sender.");
+ }
+ if (internal_sender_ && !internal_sender_->SetTrack(track)) {
+ // Since we checked the track type above, this should never happen...
+ RTC_NOTREACHED();
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to set track on RtpSender.");
+ }
+ track_ = track;
+ return RTCError::OK();
+}
+
+rtc::scoped_refptr<MediaStreamTrackInterface> OrtcRtpSenderAdapter::GetTrack()
+ const {
+ return track_;
+}
+
+RTCError OrtcRtpSenderAdapter::SetTransport(RtpTransportInterface* transport) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::UNSUPPORTED_OPERATION,
+ "Changing the transport of an RtpSender is not yet supported.");
+}
+
+RtpTransportInterface* OrtcRtpSenderAdapter::GetTransport() const {
+ return transport_;
+}
+
+RTCError OrtcRtpSenderAdapter::Send(const RtpParameters& parameters) {
+ RtpParameters filled_parameters = parameters;
+ RTCError err;
+ uint32_t ssrc = 0;
+ switch (kind_) {
+ case cricket::MEDIA_TYPE_AUDIO:
+ FillAudioSenderParameters(&filled_parameters);
+ err = rtp_transport_controller_->ValidateAndApplyAudioSenderParameters(
+ filled_parameters, &ssrc);
+ if (!err.ok()) {
+ return err;
+ }
+ break;
+ case cricket::MEDIA_TYPE_VIDEO:
+ FillVideoSenderParameters(&filled_parameters);
+ err = rtp_transport_controller_->ValidateAndApplyVideoSenderParameters(
+ filled_parameters, &ssrc);
+ if (!err.ok()) {
+ return err;
+ }
+ break;
+ case cricket::MEDIA_TYPE_DATA:
+ RTC_NOTREACHED();
+ return webrtc::RTCError(webrtc::RTCErrorType::INTERNAL_ERROR);
+ }
+ last_applied_parameters_ = filled_parameters;
+
+ // Now that parameters were applied, can call SetSsrc on the internal sender.
+ // This is analogous to a PeerConnection calling SetSsrc after
+ // SetLocalDescription is successful.
+ //
+ // If there were no encodings, this SSRC may be 0, which is valid.
+ if (!internal_sender_) {
+ CreateInternalSender();
+ }
+ internal_sender_->SetSsrc(ssrc);
+
+ return RTCError::OK();
+}
+
+RtpParameters OrtcRtpSenderAdapter::GetParameters() const {
+ return last_applied_parameters_;
+}
+
+cricket::MediaType OrtcRtpSenderAdapter::GetKind() const {
+ return kind_;
+}
+
+OrtcRtpSenderAdapter::OrtcRtpSenderAdapter(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport,
+ RtpTransportControllerAdapter* rtp_transport_controller)
+ : kind_(kind),
+ transport_(transport),
+ rtp_transport_controller_(rtp_transport_controller) {}
+
+void OrtcRtpSenderAdapter::CreateInternalSender() {
+ switch (kind_) {
+ case cricket::MEDIA_TYPE_AUDIO:
+ internal_sender_ = new AudioRtpSender(
+ rtp_transport_controller_->voice_channel(), nullptr);
+ break;
+ case cricket::MEDIA_TYPE_VIDEO:
+ internal_sender_ =
+ new VideoRtpSender(rtp_transport_controller_->video_channel());
+ break;
+ case cricket::MEDIA_TYPE_DATA:
+ RTC_NOTREACHED();
+ }
+ if (track_) {
+ if (!internal_sender_->SetTrack(track_)) {
+ // Since we checked the track type when it was set, this should never
+ // happen...
+ RTC_NOTREACHED();
+ }
+ }
+}
+
+} // namespace webrtc
diff --git a/ortc/ortcrtpsenderadapter.h b/ortc/ortcrtpsenderadapter.h
new file mode 100644
index 0000000..9b60f15
--- /dev/null
+++ b/ortc/ortcrtpsenderadapter.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_ORTCRTPSENDERADAPTER_H_
+#define WEBRTC_ORTC_ORTCRTPSENDERADAPTER_H_
+
+#include <memory>
+
+#include "webrtc/api/ortc/ortcrtpsenderinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/rtpparameters.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/ortc/rtptransportcontrolleradapter.h"
+#include "webrtc/pc/rtpsender.h"
+
+namespace webrtc {
+
+// Implementation of OrtcRtpSenderInterface that works with RtpTransportAdapter,
+// and wraps a VideoRtpSender/AudioRtpSender that's normally used with the
+// PeerConnection.
+//
+// TODO(deadbeef): When BaseChannel is split apart into separate
+// "RtpSender"/"RtpTransceiver"/"RtpSender"/"RtpReceiver" objects, this adapter
+// object can be removed.
+class OrtcRtpSenderAdapter : public OrtcRtpSenderInterface {
+ public:
+ // Wraps |wrapped_sender| in a proxy that will safely call methods on the
+ // correct thread.
+ static std::unique_ptr<OrtcRtpSenderInterface> CreateProxy(
+ std::unique_ptr<OrtcRtpSenderAdapter> wrapped_sender);
+
+ // Should only be called by RtpTransportControllerAdapter.
+ OrtcRtpSenderAdapter(cricket::MediaType kind,
+ RtpTransportInterface* transport,
+ RtpTransportControllerAdapter* rtp_transport_controller);
+ ~OrtcRtpSenderAdapter() override;
+
+ // OrtcRtpSenderInterface implementation.
+ RTCError SetTrack(MediaStreamTrackInterface* track) override;
+ rtc::scoped_refptr<MediaStreamTrackInterface> GetTrack() const override;
+
+ RTCError SetTransport(RtpTransportInterface* transport) override;
+ RtpTransportInterface* GetTransport() const override;
+
+ RTCError Send(const RtpParameters& parameters) override;
+ RtpParameters GetParameters() const override;
+
+ cricket::MediaType GetKind() const override;
+
+ // Used so that the RtpTransportControllerAdapter knows when it can
+ // deallocate resources allocated for this object.
+ sigslot::signal0<> SignalDestroyed;
+
+ private:
+ void CreateInternalSender();
+
+ cricket::MediaType kind_;
+ RtpTransportInterface* transport_;
+ RtpTransportControllerAdapter* rtp_transport_controller_;
+ // Scoped refptr due to ref-counted interface, but we should be the only
+ // reference holder.
+ rtc::scoped_refptr<RtpSenderInternal> internal_sender_;
+ rtc::scoped_refptr<MediaStreamTrackInterface> track_;
+ RtpParameters last_applied_parameters_;
+
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(OrtcRtpSenderAdapter);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_ORTCRTPSENDERADAPTER_H_
diff --git a/ortc/rtpparametersconversion.cc b/ortc/rtpparametersconversion.cc
new file mode 100644
index 0000000..63ee9c7
--- /dev/null
+++ b/ortc/rtpparametersconversion.cc
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/rtpparametersconversion.h"
+
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include "webrtc/media/base/rtputils.h"
+
+namespace webrtc {
+
+RTCErrorOr<cricket::FeedbackParam> ToCricketFeedbackParam(
+ const RtcpFeedback& feedback) {
+ switch (feedback.type) {
+ case RtcpFeedbackType::CCM:
+ if (!feedback.message_type) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Missing message type in CCM RtcpFeedback.");
+ } else if (*feedback.message_type != RtcpFeedbackMessageType::FIR) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Invalid message type in CCM RtcpFeedback.");
+ }
+ return cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
+ cricket::kRtcpFbCcmParamFir);
+ case RtcpFeedbackType::NACK:
+ if (!feedback.message_type) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Missing message type in NACK RtcpFeedback.");
+ }
+ switch (*feedback.message_type) {
+ case RtcpFeedbackMessageType::GENERIC_NACK:
+ return cricket::FeedbackParam(cricket::kRtcpFbParamNack);
+ case RtcpFeedbackMessageType::PLI:
+ return cricket::FeedbackParam(cricket::kRtcpFbParamNack,
+ cricket::kRtcpFbNackParamPli);
+ default:
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Invalid message type in NACK RtcpFeedback.");
+ }
+ case RtcpFeedbackType::REMB:
+ if (feedback.message_type) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_PARAMETER,
+ "Didn't expect message type in REMB RtcpFeedback.");
+ }
+ return cricket::FeedbackParam(cricket::kRtcpFbParamRemb);
+ case RtcpFeedbackType::TRANSPORT_CC:
+ if (feedback.message_type) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_PARAMETER,
+ "Didn't expect message type in transport-cc RtcpFeedback.");
+ }
+ return cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc);
+ }
+ // Not reached; avoids compile warning.
+ FATAL();
+}
+
+template <typename C>
+static RTCError ToCricketCodecTypeSpecific(const RtpCodecParameters& codec,
+ C* cricket_codec);
+
+template <>
+RTCError ToCricketCodecTypeSpecific<cricket::AudioCodec>(
+ const RtpCodecParameters& codec,
+ cricket::AudioCodec* cricket_codec) {
+ if (codec.kind != cricket::MEDIA_TYPE_AUDIO) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_PARAMETER,
+ "Can't use video codec with audio sender or receiver.");
+ }
+ if (!codec.num_channels) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Missing number of channels for audio codec.");
+ }
+ if (*codec.num_channels <= 0) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Number of channels must be positive.");
+ }
+ cricket_codec->channels = *codec.num_channels;
+ if (!codec.clock_rate) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Missing codec clock rate.");
+ }
+ if (*codec.clock_rate <= 0) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Clock rate must be positive.");
+ }
+ cricket_codec->clockrate = *codec.clock_rate;
+ return RTCError::OK();
+}
+
+// Video codecs don't use num_channels or clock_rate, but they should at least
+// be validated to ensure the application isn't trying to do something it
+// doesn't intend to.
+template <>
+RTCError ToCricketCodecTypeSpecific<cricket::VideoCodec>(
+ const RtpCodecParameters& codec,
+ cricket::VideoCodec*) {
+ if (codec.kind != cricket::MEDIA_TYPE_VIDEO) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_PARAMETER,
+ "Can't use audio codec with video sender or receiver.");
+ }
+ if (codec.num_channels) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Video codec shouldn't have num_channels.");
+ }
+ if (!codec.clock_rate) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Missing codec clock rate.");
+ }
+ if (*codec.clock_rate != cricket::kVideoCodecClockrate) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Video clock rate must be 90000.");
+ }
+ return RTCError::OK();
+}
+
+template <typename C>
+RTCErrorOr<C> ToCricketCodec(const RtpCodecParameters& codec) {
+ C cricket_codec;
+ // Start with audio/video specific conversion.
+ RTCError err = ToCricketCodecTypeSpecific(codec, &cricket_codec);
+ if (!err.ok()) {
+ return std::move(err);
+ }
+ cricket_codec.name = codec.name;
+ if (!cricket::IsValidRtpPayloadType(codec.payload_type)) {
+ std::ostringstream oss;
+ oss << "Invalid payload type: " << codec.payload_type;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, oss.str());
+ }
+ cricket_codec.id = codec.payload_type;
+ for (const RtcpFeedback& feedback : codec.rtcp_feedback) {
+ auto result = ToCricketFeedbackParam(feedback);
+ if (!result.ok()) {
+ return result.MoveError();
+ }
+ cricket_codec.AddFeedbackParam(result.MoveValue());
+ }
+ cricket_codec.params.insert(codec.parameters.begin(), codec.parameters.end());
+ return std::move(cricket_codec);
+}
+
+template RTCErrorOr<cricket::AudioCodec> ToCricketCodec(
+ const RtpCodecParameters& codec);
+template RTCErrorOr<cricket::VideoCodec> ToCricketCodec(
+ const RtpCodecParameters& codec);
+
+template <typename C>
+RTCErrorOr<std::vector<C>> ToCricketCodecs(
+ const std::vector<RtpCodecParameters>& codecs) {
+ std::vector<C> cricket_codecs;
+ std::set<int> seen_payload_types;
+ for (const RtpCodecParameters& codec : codecs) {
+ auto result = ToCricketCodec<C>(codec);
+ if (!result.ok()) {
+ return result.MoveError();
+ }
+ if (!seen_payload_types.insert(codec.payload_type).second) {
+ std::ostringstream oss;
+ oss << "Duplicate payload type: " << codec.payload_type;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
+ }
+ cricket_codecs.push_back(result.MoveValue());
+ }
+ return std::move(cricket_codecs);
+}
+
+template RTCErrorOr<std::vector<cricket::AudioCodec>> ToCricketCodecs<
+ cricket::AudioCodec>(const std::vector<RtpCodecParameters>& codecs);
+
+template RTCErrorOr<std::vector<cricket::VideoCodec>> ToCricketCodecs<
+ cricket::VideoCodec>(const std::vector<RtpCodecParameters>& codecs);
+
+RTCErrorOr<cricket::RtpHeaderExtensions> ToCricketRtpHeaderExtensions(
+ const std::vector<RtpHeaderExtensionParameters>& extensions) {
+ cricket::RtpHeaderExtensions cricket_extensions;
+ std::ostringstream err_writer;
+ std::set<int> seen_header_extension_ids;
+ for (const RtpHeaderExtensionParameters& extension : extensions) {
+ if (extension.id < RtpHeaderExtensionParameters::kMinId ||
+ extension.id > RtpHeaderExtensionParameters::kMaxId) {
+ err_writer << "Invalid header extension id: " << extension.id;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE, err_writer.str());
+ }
+ if (!seen_header_extension_ids.insert(extension.id).second) {
+ err_writer << "Duplicate header extension id: " << extension.id;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, err_writer.str());
+ }
+ cricket_extensions.push_back(extension);
+ }
+ return std::move(cricket_extensions);
+}
+
+RTCErrorOr<cricket::StreamParamsVec> ToCricketStreamParamsVec(
+ const std::vector<RtpEncodingParameters>& encodings) {
+ if (encodings.size() > 1u) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
+ "ORTC API implementation doesn't currently "
+ "support simulcast or layered encodings.");
+ } else if (encodings.empty()) {
+ return cricket::StreamParamsVec();
+ }
+ cricket::StreamParamsVec cricket_streams;
+ const RtpEncodingParameters& encoding = encodings[0];
+ if (encoding.rtx && encoding.rtx->ssrc && !encoding.ssrc) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
+ "Setting an RTX SSRC explicitly while leaving the "
+ "primary SSRC unset is not currently supported.");
+ }
+ if (encoding.ssrc) {
+ cricket::StreamParams stream_params;
+ stream_params.add_ssrc(*encoding.ssrc);
+ if (encoding.rtx && encoding.rtx->ssrc) {
+ stream_params.AddFidSsrc(*encoding.ssrc, *encoding.rtx->ssrc);
+ }
+ cricket_streams.push_back(std::move(stream_params));
+ }
+ return std::move(cricket_streams);
+}
+
+rtc::Optional<RtcpFeedback> ToRtcpFeedback(
+ const cricket::FeedbackParam& cricket_feedback) {
+ if (cricket_feedback.id() == cricket::kRtcpFbParamCcm) {
+ if (cricket_feedback.param() == cricket::kRtcpFbCcmParamFir) {
+ return rtc::Optional<RtcpFeedback>(
+ {RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR});
+ } else {
+ LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: "
+ << cricket_feedback.param();
+ return rtc::Optional<RtcpFeedback>();
+ }
+ } else if (cricket_feedback.id() == cricket::kRtcpFbParamNack) {
+ if (cricket_feedback.param().empty()) {
+ return rtc::Optional<RtcpFeedback>(
+ {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::GENERIC_NACK});
+ } else if (cricket_feedback.param() == cricket::kRtcpFbNackParamPli) {
+ return rtc::Optional<RtcpFeedback>(
+ {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI});
+ } else {
+ LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: "
+ << cricket_feedback.param();
+ return rtc::Optional<RtcpFeedback>();
+ }
+ } else if (cricket_feedback.id() == cricket::kRtcpFbParamRemb) {
+ if (!cricket_feedback.param().empty()) {
+ LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: "
+ << cricket_feedback.param();
+ return rtc::Optional<RtcpFeedback>();
+ } else {
+ return rtc::Optional<RtcpFeedback>(RtcpFeedback(RtcpFeedbackType::REMB));
+ }
+ } else if (cricket_feedback.id() == cricket::kRtcpFbParamTransportCc) {
+ if (!cricket_feedback.param().empty()) {
+ LOG(LS_WARNING)
+ << "Unsupported parameter for transport-cc RTCP feedback: "
+ << cricket_feedback.param();
+ return rtc::Optional<RtcpFeedback>();
+ } else {
+ return rtc::Optional<RtcpFeedback>(
+ RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC));
+ }
+ }
+ LOG(LS_WARNING) << "Unsupported RTCP feedback type: "
+ << cricket_feedback.id();
+ return rtc::Optional<RtcpFeedback>();
+}
+
+template <typename C>
+cricket::MediaType KindOfCodec();
+
+template <>
+cricket::MediaType KindOfCodec<cricket::AudioCodec>() {
+ return cricket::MEDIA_TYPE_AUDIO;
+}
+
+template <>
+cricket::MediaType KindOfCodec<cricket::VideoCodec>() {
+ return cricket::MEDIA_TYPE_VIDEO;
+}
+
+template <typename C>
+static void ToRtpCodecCapabilityTypeSpecific(const C& cricket_codec,
+ RtpCodecCapability* codec);
+
+template <>
+void ToRtpCodecCapabilityTypeSpecific<cricket::AudioCodec>(
+ const cricket::AudioCodec& cricket_codec,
+ RtpCodecCapability* codec) {
+ codec->num_channels =
+ rtc::Optional<int>(static_cast<int>(cricket_codec.channels));
+}
+
+template <>
+void ToRtpCodecCapabilityTypeSpecific<cricket::VideoCodec>(
+ const cricket::VideoCodec& cricket_codec,
+ RtpCodecCapability* codec) {}
+
+template <typename C>
+RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec) {
+ RtpCodecCapability codec;
+ codec.name = cricket_codec.name;
+ codec.kind = KindOfCodec<C>();
+ codec.clock_rate.emplace(cricket_codec.clockrate);
+ codec.preferred_payload_type.emplace(cricket_codec.id);
+ for (const cricket::FeedbackParam& cricket_feedback :
+ cricket_codec.feedback_params.params()) {
+ rtc::Optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback);
+ if (feedback) {
+ codec.rtcp_feedback.push_back(feedback.MoveValue());
+ }
+ }
+ ToRtpCodecCapabilityTypeSpecific(cricket_codec, &codec);
+ codec.parameters.insert(cricket_codec.params.begin(),
+ cricket_codec.params.end());
+ return codec;
+}
+
+template RtpCodecCapability ToRtpCodecCapability<cricket::AudioCodec>(
+ const cricket::AudioCodec& cricket_codec);
+template RtpCodecCapability ToRtpCodecCapability<cricket::VideoCodec>(
+ const cricket::VideoCodec& cricket_codec);
+
+template <class C>
+RtpCapabilities ToRtpCapabilities(
+ const std::vector<C>& cricket_codecs,
+ const cricket::RtpHeaderExtensions& cricket_extensions) {
+ RtpCapabilities capabilities;
+ bool have_red = false;
+ bool have_ulpfec = false;
+ bool have_flexfec = false;
+ for (const C& cricket_codec : cricket_codecs) {
+ if (cricket_codec.name == cricket::kRedCodecName) {
+ have_red = true;
+ } else if (cricket_codec.name == cricket::kUlpfecCodecName) {
+ have_ulpfec = true;
+ } else if (cricket_codec.name == cricket::kFlexfecCodecName) {
+ have_flexfec = true;
+ }
+ capabilities.codecs.push_back(ToRtpCodecCapability(cricket_codec));
+ }
+ for (const RtpExtension& cricket_extension : cricket_extensions) {
+ capabilities.header_extensions.emplace_back(cricket_extension.uri,
+ cricket_extension.id);
+ }
+ if (have_red) {
+ capabilities.fec.push_back(FecMechanism::RED);
+ }
+ if (have_red && have_ulpfec) {
+ capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC);
+ }
+ if (have_flexfec) {
+ capabilities.fec.push_back(FecMechanism::FLEXFEC);
+ }
+ return capabilities;
+}
+
+template RtpCapabilities ToRtpCapabilities<cricket::AudioCodec>(
+ const std::vector<cricket::AudioCodec>& cricket_codecs,
+ const cricket::RtpHeaderExtensions& cricket_extensions);
+template RtpCapabilities ToRtpCapabilities<cricket::VideoCodec>(
+ const std::vector<cricket::VideoCodec>& cricket_codecs,
+ const cricket::RtpHeaderExtensions& cricket_extensions);
+
+} // namespace webrtc
diff --git a/ortc/rtpparametersconversion.h b/ortc/rtpparametersconversion.h
new file mode 100644
index 0000000..a1680a2
--- /dev/null
+++ b/ortc/rtpparametersconversion.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_RTPPARAMETERSCONVERSION_H_
+#define WEBRTC_ORTC_RTPPARAMETERSCONVERSION_H_
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/api/rtpparameters.h"
+#include "webrtc/base/optional.h"
+#include "webrtc/pc/mediasession.h"
+#include "webrtc/media/base/codec.h"
+
+namespace webrtc {
+
+// NOTE: Some functions are templated for convenience, such that template-based
+// code dealing with AudioContentDescription and VideoContentDescription can
+// use this easily. Such methods are usable with cricket::AudioCodec and
+// cricket::VideoCodec.
+
+//***************************************************************************
+// Functions for converting from new webrtc:: structures to old cricket::
+// structures.
+//
+// As the return values imply, all of these functions do validation of the
+// parameters and return an error if they're invalid. It's expected that any
+// default values (such as video clock rate of 90000) have been filled by the
+// time the webrtc:: structure is being converted to the cricket:: one.
+//
+// These are expected to be used when parameters are passed into an RtpSender
+// or RtpReceiver, and need to be validated and converted so they can be
+// applied to the media engine level.
+//***************************************************************************
+
+// Returns error on invalid input. Certain message types are only valid for
+// certain feedback types.
+RTCErrorOr<cricket::FeedbackParam> ToCricketFeedbackParam(
+ const RtcpFeedback& feedback);
+
+// Verifies that the codec kind is correct, and it has mandatory parameters
+// filled, with values in valid ranges.
+template <typename C>
+RTCErrorOr<C> ToCricketCodec(const RtpCodecParameters& codec);
+
+// Verifies that payload types aren't duplicated, in addition to normal
+// validation.
+template <typename C>
+RTCErrorOr<std::vector<C>> ToCricketCodecs(
+ const std::vector<RtpCodecParameters>& codecs);
+
+// Validates that header extension IDs aren't duplicated.
+RTCErrorOr<cricket::RtpHeaderExtensions> ToCricketRtpHeaderExtensions(
+ const std::vector<RtpHeaderExtensionParameters>& extensions);
+
+// SSRCs are allowed to be ommitted. This may be used for receive parameters
+// where SSRCs are unsignaled.
+RTCErrorOr<cricket::StreamParamsVec> ToCricketStreamParamsVec(
+ const std::vector<RtpEncodingParameters>& encodings);
+
+//*****************************************************************************
+// Functions for converting from old cricket:: structures to new webrtc::
+// structures. Unlike the above functions, these are permissive with regards to
+// input validation; it's assumed that any necessary validation already
+// occurred.
+//
+// These are expected to be used either to convert from audio/video engine
+// capabilities to RtpCapabilities, or to convert from already-parsed SDP
+// (in the form of cricket:: structures) to webrtc:: structures. The latter
+// functionality is not yet implemented.
+//*****************************************************************************
+
+// Returns empty value if |cricket_feedback| is a feedback type not
+// supported/recognized.
+rtc::Optional<RtcpFeedback> ToRtcpFeedback(
+ const cricket::FeedbackParam& cricket_feedback);
+
+template <typename C>
+RtpCodecCapability ToRtpCodecCapability(const C& cricket_codec);
+
+template <class C>
+RtpCapabilities ToRtpCapabilities(
+ const std::vector<C>& cricket_codecs,
+ const cricket::RtpHeaderExtensions& cricket_extensions);
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_RTPPARAMETERSCONVERSION_H_
diff --git a/ortc/rtpparametersconversion_unittest.cc b/ortc/rtpparametersconversion_unittest.cc
new file mode 100644
index 0000000..6bb335c
--- /dev/null
+++ b/ortc/rtpparametersconversion_unittest.cc
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/ortc/rtpparametersconversion.h"
+#include "webrtc/ortc/testrtpparameters.h"
+
+namespace webrtc {
+
+TEST(RtpParametersConversionTest, ToCricketFeedbackParam) {
+ auto result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR});
+ EXPECT_EQ(cricket::FeedbackParam("ccm", "fir"), result.value());
+ result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::GENERIC_NACK});
+ EXPECT_EQ(cricket::FeedbackParam("nack"), result.value());
+ result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI});
+ EXPECT_EQ(cricket::FeedbackParam("nack", "pli"), result.value());
+ result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::REMB));
+ EXPECT_EQ(cricket::FeedbackParam("goog-remb"), result.value());
+ result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC));
+ EXPECT_EQ(cricket::FeedbackParam("transport-cc"), result.value());
+}
+
+TEST(RtpParametersConversionTest, ToCricketFeedbackParamErrors) {
+ // CCM with missing or invalid message type.
+ auto result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::CCM));
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+ result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::CCM, RtcpFeedbackMessageType::PLI});
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+ // NACK with missing or invalid message type.
+ result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::NACK));
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+ result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::FIR});
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+ // REMB with message type (should be left empty).
+ result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::REMB, RtcpFeedbackMessageType::GENERIC_NACK});
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+ // TRANSPORT_CC with message type (should be left empty).
+ result = ToCricketFeedbackParam(
+ {RtcpFeedbackType::TRANSPORT_CC, RtcpFeedbackMessageType::FIR});
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+TEST(RtpParametersConversionTest, ToAudioCodec) {
+ RtpCodecParameters codec;
+ codec.name = "AuDiO";
+ codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ codec.payload_type = 120;
+ codec.clock_rate.emplace(36000);
+ codec.num_channels.emplace(6);
+ codec.parameters["foo"] = "bar";
+ codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ auto result = ToCricketCodec<cricket::AudioCodec>(codec);
+ ASSERT_TRUE(result.ok());
+
+ EXPECT_EQ("AuDiO", result.value().name);
+ EXPECT_EQ(120, result.value().id);
+ EXPECT_EQ(36000, result.value().clockrate);
+ EXPECT_EQ(6u, result.value().channels);
+ ASSERT_EQ(1u, result.value().params.size());
+ EXPECT_EQ("bar", result.value().params["foo"]);
+ EXPECT_EQ(1u, result.value().feedback_params.params().size());
+ EXPECT_TRUE(result.value().feedback_params.Has(
+ cricket::FeedbackParam("transport-cc")));
+}
+
+TEST(RtpParametersConversionTest, ToVideoCodec) {
+ RtpCodecParameters codec;
+ codec.name = "coolcodec";
+ codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ codec.payload_type = 101;
+ codec.clock_rate.emplace(90000);
+ codec.parameters["foo"] = "bar";
+ codec.parameters["PING"] = "PONG";
+ codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::PLI);
+ auto result = ToCricketCodec<cricket::VideoCodec>(codec);
+ ASSERT_TRUE(result.ok());
+
+ EXPECT_EQ("coolcodec", result.value().name);
+ EXPECT_EQ(101, result.value().id);
+ EXPECT_EQ(90000, result.value().clockrate);
+ ASSERT_EQ(2u, result.value().params.size());
+ EXPECT_EQ("bar", result.value().params["foo"]);
+ EXPECT_EQ("PONG", result.value().params["PING"]);
+ EXPECT_EQ(2u, result.value().feedback_params.params().size());
+ EXPECT_TRUE(result.value().feedback_params.Has(
+ cricket::FeedbackParam("transport-cc")));
+ EXPECT_TRUE(result.value().feedback_params.Has(
+ cricket::FeedbackParam("nack", "pli")));
+}
+
+// Trying to convert to an AudioCodec if the kind is "video" should fail.
+TEST(RtpParametersConversionTest, ToCricketCodecInvalidKind) {
+ RtpCodecParameters audio_codec;
+ audio_codec.name = "opus";
+ audio_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ audio_codec.payload_type = 111;
+ audio_codec.clock_rate.emplace(48000);
+ audio_codec.num_channels.emplace(2);
+
+ RtpCodecParameters video_codec;
+ video_codec.name = "VP8";
+ video_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ video_codec.payload_type = 102;
+ video_codec.clock_rate.emplace(90000);
+
+ auto audio_result = ToCricketCodec<cricket::AudioCodec>(audio_codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, audio_result.error().type());
+
+ auto video_result = ToCricketCodec<cricket::VideoCodec>(video_codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, video_result.error().type());
+
+ // Sanity check that if the kind is correct, the conversion succeeds.
+ audio_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ video_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ audio_result = ToCricketCodec<cricket::AudioCodec>(audio_codec);
+ EXPECT_TRUE(audio_result.ok());
+ video_result = ToCricketCodec<cricket::VideoCodec>(video_codec);
+ EXPECT_TRUE(video_result.ok());
+}
+
+TEST(RtpParametersConversionTest, ToAudioCodecInvalidParameters) {
+ // Missing channels.
+ RtpCodecParameters codec;
+ codec.name = "opus";
+ codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ codec.payload_type = 111;
+ codec.clock_rate.emplace(48000);
+ auto result = ToCricketCodec<cricket::AudioCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Negative number of channels.
+ codec.num_channels.emplace(-1);
+ result = ToCricketCodec<cricket::AudioCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+
+ // Missing clock rate.
+ codec.num_channels.emplace(2);
+ codec.clock_rate.reset();
+ result = ToCricketCodec<cricket::AudioCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Negative clock rate.
+ codec.clock_rate.emplace(-48000);
+ result = ToCricketCodec<cricket::AudioCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+
+ // Sanity check that conversion succeeds if these errors are fixed.
+ codec.clock_rate.emplace(48000);
+ result = ToCricketCodec<cricket::AudioCodec>(codec);
+ EXPECT_TRUE(result.ok());
+}
+
+TEST(RtpParametersConversionTest, ToVideoCodecInvalidParameters) {
+ // Missing clock rate.
+ RtpCodecParameters codec;
+ codec.name = "VP8";
+ codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ codec.payload_type = 102;
+ auto result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Invalid clock rate.
+ codec.clock_rate.emplace(48000);
+ result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Channels set (should be unset).
+ codec.clock_rate.emplace(90000);
+ codec.num_channels.emplace(2);
+ result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Sanity check that conversion succeeds if these errors are fixed.
+ codec.num_channels.reset();
+ result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_TRUE(result.ok());
+}
+
+TEST(RtpParametersConversionTest, ToCricketCodecInvalidPayloadType) {
+ RtpCodecParameters codec;
+ codec.name = "VP8";
+ codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ codec.clock_rate.emplace(90000);
+
+ codec.payload_type = -1000;
+ auto result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+
+ // Max payload type is 127.
+ codec.payload_type = 128;
+ result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+
+ // Sanity check that conversion succeeds with a valid payload type.
+ codec.payload_type = 127;
+ result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_TRUE(result.ok());
+}
+
+// There are already tests for ToCricketFeedbackParam, but ensure that those
+// errors are propagated from ToCricketCodec.
+TEST(RtpParametersConversionTest, ToCricketCodecInvalidRtcpFeedback) {
+ RtpCodecParameters codec;
+ codec.name = "VP8";
+ codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ codec.clock_rate.emplace(90000);
+ codec.payload_type = 99;
+ codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM,
+ RtcpFeedbackMessageType::PLI);
+
+ auto result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Sanity check that conversion succeeds without invalid feedback.
+ codec.rtcp_feedback.clear();
+ result = ToCricketCodec<cricket::VideoCodec>(codec);
+ EXPECT_TRUE(result.ok());
+}
+
+TEST(RtpParametersConversionTest, ToCricketCodecs) {
+ std::vector<RtpCodecParameters> codecs;
+ RtpCodecParameters codec;
+ codec.name = "VP8";
+ codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ codec.clock_rate.emplace(90000);
+ codec.payload_type = 99;
+ codecs.push_back(codec);
+
+ codec.name = "VP9";
+ codec.payload_type = 100;
+ codecs.push_back(codec);
+
+ auto result = ToCricketCodecs<cricket::VideoCodec>(codecs);
+ ASSERT_TRUE(result.ok());
+ ASSERT_EQ(2u, result.value().size());
+ EXPECT_EQ("VP8", result.value()[0].name);
+ EXPECT_EQ(99, result.value()[0].id);
+ EXPECT_EQ("VP9", result.value()[1].name);
+ EXPECT_EQ(100, result.value()[1].id);
+}
+
+TEST(RtpParametersConversionTest, ToCricketCodecsDuplicatePayloadType) {
+ std::vector<RtpCodecParameters> codecs;
+ RtpCodecParameters codec;
+ codec.name = "VP8";
+ codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ codec.clock_rate.emplace(90000);
+ codec.payload_type = 99;
+ codecs.push_back(codec);
+
+ codec.name = "VP9";
+ codec.payload_type = 99;
+ codecs.push_back(codec);
+
+ auto result = ToCricketCodecs<cricket::VideoCodec>(codecs);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+
+ // Sanity check that this succeeds without the duplicate payload type.
+ codecs[1].payload_type = 120;
+ result = ToCricketCodecs<cricket::VideoCodec>(codecs);
+ EXPECT_TRUE(result.ok());
+}
+
+TEST(RtpParametersConversionTest, ToCricketRtpHeaderExtensions) {
+ std::vector<RtpHeaderExtensionParameters> extensions = {
+ {"http://example.com", 1}, {"urn:foo:bar", 14}};
+ auto result = ToCricketRtpHeaderExtensions(extensions);
+ ASSERT_TRUE(result.ok());
+ ASSERT_EQ(2u, result.value().size());
+ EXPECT_EQ("http://example.com", result.value()[0].uri);
+ EXPECT_EQ(1, result.value()[0].id);
+ EXPECT_EQ("urn:foo:bar", result.value()[1].uri);
+ EXPECT_EQ(14, result.value()[1].id);
+}
+
+TEST(RtpParametersConversionTest, ToCricketRtpHeaderExtensionsErrors) {
+ // First, IDs outside the range 1-14.
+ std::vector<RtpHeaderExtensionParameters> extensions = {
+ {"http://example.com", 0}};
+ auto result = ToCricketRtpHeaderExtensions(extensions);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+
+ extensions[0].id = 15;
+ result = ToCricketRtpHeaderExtensions(extensions);
+ EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type());
+
+ // Duplicate IDs.
+ extensions = {{"http://example.com", 1}, {"urn:foo:bar", 1}};
+ result = ToCricketRtpHeaderExtensions(extensions);
+ EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type());
+}
+
+TEST(RtpParametersConversionTest, ToCricketStreamParamsVecSimple) {
+ std::vector<RtpEncodingParameters> encodings;
+ RtpEncodingParameters encoding;
+ encoding.ssrc.emplace(0xbaadf00d);
+ encodings.push_back(encoding);
+ auto result = ToCricketStreamParamsVec(encodings);
+ ASSERT_TRUE(result.ok());
+ ASSERT_EQ(1u, result.value().size());
+ EXPECT_EQ(1u, result.value()[0].ssrcs.size());
+ EXPECT_EQ(0xbaadf00d, result.value()[0].first_ssrc());
+}
+
+TEST(RtpParametersConversionTest, ToCricketStreamParamsVecWithRtx) {
+ std::vector<RtpEncodingParameters> encodings;
+ RtpEncodingParameters encoding;
+ // Test a corner case SSRC of 0.
+ encoding.ssrc.emplace(0u);
+ encoding.rtx.emplace(0xdeadbeef);
+ encodings.push_back(encoding);
+ auto result = ToCricketStreamParamsVec(encodings);
+ ASSERT_TRUE(result.ok());
+ ASSERT_EQ(1u, result.value().size());
+ EXPECT_EQ(2u, result.value()[0].ssrcs.size());
+ EXPECT_EQ(0u, result.value()[0].first_ssrc());
+ uint32_t rtx_ssrc = 0;
+ EXPECT_TRUE(result.value()[0].GetFidSsrc(0u, &rtx_ssrc));
+ EXPECT_EQ(0xdeadbeef, rtx_ssrc);
+}
+
+// No encodings should be accepted; an endpoint may want to prepare a
+// decoder/encoder without having something to receive/send yet.
+TEST(RtpParametersConversionTest, ToCricketStreamParamsVecNoEncodings) {
+ std::vector<RtpEncodingParameters> encodings;
+ auto result = ToCricketStreamParamsVec(encodings);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(0u, result.value().size());
+}
+
+// An encoding without SSRCs should be accepted. This could be the case when
+// SSRCs aren't signaled and payload-type based demuxing is used.
+TEST(RtpParametersConversionTest, ToCricketStreamParamsVecMissingSsrcs) {
+ std::vector<RtpEncodingParameters> encodings = {{}};
+ // Creates RtxParameters with empty SSRC.
+ encodings[0].rtx.emplace();
+ auto result = ToCricketStreamParamsVec(encodings);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(0u, result.value().size());
+}
+
+// The media engine doesn't have a way of receiving an RTX SSRC that's known
+// with a primary SSRC that's unknown, so this should produce an error.
+TEST(RtpParametersConversionTest, ToStreamParamsWithPrimarySsrcSetAndRtxUnset) {
+ std::vector<RtpEncodingParameters> encodings = {{}};
+ encodings[0].rtx.emplace(0xdeadbeef);
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
+ ToCricketStreamParamsVec(encodings).error().type());
+}
+
+// TODO(deadbeef): Update this test when we support multiple encodings.
+TEST(RtpParametersConversionTest, ToCricketStreamParamsVecMultipleEncodings) {
+ std::vector<RtpEncodingParameters> encodings = {{}, {}};
+ auto result = ToCricketStreamParamsVec(encodings);
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, result.error().type());
+}
+
+TEST(RtpParametersConversionTest, ToRtcpFeedback) {
+ rtc::Optional<RtcpFeedback> result = ToRtcpFeedback({"ccm", "fir"});
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR),
+ *result);
+ result = ToRtcpFeedback(cricket::FeedbackParam("nack"));
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::GENERIC_NACK),
+ *result);
+ result = ToRtcpFeedback({"nack", "pli"});
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI),
+ *result);
+ result = ToRtcpFeedback(cricket::FeedbackParam("goog-remb"));
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::REMB), *result);
+ result = ToRtcpFeedback(cricket::FeedbackParam("transport-cc"));
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), *result);
+}
+
+TEST(RtpParametersConversionTest, ToRtcpFeedbackErrors) {
+ // CCM with missing or invalid message type.
+ rtc::Optional<RtcpFeedback> result = ToRtcpFeedback({"ccm", "pli"});
+ EXPECT_FALSE(result);
+ result = ToRtcpFeedback(cricket::FeedbackParam("ccm"));
+ EXPECT_FALSE(result);
+ // NACK with missing or invalid message type.
+ result = ToRtcpFeedback({"nack", "fir"});
+ EXPECT_FALSE(result);
+ // REMB with message type (should be left empty).
+ result = ToRtcpFeedback({"goog-remb", "pli"});
+ EXPECT_FALSE(result);
+ // TRANSPORT_CC with message type (should be left empty).
+ result = ToRtcpFeedback({"transport-cc", "fir"});
+ EXPECT_FALSE(result);
+ // Unknown message type.
+ result = ToRtcpFeedback(cricket::FeedbackParam("foo"));
+ EXPECT_FALSE(result);
+}
+
+TEST(RtpParametersConversionTest, ToAudioRtpCodecCapability) {
+ cricket::AudioCodec cricket_codec;
+ cricket_codec.name = "foo";
+ cricket_codec.id = 50;
+ cricket_codec.clockrate = 22222;
+ cricket_codec.channels = 4;
+ cricket_codec.params["foo"] = "bar";
+ cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc"));
+ RtpCodecCapability codec = ToRtpCodecCapability(cricket_codec);
+
+ EXPECT_EQ("foo", codec.name);
+ EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, codec.kind);
+ EXPECT_EQ(rtc::Optional<int>(50), codec.preferred_payload_type);
+ EXPECT_EQ(rtc::Optional<int>(22222), codec.clock_rate);
+ EXPECT_EQ(rtc::Optional<int>(4), codec.num_channels);
+ ASSERT_EQ(1u, codec.parameters.size());
+ EXPECT_EQ("bar", codec.parameters["foo"]);
+ EXPECT_EQ(1u, codec.rtcp_feedback.size());
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC),
+ codec.rtcp_feedback[0]);
+}
+
+TEST(RtpParametersConversionTest, ToVideoRtpCodecCapability) {
+ cricket::VideoCodec cricket_codec;
+ cricket_codec.name = "VID";
+ cricket_codec.id = 101;
+ cricket_codec.clockrate = 80000;
+ cricket_codec.params["foo"] = "bar";
+ cricket_codec.params["ANOTHER"] = "param";
+ cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc"));
+ cricket_codec.feedback_params.Add({"nack", "pli"});
+ RtpCodecCapability codec = ToRtpCodecCapability(cricket_codec);
+
+ EXPECT_EQ("VID", codec.name);
+ EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, codec.kind);
+ EXPECT_EQ(rtc::Optional<int>(101), codec.preferred_payload_type);
+ EXPECT_EQ(rtc::Optional<int>(80000), codec.clock_rate);
+ ASSERT_EQ(2u, codec.parameters.size());
+ EXPECT_EQ("bar", codec.parameters["foo"]);
+ EXPECT_EQ("param", codec.parameters["ANOTHER"]);
+ EXPECT_EQ(2u, codec.rtcp_feedback.size());
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC),
+ codec.rtcp_feedback[0]);
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI),
+ codec.rtcp_feedback[1]);
+}
+
+// An unknown feedback param should just be ignored.
+TEST(RtpParametersConversionTest, ToRtpCodecCapabilityUnknownFeedbackParam) {
+ cricket::AudioCodec cricket_codec;
+ cricket_codec.name = "foo";
+ cricket_codec.id = 50;
+ cricket_codec.clockrate = 22222;
+ cricket_codec.channels = 4;
+ cricket_codec.params["foo"] = "bar";
+ cricket_codec.feedback_params.Add({"unknown", "param"});
+ cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc"));
+ RtpCodecCapability codec = ToRtpCodecCapability(cricket_codec);
+
+ ASSERT_EQ(1u, codec.rtcp_feedback.size());
+ EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC),
+ codec.rtcp_feedback[0]);
+}
+
+// Most of ToRtpCapabilities is tested by ToRtpCodecCapability, but we need to
+// test that the result of ToRtpCodecCapability ends up in the result, and that
+// the "fec" list is assembled correctly.
+TEST(RtpParametersConversionTest, ToRtpCapabilities) {
+ cricket::VideoCodec vp8;
+ vp8.name = "VP8";
+ vp8.id = 101;
+ vp8.clockrate = 90000;
+
+ cricket::VideoCodec red;
+ red.name = "red";
+ red.id = 102;
+ red.clockrate = 90000;
+
+ cricket::VideoCodec ulpfec;
+ ulpfec.name = "ulpfec";
+ ulpfec.id = 103;
+ ulpfec.clockrate = 90000;
+
+ cricket::VideoCodec flexfec;
+ flexfec.name = "flexfec-03";
+ flexfec.id = 102;
+ flexfec.clockrate = 90000;
+
+ RtpCapabilities capabilities = ToRtpCapabilities<cricket::VideoCodec>(
+ {vp8, ulpfec}, {{"uri", 1}, {"uri2", 3}});
+ ASSERT_EQ(2u, capabilities.codecs.size());
+ EXPECT_EQ("VP8", capabilities.codecs[0].name);
+ EXPECT_EQ("ulpfec", capabilities.codecs[1].name);
+ ASSERT_EQ(2u, capabilities.header_extensions.size());
+ EXPECT_EQ("uri", capabilities.header_extensions[0].uri);
+ EXPECT_EQ(1, capabilities.header_extensions[0].preferred_id);
+ EXPECT_EQ("uri2", capabilities.header_extensions[1].uri);
+ EXPECT_EQ(3, capabilities.header_extensions[1].preferred_id);
+ EXPECT_EQ(0u, capabilities.fec.size());
+
+ capabilities = ToRtpCapabilities<cricket::VideoCodec>(
+ {vp8, red, ulpfec}, cricket::RtpHeaderExtensions());
+ EXPECT_EQ(3u, capabilities.codecs.size());
+ EXPECT_EQ(2u, capabilities.fec.size());
+ EXPECT_NE(capabilities.fec.end(),
+ std::find(capabilities.fec.begin(), capabilities.fec.end(),
+ FecMechanism::RED));
+ EXPECT_NE(capabilities.fec.end(),
+ std::find(capabilities.fec.begin(), capabilities.fec.end(),
+ FecMechanism::RED_AND_ULPFEC));
+
+ capabilities = ToRtpCapabilities<cricket::VideoCodec>(
+ {vp8, red, flexfec}, cricket::RtpHeaderExtensions());
+ EXPECT_EQ(3u, capabilities.codecs.size());
+ EXPECT_EQ(2u, capabilities.fec.size());
+ EXPECT_NE(capabilities.fec.end(),
+ std::find(capabilities.fec.begin(), capabilities.fec.end(),
+ FecMechanism::RED));
+ EXPECT_NE(capabilities.fec.end(),
+ std::find(capabilities.fec.begin(), capabilities.fec.end(),
+ FecMechanism::FLEXFEC));
+}
+
+} // namespace webrtc
diff --git a/ortc/rtptransport_unittest.cc b/ortc/rtptransport_unittest.cc
new file mode 100644
index 0000000..6d16230
--- /dev/null
+++ b/ortc/rtptransport_unittest.cc
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/media/base/fakemediaengine.h"
+#include "webrtc/ortc/ortcfactory.h"
+#include "webrtc/ortc/testrtpparameters.h"
+#include "webrtc/p2p/base/fakepackettransport.h"
+
+namespace webrtc {
+
+// This test uses fake packet transports and a fake media engine, in order to
+// test the RtpTransport at only an API level. Any end-to-end test should go in
+// ortcfactory_integrationtest.cc instead.
+class RtpTransportTest : public testing::Test {
+ public:
+ RtpTransportTest() {
+ fake_media_engine_ = new cricket::FakeMediaEngine();
+ // Note: This doesn't need to use fake network classes, since it uses
+ // FakePacketTransports.
+ auto result = OrtcFactory::Create(
+ nullptr, nullptr, nullptr, nullptr, nullptr,
+ std::unique_ptr<cricket::MediaEngineInterface>(fake_media_engine_));
+ ortc_factory_ = result.MoveValue();
+ }
+
+ protected:
+ // Owned by |ortc_factory_|.
+ cricket::FakeMediaEngine* fake_media_engine_;
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory_;
+};
+
+// Test GetRtpPacketTransport and GetRtcpPacketTransport, with and without RTCP
+// muxing.
+TEST_F(RtpTransportTest, GetPacketTransports) {
+ rtc::FakePacketTransport rtp("rtp");
+ rtc::FakePacketTransport rtcp("rtcp");
+ // With muxed RTCP.
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ auto result = ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp,
+ nullptr, nullptr);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(&rtp, result.value()->GetRtpPacketTransport());
+ EXPECT_EQ(nullptr, result.value()->GetRtcpPacketTransport());
+ result.MoveValue().reset();
+ // With non-muxed RTCP.
+ rtcp_parameters.mux = false;
+ result =
+ ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp, &rtcp, nullptr);
+ ASSERT_TRUE(result.ok());
+ EXPECT_EQ(&rtp, result.value()->GetRtpPacketTransport());
+ EXPECT_EQ(&rtcp, result.value()->GetRtcpPacketTransport());
+}
+
+// If an RtpTransport starts out un-muxed and then starts muxing, the RTCP
+// packet transport should be forgotten and GetRtcpPacketTransport should
+// return null.
+TEST_F(RtpTransportTest, EnablingRtcpMuxingUnsetsRtcpTransport) {
+ rtc::FakePacketTransport rtp("rtp");
+ rtc::FakePacketTransport rtcp("rtcp");
+
+ // Create non-muxed.
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = false;
+ auto result =
+ ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp, &rtcp, nullptr);
+ ASSERT_TRUE(result.ok());
+ auto rtp_transport = result.MoveValue();
+
+ // Enable muxing.
+ rtcp_parameters.mux = true;
+ EXPECT_TRUE(rtp_transport->SetRtcpParameters(rtcp_parameters).ok());
+ EXPECT_EQ(nullptr, rtp_transport->GetRtcpPacketTransport());
+}
+
+TEST_F(RtpTransportTest, GetAndSetRtcpParameters) {
+ rtc::FakePacketTransport rtp("rtp");
+ rtc::FakePacketTransport rtcp("rtcp");
+ // Start with non-muxed RTCP.
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = false;
+ rtcp_parameters.cname = "teST";
+ rtcp_parameters.reduced_size = false;
+ auto result =
+ ortc_factory_->CreateRtpTransport(rtcp_parameters, &rtp, &rtcp, nullptr);
+ ASSERT_TRUE(result.ok());
+ auto transport = result.MoveValue();
+ EXPECT_EQ(rtcp_parameters, transport->GetRtcpParameters());
+
+ // Changing the CNAME is currently unsupported.
+ rtcp_parameters.cname = "different";
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ transport->SetRtcpParameters(rtcp_parameters).type());
+ rtcp_parameters.cname = "teST";
+
+ // Enable RTCP muxing and reduced-size RTCP.
+ rtcp_parameters.mux = true;
+ rtcp_parameters.reduced_size = true;
+ EXPECT_TRUE(transport->SetRtcpParameters(rtcp_parameters).ok());
+ EXPECT_EQ(rtcp_parameters, transport->GetRtcpParameters());
+
+ // Empty CNAME should result in the existing CNAME being used.
+ rtcp_parameters.cname.clear();
+ EXPECT_TRUE(transport->SetRtcpParameters(rtcp_parameters).ok());
+ EXPECT_EQ("teST", transport->GetRtcpParameters().cname);
+
+ // Disabling RTCP muxing after enabling shouldn't be allowed, since enabling
+ // muxing should have made the RTP transport forget about the RTCP packet
+ // transport initially passed into it.
+ rtcp_parameters.mux = false;
+ EXPECT_EQ(RTCErrorType::INVALID_STATE,
+ transport->SetRtcpParameters(rtcp_parameters).type());
+}
+
+// When Send or Receive is called on a sender or receiver, the RTCP parameters
+// from the RtpTransport underneath the sender should be applied to the created
+// media stream. The only relevant parameters (currently) are |cname| and
+// |reduced_size|.
+TEST_F(RtpTransportTest, SendAndReceiveApplyRtcpParametersToMediaEngine) {
+ // First, create video transport with reduced-size RTCP.
+ rtc::FakePacketTransport fake_packet_transport1("1");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ rtcp_parameters.reduced_size = true;
+ rtcp_parameters.cname = "foo";
+ auto rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport1, nullptr, nullptr);
+ auto video_transport = rtp_transport_result.MoveValue();
+
+ // Create video sender and call Send, expecting parameters to be applied.
+ auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_VIDEO,
+ video_transport.get());
+ auto video_sender = sender_result.MoveValue();
+ EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok());
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->send_rtcp_parameters().reduced_size);
+ ASSERT_EQ(1u, fake_video_channel->send_streams().size());
+ const cricket::StreamParams& video_send_stream =
+ fake_video_channel->send_streams()[0];
+ EXPECT_EQ("foo", video_send_stream.cname);
+
+ // Create video receiver and call Receive, expecting parameters to be applied
+ // (minus |cname|, since that's the sent cname, not received).
+ auto receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, video_transport.get());
+ auto video_receiver = receiver_result.MoveValue();
+ EXPECT_TRUE(
+ video_receiver->Receive(MakeMinimalVp8ParametersWithSsrc(0xdeadbeef))
+ .ok());
+ EXPECT_TRUE(fake_video_channel->recv_rtcp_parameters().reduced_size);
+
+ // Create audio transport with non-reduced size RTCP.
+ rtc::FakePacketTransport fake_packet_transport2("2");
+ rtcp_parameters.reduced_size = false;
+ rtcp_parameters.cname = "bar";
+ rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport2, nullptr, nullptr);
+ auto audio_transport = rtp_transport_result.MoveValue();
+
+ // Create audio sender and call Send, expecting parameters to be applied.
+ sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO,
+ audio_transport.get());
+ auto audio_sender = sender_result.MoveValue();
+ EXPECT_TRUE(audio_sender->Send(MakeMinimalIsacParameters()).ok());
+
+ cricket::FakeVoiceMediaChannel* fake_voice_channel =
+ fake_media_engine_->GetVoiceChannel(0);
+ ASSERT_NE(nullptr, fake_voice_channel);
+ EXPECT_FALSE(fake_voice_channel->send_rtcp_parameters().reduced_size);
+ ASSERT_EQ(1u, fake_voice_channel->send_streams().size());
+ const cricket::StreamParams& audio_send_stream =
+ fake_voice_channel->send_streams()[0];
+ EXPECT_EQ("bar", audio_send_stream.cname);
+
+ // Create audio receiver and call Receive, expecting parameters to be applied
+ // (minus |cname|, since that's the sent cname, not received).
+ receiver_result = ortc_factory_->CreateRtpReceiver(cricket::MEDIA_TYPE_AUDIO,
+ audio_transport.get());
+ auto audio_receiver = receiver_result.MoveValue();
+ EXPECT_TRUE(
+ audio_receiver->Receive(MakeMinimalOpusParametersWithSsrc(0xbaadf00d))
+ .ok());
+ EXPECT_FALSE(fake_voice_channel->recv_rtcp_parameters().reduced_size);
+}
+
+// When SetRtcpParameters is called, the modified parameters should be applied
+// to the media engine.
+// TODO(deadbeef): Once the implementation supports changing the CNAME,
+// test that here.
+TEST_F(RtpTransportTest, SetRtcpParametersAppliesParametersToMediaEngine) {
+ rtc::FakePacketTransport fake_packet_transport("fake");
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ rtcp_parameters.reduced_size = false;
+ auto rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ rtcp_parameters, &fake_packet_transport, nullptr, nullptr);
+ auto rtp_transport = rtp_transport_result.MoveValue();
+
+ // Create video sender and call Send, applying an initial set of parameters.
+ auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_VIDEO,
+ rtp_transport.get());
+ auto sender = sender_result.MoveValue();
+ EXPECT_TRUE(sender->Send(MakeMinimalVp8Parameters()).ok());
+
+ // Modify parameters and expect them to be changed at the media engine level.
+ rtcp_parameters.reduced_size = true;
+ EXPECT_TRUE(rtp_transport->SetRtcpParameters(rtcp_parameters).ok());
+
+ cricket::FakeVideoMediaChannel* fake_video_channel =
+ fake_media_engine_->GetVideoChannel(0);
+ ASSERT_NE(nullptr, fake_video_channel);
+ EXPECT_TRUE(fake_video_channel->send_rtcp_parameters().reduced_size);
+}
+
+} // namespace webrtc
diff --git a/ortc/rtptransportadapter.cc b/ortc/rtptransportadapter.cc
new file mode 100644
index 0000000..439f9a8
--- /dev/null
+++ b/ortc/rtptransportadapter.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/rtptransportadapter.h"
+
+#include <algorithm> // For std::find.
+#include <set>
+#include <sstream>
+#include <utility> // For std::move.
+
+#include "webrtc/api/proxy.h"
+#include "webrtc/base/logging.h"
+
+namespace webrtc {
+
+BEGIN_OWNED_PROXY_MAP(RtpTransport)
+PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_CONSTMETHOD0(PacketTransportInterface*, GetRtpPacketTransport)
+PROXY_CONSTMETHOD0(PacketTransportInterface*, GetRtcpPacketTransport)
+PROXY_METHOD1(RTCError, SetRtcpParameters, const RtcpParameters&)
+PROXY_CONSTMETHOD0(RtcpParameters, GetRtcpParameters)
+protected:
+RtpTransportAdapter* GetInternal() override {
+ return internal();
+}
+END_PROXY_MAP()
+
+// static
+RTCErrorOr<std::unique_ptr<RtpTransportInterface>>
+RtpTransportAdapter::CreateProxied(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerAdapter* rtp_transport_controller) {
+ if (!rtp) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Must provide an RTP packet transport.");
+ }
+ if (!rtcp_parameters.mux && !rtcp) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_PARAMETER,
+ "Must provide an RTCP packet transport when RTCP muxing is not used.");
+ }
+ if (rtcp_parameters.mux && rtcp) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Creating an RtpTransport with RTCP muxing enabled, "
+ "with a separate RTCP packet transport?");
+ }
+ if (!rtp_transport_controller) {
+ // Since OrtcFactory::CreateRtpTransport creates an RtpTransportController
+ // automatically when one isn't passed in, this should never be reached.
+ RTC_NOTREACHED();
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "Must provide an RTP transport controller.");
+ }
+ return RtpTransportProxyWithInternal<RtpTransportAdapter>::Create(
+ rtp_transport_controller->signaling_thread(),
+ rtp_transport_controller->worker_thread(),
+ std::unique_ptr<RtpTransportAdapter>(new RtpTransportAdapter(
+ rtcp_parameters, rtp, rtcp, rtp_transport_controller)));
+}
+
+void RtpTransportAdapter::TakeOwnershipOfRtpTransportController(
+ std::unique_ptr<RtpTransportControllerInterface> controller) {
+ RTC_DCHECK_EQ(rtp_transport_controller_, controller->GetInternal());
+ RTC_DCHECK(owned_rtp_transport_controller_.get() == nullptr);
+ owned_rtp_transport_controller_ = std::move(controller);
+}
+
+RtpTransportAdapter::RtpTransportAdapter(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerAdapter* rtp_transport_controller)
+ : rtp_packet_transport_(rtp),
+ rtcp_packet_transport_(rtcp),
+ rtp_transport_controller_(rtp_transport_controller),
+ rtcp_parameters_(rtcp_parameters) {
+ RTC_DCHECK(rtp_transport_controller);
+ // CNAME should have been filled by OrtcFactory if empty.
+ RTC_DCHECK(!rtcp_parameters_.cname.empty());
+}
+
+RtpTransportAdapter::~RtpTransportAdapter() {
+ SignalDestroyed(this);
+}
+
+PacketTransportInterface* RtpTransportAdapter::GetRtpPacketTransport() const {
+ return rtp_packet_transport_;
+}
+
+PacketTransportInterface* RtpTransportAdapter::GetRtcpPacketTransport() const {
+ return rtcp_packet_transport_;
+}
+
+RTCError RtpTransportAdapter::SetRtcpParameters(
+ const RtcpParameters& parameters) {
+ if (!parameters.mux && rtcp_parameters_.mux) {
+ LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::INVALID_STATE,
+ "Can't disable RTCP muxing after enabling.");
+ }
+ if (!parameters.cname.empty() && parameters.cname != rtcp_parameters_.cname) {
+ LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
+ "Changing the RTCP CNAME is currently unsupported.");
+ }
+ // If the CNAME is empty, use the existing one.
+ RtcpParameters copy = parameters;
+ if (copy.cname.empty()) {
+ copy.cname = rtcp_parameters_.cname;
+ }
+ RTCError err = rtp_transport_controller_->SetRtcpParameters(copy, this);
+ if (!err.ok()) {
+ return err;
+ }
+ rtcp_parameters_ = copy;
+ if (rtcp_parameters_.mux) {
+ rtcp_packet_transport_ = nullptr;
+ }
+ return RTCError::OK();
+}
+
+} // namespace webrtc
diff --git a/ortc/rtptransportadapter.h b/ortc/rtptransportadapter.h
new file mode 100644
index 0000000..169ae61
--- /dev/null
+++ b/ortc/rtptransportadapter.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_RTPTRANSPORTADAPTER_H_
+#define WEBRTC_ORTC_RTPTRANSPORTADAPTER_H_
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/api/ortc/rtptransportinterface.h"
+#include "webrtc/api/rtcerror.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/media/base/streamparams.h"
+#include "webrtc/ortc/rtptransportcontrolleradapter.h"
+#include "webrtc/pc/channel.h"
+
+namespace webrtc {
+
+// Implementation of RtpTransportInterface to be used with RtpSenderAdapter,
+// RtpReceiverAdapter, and RtpTransportControllerAdapter classes.
+//
+// TODO(deadbeef): When BaseChannel is split apart into separate
+// "RtpTransport"/"RtpTransceiver"/"RtpSender"/"RtpReceiver" objects, this
+// adapter object can be removed.
+class RtpTransportAdapter : public RtpTransportInterface {
+ public:
+ // |rtp| can't be null. |rtcp| can if RTCP muxing is used immediately (meaning
+ // |rtcp_parameters.mux| is also true).
+ static RTCErrorOr<std::unique_ptr<RtpTransportInterface>> CreateProxied(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerAdapter* rtp_transport_controller);
+ ~RtpTransportAdapter() override;
+
+ // RtpTransportInterface implementation.
+ PacketTransportInterface* GetRtpPacketTransport() const override;
+ PacketTransportInterface* GetRtcpPacketTransport() const override;
+ RTCError SetRtcpParameters(const RtcpParameters& parameters) override;
+ RtcpParameters GetRtcpParameters() const override { return rtcp_parameters_; }
+
+ // Methods used internally by OrtcFactory.
+ RtpTransportControllerAdapter* rtp_transport_controller() {
+ return rtp_transport_controller_;
+ }
+ void TakeOwnershipOfRtpTransportController(
+ std::unique_ptr<RtpTransportControllerInterface> controller);
+
+ // Used by RtpTransportControllerAdapter to tell when it should stop
+ // returning this transport from GetTransports().
+ sigslot::signal1<RtpTransportAdapter*> SignalDestroyed;
+
+ protected:
+ RtpTransportAdapter* GetInternal() override { return this; }
+
+ private:
+ RtpTransportAdapter(const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp,
+ RtpTransportControllerAdapter* rtp_transport_controller);
+
+ PacketTransportInterface* rtp_packet_transport_;
+ PacketTransportInterface* rtcp_packet_transport_;
+ RtpTransportControllerAdapter* rtp_transport_controller_;
+ // Non-null if this class owns the transport controller.
+ std::unique_ptr<RtpTransportControllerInterface>
+ owned_rtp_transport_controller_;
+ RtcpParameters rtcp_parameters_;
+
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpTransportAdapter);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_RTPTRANSPORTADAPTER_H_
diff --git a/ortc/rtptransportcontroller_unittest.cc b/ortc/rtptransportcontroller_unittest.cc
new file mode 100644
index 0000000..40e9851
--- /dev/null
+++ b/ortc/rtptransportcontroller_unittest.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/media/base/fakemediaengine.h"
+#include "webrtc/ortc/ortcfactory.h"
+#include "webrtc/ortc/testrtpparameters.h"
+#include "webrtc/p2p/base/fakepackettransport.h"
+
+namespace webrtc {
+
+// This test uses fake packet transports and a fake media engine, in order to
+// test the RtpTransportController at only an API level. Any end-to-end test
+// should go in ortcfactory_integrationtest.cc instead.
+//
+// Currently, this test mainly focuses on the limitations of the "adapter"
+// RtpTransportController implementation. Only one of each type of
+// sender/receiver can be created, and the sender/receiver of the same media
+// type must use the same transport.
+class RtpTransportControllerTest : public testing::Test {
+ public:
+ RtpTransportControllerTest() {
+ // Note: This doesn't need to use fake network classes, since it uses
+ // FakePacketTransports.
+ auto result =
+ OrtcFactory::Create(nullptr, nullptr, nullptr, nullptr, nullptr,
+ std::unique_ptr<cricket::MediaEngineInterface>(
+ new cricket::FakeMediaEngine()));
+ ortc_factory_ = result.MoveValue();
+ rtp_transport_controller_ =
+ ortc_factory_->CreateRtpTransportController().MoveValue();
+ }
+
+ protected:
+ std::unique_ptr<OrtcFactoryInterface> ortc_factory_;
+ std::unique_ptr<RtpTransportControllerInterface> rtp_transport_controller_;
+};
+
+TEST_F(RtpTransportControllerTest, GetTransports) {
+ rtc::FakePacketTransport packet_transport1("one");
+ rtc::FakePacketTransport packet_transport2("two");
+
+ auto rtp_transport_result1 = ortc_factory_->CreateRtpTransport(
+ MakeRtcpMuxParameters(), &packet_transport1, nullptr,
+ rtp_transport_controller_.get());
+ ASSERT_TRUE(rtp_transport_result1.ok());
+
+ auto rtp_transport_result2 = ortc_factory_->CreateRtpTransport(
+ MakeRtcpMuxParameters(), &packet_transport2, nullptr,
+ rtp_transport_controller_.get());
+ ASSERT_TRUE(rtp_transport_result2.ok());
+
+ auto returned_transports = rtp_transport_controller_->GetTransports();
+ ASSERT_EQ(2u, returned_transports.size());
+ EXPECT_EQ(rtp_transport_result1.value().get(), returned_transports[0]);
+ EXPECT_EQ(rtp_transport_result2.value().get(), returned_transports[1]);
+
+ // If a transport is deleted, it shouldn't be returned any more.
+ rtp_transport_result1.MoveValue().reset();
+ returned_transports = rtp_transport_controller_->GetTransports();
+ ASSERT_EQ(1u, returned_transports.size());
+ EXPECT_EQ(rtp_transport_result2.value().get(), returned_transports[0]);
+}
+
+// Create RtpSenders and RtpReceivers on top of RtpTransports controlled by the
+// same RtpTransportController. Currently only one each of audio/video is
+// supported.
+TEST_F(RtpTransportControllerTest, AttachMultipleSendersAndReceivers) {
+ rtc::FakePacketTransport audio_packet_transport("audio");
+ rtc::FakePacketTransport video_packet_transport("video");
+
+ auto audio_rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ MakeRtcpMuxParameters(), &audio_packet_transport, nullptr,
+ rtp_transport_controller_.get());
+ ASSERT_TRUE(audio_rtp_transport_result.ok());
+ auto audio_rtp_transport = audio_rtp_transport_result.MoveValue();
+
+ auto video_rtp_transport_result = ortc_factory_->CreateRtpTransport(
+ MakeRtcpMuxParameters(), &video_packet_transport, nullptr,
+ rtp_transport_controller_.get());
+ ASSERT_TRUE(video_rtp_transport_result.ok());
+ auto video_rtp_transport = video_rtp_transport_result.MoveValue();
+
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transport.get());
+ EXPECT_TRUE(audio_sender_result.ok());
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transport.get());
+ EXPECT_TRUE(audio_receiver_result.ok());
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transport.get());
+ EXPECT_TRUE(video_sender_result.ok());
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transport.get());
+ EXPECT_TRUE(video_receiver_result.ok());
+
+ // Now that we have one each of audio/video senders/receivers, trying to
+ // create more on top of the same controller is expected to fail.
+ // TODO(deadbeef): Update this test once multiple senders/receivers on top of
+ // the same controller is supported.
+ auto failed_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ failed_sender_result.error().type());
+ auto failed_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ failed_receiver_result.error().type());
+ failed_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ failed_sender_result.error().type());
+ failed_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transport.get());
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ failed_receiver_result.error().type());
+
+ // If we destroy the existing sender/receiver using a transport controller,
+ // we should be able to make a new one, despite the above limitation.
+ audio_sender_result.MoveValue().reset();
+ audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transport.get());
+ EXPECT_TRUE(audio_sender_result.ok());
+ audio_receiver_result.MoveValue().reset();
+ audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, audio_rtp_transport.get());
+ EXPECT_TRUE(audio_receiver_result.ok());
+ video_sender_result.MoveValue().reset();
+ video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transport.get());
+ EXPECT_TRUE(video_sender_result.ok());
+ video_receiver_result.MoveValue().reset();
+ video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, video_rtp_transport.get());
+ EXPECT_TRUE(video_receiver_result.ok());
+}
+
+// Given the current limitations of the BaseChannel-based implementation, it's
+// not possible for an audio sender and receiver to use different RtpTransports.
+// TODO(deadbeef): Once this is supported, update/replace this test.
+TEST_F(RtpTransportControllerTest,
+ SenderAndReceiverUsingDifferentTransportsUnsupported) {
+ rtc::FakePacketTransport packet_transport1("one");
+ rtc::FakePacketTransport packet_transport2("two");
+
+ auto rtp_transport_result1 = ortc_factory_->CreateRtpTransport(
+ MakeRtcpMuxParameters(), &packet_transport1, nullptr,
+ rtp_transport_controller_.get());
+ ASSERT_TRUE(rtp_transport_result1.ok());
+ auto rtp_transport1 = rtp_transport_result1.MoveValue();
+
+ auto rtp_transport_result2 = ortc_factory_->CreateRtpTransport(
+ MakeRtcpMuxParameters(), &packet_transport2, nullptr,
+ rtp_transport_controller_.get());
+ ASSERT_TRUE(rtp_transport_result2.ok());
+ auto rtp_transport2 = rtp_transport_result2.MoveValue();
+
+ // Create an audio sender on transport 1, then try to create a receiver on 2.
+ auto audio_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport1.get());
+ EXPECT_TRUE(audio_sender_result.ok());
+ auto audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport2.get());
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ audio_receiver_result.error().type());
+ // Delete the sender; now we should be ok to create the receiver on 2.
+ audio_sender_result.MoveValue().reset();
+ audio_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_AUDIO, rtp_transport2.get());
+ EXPECT_TRUE(audio_receiver_result.ok());
+
+ // Do the same thing for video, reversing 1 and 2 (for variety).
+ auto video_sender_result = ortc_factory_->CreateRtpSender(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport2.get());
+ EXPECT_TRUE(video_sender_result.ok());
+ auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport1.get());
+ EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
+ video_receiver_result.error().type());
+ video_sender_result.MoveValue().reset();
+ video_receiver_result = ortc_factory_->CreateRtpReceiver(
+ cricket::MEDIA_TYPE_VIDEO, rtp_transport1.get());
+ EXPECT_TRUE(video_receiver_result.ok());
+}
+
+} // namespace webrtc
diff --git a/ortc/rtptransportcontrolleradapter.cc b/ortc/rtptransportcontrolleradapter.cc
new file mode 100644
index 0000000..08e943a
--- /dev/null
+++ b/ortc/rtptransportcontrolleradapter.cc
@@ -0,0 +1,899 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/rtptransportcontrolleradapter.h"
+
+#include <algorithm> // For "remove", "find".
+#include <sstream>
+#include <set>
+#include <unordered_map>
+#include <utility> // For std::move.
+
+#include "webrtc/api/proxy.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/media/base/mediaconstants.h"
+#include "webrtc/ortc/ortcrtpreceiveradapter.h"
+#include "webrtc/ortc/ortcrtpsenderadapter.h"
+#include "webrtc/ortc/rtpparametersconversion.h"
+#include "webrtc/ortc/rtptransportadapter.h"
+
+namespace webrtc {
+
+// Note: It's assumed that each individual list doesn't have conflicts, since
+// they should have been detected already by rtpparametersconversion.cc. This
+// only needs to detect conflicts *between* A and B.
+template <typename C1, typename C2>
+static RTCError CheckForIdConflicts(
+ const std::vector<C1>& codecs_a,
+ const cricket::RtpHeaderExtensions& extensions_a,
+ const cricket::StreamParamsVec& streams_a,
+ const std::vector<C2>& codecs_b,
+ const cricket::RtpHeaderExtensions& extensions_b,
+ const cricket::StreamParamsVec& streams_b) {
+ std::ostringstream oss;
+ // Since it's assumed that C1 and C2 are different types, codecs_a and
+ // codecs_b should never contain the same payload type, and thus we can just
+ // use a set.
+ std::set<int> seen_payload_types;
+ for (const C1& codec : codecs_a) {
+ seen_payload_types.insert(codec.id);
+ }
+ for (const C2& codec : codecs_b) {
+ if (!seen_payload_types.insert(codec.id).second) {
+ oss << "Same payload type used for audio and video codecs: " << codec.id;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
+ }
+ }
+ // Audio and video *may* use the same header extensions, so use a map.
+ std::unordered_map<int, std::string> seen_extensions;
+ for (const webrtc::RtpExtension& extension : extensions_a) {
+ seen_extensions[extension.id] = extension.uri;
+ }
+ for (const webrtc::RtpExtension& extension : extensions_b) {
+ if (seen_extensions.find(extension.id) != seen_extensions.end() &&
+ seen_extensions.at(extension.id) != extension.uri) {
+ oss << "Same ID used for different RTP header extensions: "
+ << extension.id;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
+ }
+ }
+ std::set<uint32_t> seen_ssrcs;
+ for (const cricket::StreamParams& stream : streams_a) {
+ seen_ssrcs.insert(stream.ssrcs.begin(), stream.ssrcs.end());
+ }
+ for (const cricket::StreamParams& stream : streams_b) {
+ for (uint32_t ssrc : stream.ssrcs) {
+ if (!seen_ssrcs.insert(ssrc).second) {
+ oss << "Same SSRC used for audio and video senders: " << ssrc;
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, oss.str());
+ }
+ }
+ }
+ return RTCError::OK();
+}
+
+BEGIN_OWNED_PROXY_MAP(RtpTransportController)
+PROXY_SIGNALING_THREAD_DESTRUCTOR()
+PROXY_CONSTMETHOD0(std::vector<RtpTransportInterface*>, GetTransports)
+protected:
+RtpTransportControllerAdapter* GetInternal() override {
+ return internal();
+}
+END_PROXY_MAP()
+
+// static
+std::unique_ptr<RtpTransportControllerInterface>
+RtpTransportControllerAdapter::CreateProxied(
+ const cricket::MediaConfig& config,
+ cricket::ChannelManager* channel_manager,
+ webrtc::RtcEventLog* event_log,
+ rtc::Thread* signaling_thread,
+ rtc::Thread* worker_thread) {
+ std::unique_ptr<RtpTransportControllerAdapter> wrapped(
+ new RtpTransportControllerAdapter(config, channel_manager, event_log,
+ signaling_thread, worker_thread));
+ return RtpTransportControllerProxyWithInternal<
+ RtpTransportControllerAdapter>::Create(signaling_thread, worker_thread,
+ std::move(wrapped));
+}
+
+RtpTransportControllerAdapter::~RtpTransportControllerAdapter() {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ if (!transport_proxies_.empty()) {
+ LOG(LS_ERROR)
+ << "Destroying RtpTransportControllerAdapter while RtpTransports "
+ "are still using it; this is unsafe.";
+ }
+ if (voice_channel_) {
+ // This would mean audio RTP senders/receivers that are using us haven't
+ // been destroyed. This isn't safe (see error log above).
+ DestroyVoiceChannel();
+ }
+ if (voice_channel_) {
+ // This would mean video RTP senders/receivers that are using us haven't
+ // been destroyed. This isn't safe (see error log above).
+ DestroyVideoChannel();
+ }
+}
+
+RTCErrorOr<std::unique_ptr<RtpTransportInterface>>
+RtpTransportControllerAdapter::CreateProxiedRtpTransport(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp) {
+ auto result =
+ RtpTransportAdapter::CreateProxied(rtcp_parameters, rtp, rtcp, this);
+ if (result.ok()) {
+ transport_proxies_.push_back(result.value().get());
+ transport_proxies_.back()->GetInternal()->SignalDestroyed.connect(
+ this, &RtpTransportControllerAdapter::OnRtpTransportDestroyed);
+ }
+ return result;
+}
+
+RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>>
+RtpTransportControllerAdapter::CreateProxiedRtpSender(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport_proxy) {
+ RTC_DCHECK(transport_proxy);
+ RTC_DCHECK(std::find(transport_proxies_.begin(), transport_proxies_.end(),
+ transport_proxy) != transport_proxies_.end());
+ std::unique_ptr<OrtcRtpSenderAdapter> new_sender(
+ new OrtcRtpSenderAdapter(kind, transport_proxy, this));
+ RTCError err;
+ switch (kind) {
+ case cricket::MEDIA_TYPE_AUDIO:
+ err = AttachAudioSender(new_sender.get(), transport_proxy->GetInternal());
+ break;
+ case cricket::MEDIA_TYPE_VIDEO:
+ err = AttachVideoSender(new_sender.get(), transport_proxy->GetInternal());
+ break;
+ case cricket::MEDIA_TYPE_DATA:
+ RTC_NOTREACHED();
+ }
+ if (!err.ok()) {
+ return std::move(err);
+ }
+
+ return OrtcRtpSenderAdapter::CreateProxy(std::move(new_sender));
+}
+
+RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>
+RtpTransportControllerAdapter::CreateProxiedRtpReceiver(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport_proxy) {
+ RTC_DCHECK(transport_proxy);
+ RTC_DCHECK(std::find(transport_proxies_.begin(), transport_proxies_.end(),
+ transport_proxy) != transport_proxies_.end());
+ std::unique_ptr<OrtcRtpReceiverAdapter> new_receiver(
+ new OrtcRtpReceiverAdapter(kind, transport_proxy, this));
+ RTCError err;
+ switch (kind) {
+ case cricket::MEDIA_TYPE_AUDIO:
+ err = AttachAudioReceiver(new_receiver.get(),
+ transport_proxy->GetInternal());
+ break;
+ case cricket::MEDIA_TYPE_VIDEO:
+ err = AttachVideoReceiver(new_receiver.get(),
+ transport_proxy->GetInternal());
+ break;
+ case cricket::MEDIA_TYPE_DATA:
+ RTC_NOTREACHED();
+ }
+ if (!err.ok()) {
+ return std::move(err);
+ }
+
+ return OrtcRtpReceiverAdapter::CreateProxy(std::move(new_receiver));
+}
+
+std::vector<RtpTransportInterface*>
+RtpTransportControllerAdapter::GetTransports() const {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ return transport_proxies_;
+}
+
+RTCError RtpTransportControllerAdapter::SetRtcpParameters(
+ const RtcpParameters& parameters,
+ RtpTransportInterface* inner_transport) {
+ do {
+ if (inner_transport == inner_audio_transport_) {
+ CopyRtcpParametersToDescriptions(parameters, &local_audio_description_,
+ &remote_audio_description_);
+ if (!voice_channel_->SetLocalContent(&local_audio_description_,
+ cricket::CA_OFFER, nullptr)) {
+ break;
+ }
+ if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
+ cricket::CA_ANSWER, nullptr)) {
+ break;
+ }
+ } else if (inner_transport == inner_video_transport_) {
+ CopyRtcpParametersToDescriptions(parameters, &local_video_description_,
+ &remote_video_description_);
+ if (!video_channel_->SetLocalContent(&local_video_description_,
+ cricket::CA_OFFER, nullptr)) {
+ break;
+ }
+ if (!video_channel_->SetRemoteContent(&remote_video_description_,
+ cricket::CA_ANSWER, nullptr)) {
+ break;
+ }
+ }
+ return RTCError::OK();
+ } while (false);
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply new RTCP parameters.");
+}
+
+RTCError RtpTransportControllerAdapter::ValidateAndApplyAudioSenderParameters(
+ const RtpParameters& parameters,
+ uint32_t* primary_ssrc) {
+ RTC_DCHECK(voice_channel_);
+ RTC_DCHECK(have_audio_sender_);
+
+ auto codecs_result = ToCricketCodecs<cricket::AudioCodec>(parameters.codecs);
+ if (!codecs_result.ok()) {
+ return codecs_result.MoveError();
+ }
+
+ auto extensions_result =
+ ToCricketRtpHeaderExtensions(parameters.header_extensions);
+ if (!extensions_result.ok()) {
+ return extensions_result.MoveError();
+ }
+
+ auto stream_params_result = MakeSendStreamParamsVec(
+ parameters.encodings, inner_audio_transport_->GetRtcpParameters().cname,
+ local_audio_description_);
+ if (!stream_params_result.ok()) {
+ return stream_params_result.MoveError();
+ }
+
+ // Check that audio/video sender aren't using the same IDs to refer to
+ // different things, if they share the same transport.
+ if (inner_audio_transport_ == inner_video_transport_) {
+ RTCError err = CheckForIdConflicts(
+ codecs_result.value(), extensions_result.value(),
+ stream_params_result.value(), remote_video_description_.codecs(),
+ remote_video_description_.rtp_header_extensions(),
+ local_video_description_.streams());
+ if (!err.ok()) {
+ return err;
+ }
+ }
+
+ cricket::RtpTransceiverDirection local_direction =
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(
+ local_audio_description_.direction());
+ int bandwidth = cricket::kAutoBandwidth;
+ if (parameters.encodings.size() == 1u) {
+ if (parameters.encodings[0].max_bitrate_bps) {
+ bandwidth = *parameters.encodings[0].max_bitrate_bps;
+ }
+ local_direction.send = parameters.encodings[0].active;
+ } else {
+ local_direction.send = false;
+ }
+ if (primary_ssrc && !stream_params_result.value().empty()) {
+ *primary_ssrc = stream_params_result.value()[0].first_ssrc();
+ }
+
+ // Validation is done, so we can attempt applying the descriptions. Sent
+ // codecs and header extensions go in remote description, streams go in
+ // local.
+ //
+ // If there are no codecs or encodings, just leave the previous set of
+ // codecs. The media engine doesn't like an empty set of codecs.
+ if (local_audio_description_.streams().empty() &&
+ remote_audio_description_.codecs().empty()) {
+ } else {
+ remote_audio_description_.set_codecs(codecs_result.MoveValue());
+ }
+ remote_audio_description_.set_rtp_header_extensions(
+ extensions_result.MoveValue());
+ remote_audio_description_.set_bandwidth(bandwidth);
+ local_audio_description_.mutable_streams() = stream_params_result.MoveValue();
+ // Direction set based on encoding "active" flag.
+ local_audio_description_.set_direction(
+ local_direction.ToMediaContentDirection());
+ remote_audio_description_.set_direction(
+ local_direction.Reversed().ToMediaContentDirection());
+
+ // Set remote content first, to ensure the stream is created with the correct
+ // codec.
+ if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
+ cricket::CA_OFFER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply remote parameters to media channel.");
+ }
+ if (!voice_channel_->SetLocalContent(&local_audio_description_,
+ cricket::CA_ANSWER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply local parameters to media channel.");
+ }
+ return RTCError::OK();
+}
+
+RTCError RtpTransportControllerAdapter::ValidateAndApplyVideoSenderParameters(
+ const RtpParameters& parameters,
+ uint32_t* primary_ssrc) {
+ RTC_DCHECK(video_channel_);
+ RTC_DCHECK(have_video_sender_);
+
+ auto codecs_result = ToCricketCodecs<cricket::VideoCodec>(parameters.codecs);
+ if (!codecs_result.ok()) {
+ return codecs_result.MoveError();
+ }
+
+ auto extensions_result =
+ ToCricketRtpHeaderExtensions(parameters.header_extensions);
+ if (!extensions_result.ok()) {
+ return extensions_result.MoveError();
+ }
+
+ auto stream_params_result = MakeSendStreamParamsVec(
+ parameters.encodings, inner_video_transport_->GetRtcpParameters().cname,
+ local_video_description_);
+ if (!stream_params_result.ok()) {
+ return stream_params_result.MoveError();
+ }
+
+ // Check that audio/video sender aren't using the same IDs to refer to
+ // different things, if they share the same transport.
+ if (inner_audio_transport_ == inner_video_transport_) {
+ RTCError err = CheckForIdConflicts(
+ codecs_result.value(), extensions_result.value(),
+ stream_params_result.value(), remote_audio_description_.codecs(),
+ remote_audio_description_.rtp_header_extensions(),
+ local_audio_description_.streams());
+ if (!err.ok()) {
+ return err;
+ }
+ }
+
+ cricket::RtpTransceiverDirection local_direction =
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(
+ local_video_description_.direction());
+ int bandwidth = cricket::kAutoBandwidth;
+ if (parameters.encodings.size() == 1u) {
+ if (parameters.encodings[0].max_bitrate_bps) {
+ bandwidth = *parameters.encodings[0].max_bitrate_bps;
+ }
+ local_direction.send = parameters.encodings[0].active;
+ } else {
+ local_direction.send = false;
+ }
+ if (primary_ssrc && !stream_params_result.value().empty()) {
+ *primary_ssrc = stream_params_result.value()[0].first_ssrc();
+ }
+
+ // Validation is done, so we can attempt applying the descriptions. Sent
+ // codecs and header extensions go in remote description, streams go in
+ // local.
+ //
+ // If there are no codecs or encodings, just leave the previous set of
+ // codecs. The media engine doesn't like an empty set of codecs.
+ if (local_video_description_.streams().empty() &&
+ remote_video_description_.codecs().empty()) {
+ } else {
+ remote_video_description_.set_codecs(codecs_result.MoveValue());
+ }
+ remote_video_description_.set_rtp_header_extensions(
+ extensions_result.MoveValue());
+ remote_video_description_.set_bandwidth(bandwidth);
+ local_video_description_.mutable_streams() = stream_params_result.MoveValue();
+ // Direction set based on encoding "active" flag.
+ local_video_description_.set_direction(
+ local_direction.ToMediaContentDirection());
+ remote_video_description_.set_direction(
+ local_direction.Reversed().ToMediaContentDirection());
+
+ // Set remote content first, to ensure the stream is created with the correct
+ // codec.
+ if (!video_channel_->SetRemoteContent(&remote_video_description_,
+ cricket::CA_OFFER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply remote parameters to media channel.");
+ }
+ if (!video_channel_->SetLocalContent(&local_video_description_,
+ cricket::CA_ANSWER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply local parameters to media channel.");
+ }
+ return RTCError::OK();
+}
+
+RTCError RtpTransportControllerAdapter::ValidateAndApplyAudioReceiverParameters(
+ const RtpParameters& parameters) {
+ RTC_DCHECK(voice_channel_);
+ RTC_DCHECK(have_audio_receiver_);
+
+ auto codecs_result = ToCricketCodecs<cricket::AudioCodec>(parameters.codecs);
+ if (!codecs_result.ok()) {
+ return codecs_result.MoveError();
+ }
+
+ auto extensions_result =
+ ToCricketRtpHeaderExtensions(parameters.header_extensions);
+ if (!extensions_result.ok()) {
+ return extensions_result.MoveError();
+ }
+
+ cricket::RtpTransceiverDirection local_direction =
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(
+ local_audio_description_.direction());
+ auto stream_params_result = ToCricketStreamParamsVec(parameters.encodings);
+ if (!stream_params_result.ok()) {
+ return stream_params_result.MoveError();
+ }
+
+ // Check that audio/video receive aren't using the same IDs to refer to
+ // different things, if they share the same transport.
+ if (inner_audio_transport_ == inner_video_transport_) {
+ RTCError err = CheckForIdConflicts(
+ codecs_result.value(), extensions_result.value(),
+ stream_params_result.value(), local_video_description_.codecs(),
+ local_video_description_.rtp_header_extensions(),
+ remote_video_description_.streams());
+ if (!err.ok()) {
+ return err;
+ }
+ }
+
+ local_direction.recv =
+ !parameters.encodings.empty() && parameters.encodings[0].active;
+
+ // Validation is done, so we can attempt applying the descriptions. Received
+ // codecs and header extensions go in local description, streams go in
+ // remote.
+ //
+ // If there are no codecs or encodings, just leave the previous set of
+ // codecs. The media engine doesn't like an empty set of codecs.
+ if (remote_audio_description_.streams().empty() &&
+ local_audio_description_.codecs().empty()) {
+ } else {
+ local_audio_description_.set_codecs(codecs_result.MoveValue());
+ }
+ local_audio_description_.set_rtp_header_extensions(
+ extensions_result.MoveValue());
+ remote_audio_description_.mutable_streams() =
+ stream_params_result.MoveValue();
+ // Direction set based on encoding "active" flag.
+ local_audio_description_.set_direction(
+ local_direction.ToMediaContentDirection());
+ remote_audio_description_.set_direction(
+ local_direction.Reversed().ToMediaContentDirection());
+
+ if (!voice_channel_->SetLocalContent(&local_audio_description_,
+ cricket::CA_OFFER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply local parameters to media channel.");
+ }
+ if (!voice_channel_->SetRemoteContent(&remote_audio_description_,
+ cricket::CA_ANSWER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply remote parameters to media channel.");
+ }
+ return RTCError::OK();
+}
+
+RTCError RtpTransportControllerAdapter::ValidateAndApplyVideoReceiverParameters(
+ const RtpParameters& parameters) {
+ RTC_DCHECK(video_channel_);
+ RTC_DCHECK(have_video_receiver_);
+
+ auto codecs_result = ToCricketCodecs<cricket::VideoCodec>(parameters.codecs);
+ if (!codecs_result.ok()) {
+ return codecs_result.MoveError();
+ }
+
+ auto extensions_result =
+ ToCricketRtpHeaderExtensions(parameters.header_extensions);
+ if (!extensions_result.ok()) {
+ return extensions_result.MoveError();
+ }
+
+ cricket::RtpTransceiverDirection local_direction =
+ cricket::RtpTransceiverDirection::FromMediaContentDirection(
+ local_video_description_.direction());
+ int bandwidth = cricket::kAutoBandwidth;
+ auto stream_params_result = ToCricketStreamParamsVec(parameters.encodings);
+ if (!stream_params_result.ok()) {
+ return stream_params_result.MoveError();
+ }
+
+ // Check that audio/video receiver aren't using the same IDs to refer to
+ // different things, if they share the same transport.
+ if (inner_audio_transport_ == inner_video_transport_) {
+ RTCError err = CheckForIdConflicts(
+ codecs_result.value(), extensions_result.value(),
+ stream_params_result.value(), local_audio_description_.codecs(),
+ local_audio_description_.rtp_header_extensions(),
+ remote_audio_description_.streams());
+ if (!err.ok()) {
+ return err;
+ }
+ }
+
+ local_direction.recv =
+ !parameters.encodings.empty() && parameters.encodings[0].active;
+
+ // Validation is done, so we can attempt applying the descriptions. Received
+ // codecs and header extensions go in local description, streams go in
+ // remote.
+ //
+ // If there are no codecs or encodings, just leave the previous set of
+ // codecs. The media engine doesn't like an empty set of codecs.
+ if (remote_video_description_.streams().empty() &&
+ local_video_description_.codecs().empty()) {
+ } else {
+ local_video_description_.set_codecs(codecs_result.MoveValue());
+ }
+ local_video_description_.set_rtp_header_extensions(
+ extensions_result.MoveValue());
+ local_video_description_.set_bandwidth(bandwidth);
+ remote_video_description_.mutable_streams() =
+ stream_params_result.MoveValue();
+ // Direction set based on encoding "active" flag.
+ local_video_description_.set_direction(
+ local_direction.ToMediaContentDirection());
+ remote_video_description_.set_direction(
+ local_direction.Reversed().ToMediaContentDirection());
+
+ if (!video_channel_->SetLocalContent(&local_video_description_,
+ cricket::CA_OFFER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply local parameters to media channel.");
+ }
+ if (!video_channel_->SetRemoteContent(&remote_video_description_,
+ cricket::CA_ANSWER, nullptr)) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
+ "Failed to apply remote parameters to media channel.");
+ }
+ return RTCError::OK();
+}
+
+RtpTransportControllerAdapter::RtpTransportControllerAdapter(
+ const cricket::MediaConfig& config,
+ cricket::ChannelManager* channel_manager,
+ webrtc::RtcEventLog* event_log,
+ rtc::Thread* signaling_thread,
+ rtc::Thread* worker_thread)
+ : signaling_thread_(signaling_thread),
+ worker_thread_(worker_thread),
+ media_controller_(MediaControllerInterface::Create(config,
+ worker_thread,
+ channel_manager,
+ event_log)) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ RTC_DCHECK(channel_manager);
+ // MediaControllerInterface::Create should never fail.
+ RTC_DCHECK(media_controller_);
+ // Add "dummy" codecs to the descriptions, because the media engines
+ // currently reject empty lists of codecs. Note that these codecs will never
+ // actually be used, because when parameters are set, the dummy codecs will
+ // be replaced by actual codecs before any send/receive streams are created.
+ static const cricket::AudioCodec dummy_audio(0, cricket::kPcmuCodecName, 8000,
+ 0, 1);
+ static const cricket::VideoCodec dummy_video(96, cricket::kVp8CodecName);
+ local_audio_description_.AddCodec(dummy_audio);
+ remote_audio_description_.AddCodec(dummy_audio);
+ local_video_description_.AddCodec(dummy_video);
+ remote_video_description_.AddCodec(dummy_video);
+}
+
+RTCError RtpTransportControllerAdapter::AttachAudioSender(
+ OrtcRtpSenderAdapter* sender,
+ RtpTransportInterface* inner_transport) {
+ if (have_audio_sender_) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using two audio RtpSenders with the same "
+ "RtpTransportControllerAdapter is not currently "
+ "supported.");
+ }
+ if (inner_audio_transport_ && inner_audio_transport_ != inner_transport) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using different transports for the audio "
+ "RtpSender and RtpReceiver is not currently "
+ "supported.");
+ }
+ // If setting new transport, extract its RTCP parameters and create voice
+ // channel.
+ if (!inner_audio_transport_) {
+ CopyRtcpParametersToDescriptions(inner_transport->GetRtcpParameters(),
+ &local_audio_description_,
+ &remote_audio_description_);
+ inner_audio_transport_ = inner_transport;
+ CreateVoiceChannel();
+ }
+ have_audio_sender_ = true;
+ sender->SignalDestroyed.connect(
+ this, &RtpTransportControllerAdapter::OnAudioSenderDestroyed);
+ return RTCError::OK();
+}
+
+RTCError RtpTransportControllerAdapter::AttachVideoSender(
+ OrtcRtpSenderAdapter* sender,
+ RtpTransportInterface* inner_transport) {
+ if (have_video_sender_) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using two video RtpSenders with the same "
+ "RtpTransportControllerAdapter is not currently "
+ "supported.");
+ }
+ if (inner_video_transport_ && inner_video_transport_ != inner_transport) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using different transports for the video "
+ "RtpSender and RtpReceiver is not currently "
+ "supported.");
+ }
+ // If setting new transport, extract its RTCP parameters and create video
+ // channel.
+ if (!inner_video_transport_) {
+ CopyRtcpParametersToDescriptions(inner_transport->GetRtcpParameters(),
+ &local_video_description_,
+ &remote_video_description_);
+ inner_video_transport_ = inner_transport;
+ CreateVideoChannel();
+ }
+ have_video_sender_ = true;
+ sender->SignalDestroyed.connect(
+ this, &RtpTransportControllerAdapter::OnVideoSenderDestroyed);
+ return RTCError::OK();
+}
+
+RTCError RtpTransportControllerAdapter::AttachAudioReceiver(
+ OrtcRtpReceiverAdapter* receiver,
+ RtpTransportInterface* inner_transport) {
+ if (have_audio_receiver_) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using two audio RtpReceivers with the same "
+ "RtpTransportControllerAdapter is not currently "
+ "supported.");
+ }
+ if (inner_audio_transport_ && inner_audio_transport_ != inner_transport) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using different transports for the audio "
+ "RtpReceiver and RtpReceiver is not currently "
+ "supported.");
+ }
+ // If setting new transport, extract its RTCP parameters and create voice
+ // channel.
+ if (!inner_audio_transport_) {
+ CopyRtcpParametersToDescriptions(inner_transport->GetRtcpParameters(),
+ &local_audio_description_,
+ &remote_audio_description_);
+ inner_audio_transport_ = inner_transport;
+ CreateVoiceChannel();
+ }
+ have_audio_receiver_ = true;
+ receiver->SignalDestroyed.connect(
+ this, &RtpTransportControllerAdapter::OnAudioReceiverDestroyed);
+ return RTCError::OK();
+}
+
+RTCError RtpTransportControllerAdapter::AttachVideoReceiver(
+ OrtcRtpReceiverAdapter* receiver,
+ RtpTransportInterface* inner_transport) {
+ if (have_video_receiver_) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using two video RtpReceivers with the same "
+ "RtpTransportControllerAdapter is not currently "
+ "supported.");
+ }
+ if (inner_video_transport_ && inner_video_transport_ != inner_transport) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
+ "Using different transports for the video "
+ "RtpReceiver and RtpReceiver is not currently "
+ "supported.");
+ }
+ // If setting new transport, extract its RTCP parameters and create video
+ // channel.
+ if (!inner_video_transport_) {
+ CopyRtcpParametersToDescriptions(inner_transport->GetRtcpParameters(),
+ &local_video_description_,
+ &remote_video_description_);
+ inner_video_transport_ = inner_transport;
+ CreateVideoChannel();
+ }
+ have_video_receiver_ = true;
+ receiver->SignalDestroyed.connect(
+ this, &RtpTransportControllerAdapter::OnVideoReceiverDestroyed);
+ return RTCError::OK();
+}
+
+void RtpTransportControllerAdapter::OnRtpTransportDestroyed(
+ RtpTransportAdapter* transport) {
+ RTC_DCHECK_RUN_ON(signaling_thread_);
+ auto it = std::find_if(transport_proxies_.begin(), transport_proxies_.end(),
+ [transport](RtpTransportInterface* proxy) {
+ return proxy->GetInternal() == transport;
+ });
+ if (it == transport_proxies_.end()) {
+ RTC_NOTREACHED();
+ return;
+ }
+ transport_proxies_.erase(it);
+}
+
+void RtpTransportControllerAdapter::OnAudioSenderDestroyed() {
+ if (!have_audio_sender_) {
+ RTC_NOTREACHED();
+ return;
+ }
+ // Empty parameters should result in sending being stopped.
+ RTCError err =
+ ValidateAndApplyAudioSenderParameters(RtpParameters(), nullptr);
+ RTC_DCHECK(err.ok());
+ have_audio_sender_ = false;
+ if (!have_audio_receiver_) {
+ DestroyVoiceChannel();
+ }
+}
+
+void RtpTransportControllerAdapter::OnVideoSenderDestroyed() {
+ if (!have_video_sender_) {
+ RTC_NOTREACHED();
+ return;
+ }
+ // Empty parameters should result in sending being stopped.
+ RTCError err =
+ ValidateAndApplyVideoSenderParameters(RtpParameters(), nullptr);
+ RTC_DCHECK(err.ok());
+ have_video_sender_ = false;
+ if (!have_video_receiver_) {
+ DestroyVideoChannel();
+ }
+}
+
+void RtpTransportControllerAdapter::OnAudioReceiverDestroyed() {
+ if (!have_audio_receiver_) {
+ RTC_NOTREACHED();
+ return;
+ }
+ // Empty parameters should result in receiving being stopped.
+ RTCError err = ValidateAndApplyAudioReceiverParameters(RtpParameters());
+ RTC_DCHECK(err.ok());
+ have_audio_receiver_ = false;
+ if (!have_audio_sender_) {
+ DestroyVoiceChannel();
+ }
+}
+
+void RtpTransportControllerAdapter::OnVideoReceiverDestroyed() {
+ if (!have_video_receiver_) {
+ RTC_NOTREACHED();
+ return;
+ }
+ // Empty parameters should result in receiving being stopped.
+ RTCError err = ValidateAndApplyVideoReceiverParameters(RtpParameters());
+ RTC_DCHECK(err.ok());
+ have_video_receiver_ = false;
+ if (!have_video_sender_) {
+ DestroyVideoChannel();
+ }
+}
+
+void RtpTransportControllerAdapter::CreateVoiceChannel() {
+ voice_channel_ = media_controller_->channel_manager()->CreateVoiceChannel(
+ media_controller_.get(),
+ inner_audio_transport_->GetRtpPacketTransport()->GetInternal(),
+ inner_audio_transport_->GetRtcpPacketTransport()
+ ? inner_audio_transport_->GetRtcpPacketTransport()->GetInternal()
+ : nullptr,
+ signaling_thread_, "audio", false, cricket::AudioOptions());
+ RTC_DCHECK(voice_channel_);
+ voice_channel_->Enable(true);
+}
+
+void RtpTransportControllerAdapter::CreateVideoChannel() {
+ video_channel_ = media_controller_->channel_manager()->CreateVideoChannel(
+ media_controller_.get(),
+ inner_video_transport_->GetRtpPacketTransport()->GetInternal(),
+ inner_video_transport_->GetRtcpPacketTransport()
+ ? inner_video_transport_->GetRtcpPacketTransport()->GetInternal()
+ : nullptr,
+ signaling_thread_, "video", false, cricket::VideoOptions());
+ RTC_DCHECK(video_channel_);
+ video_channel_->Enable(true);
+}
+
+void RtpTransportControllerAdapter::DestroyVoiceChannel() {
+ RTC_DCHECK(voice_channel_);
+ media_controller_->channel_manager()->DestroyVoiceChannel(voice_channel_);
+ voice_channel_ = nullptr;
+ inner_audio_transport_ = nullptr;
+}
+
+void RtpTransportControllerAdapter::DestroyVideoChannel() {
+ RTC_DCHECK(video_channel_);
+ media_controller_->channel_manager()->DestroyVideoChannel(video_channel_);
+ video_channel_ = nullptr;
+ inner_video_transport_ = nullptr;
+}
+
+void RtpTransportControllerAdapter::CopyRtcpParametersToDescriptions(
+ const RtcpParameters& params,
+ cricket::MediaContentDescription* local,
+ cricket::MediaContentDescription* remote) {
+ local->set_rtcp_mux(params.mux);
+ remote->set_rtcp_mux(params.mux);
+ local->set_rtcp_reduced_size(params.reduced_size);
+ remote->set_rtcp_reduced_size(params.reduced_size);
+ for (cricket::StreamParams& stream_params : local->mutable_streams()) {
+ stream_params.cname = params.cname;
+ }
+}
+
+uint32_t RtpTransportControllerAdapter::GenerateUnusedSsrc(
+ std::set<uint32_t>* new_ssrcs) const {
+ uint32_t ssrc;
+ do {
+ ssrc = rtc::CreateRandomNonZeroId();
+ } while (
+ cricket::GetStreamBySsrc(local_audio_description_.streams(), ssrc) ||
+ cricket::GetStreamBySsrc(remote_audio_description_.streams(), ssrc) ||
+ cricket::GetStreamBySsrc(local_video_description_.streams(), ssrc) ||
+ cricket::GetStreamBySsrc(remote_video_description_.streams(), ssrc) ||
+ !new_ssrcs->insert(ssrc).second);
+ return ssrc;
+}
+
+RTCErrorOr<cricket::StreamParamsVec>
+RtpTransportControllerAdapter::MakeSendStreamParamsVec(
+ std::vector<RtpEncodingParameters> encodings,
+ const std::string& cname,
+ const cricket::MediaContentDescription& description) const {
+ if (encodings.size() > 1u) {
+ LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::UNSUPPORTED_PARAMETER,
+ "ORTC API implementation doesn't currently "
+ "support simulcast or layered encodings.");
+ } else if (encodings.empty()) {
+ return cricket::StreamParamsVec();
+ }
+ RtpEncodingParameters& encoding = encodings[0];
+ std::set<uint32_t> new_ssrcs;
+ if (encoding.ssrc) {
+ new_ssrcs.insert(*encoding.ssrc);
+ }
+ if (encoding.rtx && encoding.rtx->ssrc) {
+ new_ssrcs.insert(*encoding.rtx->ssrc);
+ }
+ // May need to fill missing SSRCs with generated ones.
+ if (!encoding.ssrc) {
+ if (!description.streams().empty()) {
+ encoding.ssrc.emplace(description.streams()[0].first_ssrc());
+ } else {
+ encoding.ssrc.emplace(GenerateUnusedSsrc(&new_ssrcs));
+ }
+ }
+ if (encoding.rtx && !encoding.rtx->ssrc) {
+ uint32_t existing_rtx_ssrc;
+ if (!description.streams().empty() &&
+ description.streams()[0].GetFidSsrc(
+ description.streams()[0].first_ssrc(), &existing_rtx_ssrc)) {
+ encoding.rtx->ssrc.emplace(existing_rtx_ssrc);
+ } else {
+ encoding.rtx->ssrc.emplace(GenerateUnusedSsrc(&new_ssrcs));
+ }
+ }
+
+ auto result = ToCricketStreamParamsVec(encodings);
+ if (!result.ok()) {
+ return result.MoveError();
+ }
+ // If conversion was successful, there should be one StreamParams.
+ RTC_DCHECK_EQ(1u, result.value().size());
+ result.value()[0].cname = cname;
+ return result;
+}
+
+} // namespace webrtc
diff --git a/ortc/rtptransportcontrolleradapter.h b/ortc/rtptransportcontrolleradapter.h
new file mode 100644
index 0000000..4e02b95
--- /dev/null
+++ b/ortc/rtptransportcontrolleradapter.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_RTPTRANSPORTCONTROLLERADAPTER_H_
+#define WEBRTC_ORTC_RTPTRANSPORTCONTROLLERADAPTER_H_
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/call/call.h"
+#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
+#include "webrtc/api/ortc/ortcrtpreceiverinterface.h"
+#include "webrtc/api/ortc/ortcrtpsenderinterface.h"
+#include "webrtc/api/ortc/rtptransportcontrollerinterface.h"
+#include "webrtc/api/ortc/rtptransportinterface.h"
+#include "webrtc/pc/channelmanager.h"
+#include "webrtc/pc/mediacontroller.h"
+#include "webrtc/media/base/mediachannel.h" // For MediaConfig.
+
+namespace webrtc {
+
+class RtpTransportAdapter;
+class OrtcRtpSenderAdapter;
+class OrtcRtpReceiverAdapter;
+
+// Implementation of RtpTransportControllerInterface. Wraps a MediaController,
+// a VoiceChannel and VideoChannel, and maintains a list of dependent RTP
+// transports.
+//
+// When used along with an RtpSenderAdapter or RtpReceiverAdapter, the
+// sender/receiver passes its parameters along to this class, which turns them
+// into cricket:: media descriptions (the interface used by BaseChannel).
+//
+// Due to the fact that BaseChannel has different subclasses for audio/video,
+// the actual BaseChannel object is not created until an RtpSender/RtpReceiver
+// needs them.
+//
+// All methods should be called on the signaling thread.
+//
+// TODO(deadbeef): When BaseChannel is split apart into separate
+// "RtpSender"/"RtpTransceiver"/"RtpSender"/"RtpReceiver" objects, this adapter
+// object can be replaced by a "real" one.
+class RtpTransportControllerAdapter : public RtpTransportControllerInterface,
+ public sigslot::has_slots<> {
+ public:
+ // Creates a proxy that will call "public interface" methods on the correct
+ // thread.
+ //
+ // Doesn't take ownership of any objects passed in.
+ //
+ // |channel_manager| must not be null.
+ static std::unique_ptr<RtpTransportControllerInterface> CreateProxied(
+ const cricket::MediaConfig& config,
+ cricket::ChannelManager* channel_manager,
+ webrtc::RtcEventLog* event_log,
+ rtc::Thread* signaling_thread,
+ rtc::Thread* worker_thread);
+
+ ~RtpTransportControllerAdapter() override;
+
+ // RtpTransportControllerInterface implementation.
+ std::vector<RtpTransportInterface*> GetTransports() const override;
+
+ // These methods are used by OrtcFactory to create RtpTransports, RtpSenders
+ // and RtpReceivers using this controller. Called "CreateProxied" because
+ // these methods return proxies that will safely call methods on the correct
+ // thread.
+ RTCErrorOr<std::unique_ptr<RtpTransportInterface>> CreateProxiedRtpTransport(
+ const RtcpParameters& rtcp_parameters,
+ PacketTransportInterface* rtp,
+ PacketTransportInterface* rtcp);
+ // |transport_proxy| needs to be a proxy to a transport because the
+ // application may call GetTransport() on the returned sender or receiver,
+ // and expects it to return a thread-safe transport proxy.
+ RTCErrorOr<std::unique_ptr<OrtcRtpSenderInterface>> CreateProxiedRtpSender(
+ cricket::MediaType kind,
+ RtpTransportInterface* transport_proxy);
+ RTCErrorOr<std::unique_ptr<OrtcRtpReceiverInterface>>
+ CreateProxiedRtpReceiver(cricket::MediaType kind,
+ RtpTransportInterface* transport_proxy);
+
+ // Methods used internally by other "adapter" classes.
+ rtc::Thread* signaling_thread() const { return signaling_thread_; }
+ rtc::Thread* worker_thread() const { return worker_thread_; }
+
+ RTCError SetRtcpParameters(const RtcpParameters& parameters,
+ RtpTransportInterface* inner_transport);
+
+ cricket::VoiceChannel* voice_channel() { return voice_channel_; }
+ cricket::VideoChannel* video_channel() { return video_channel_; }
+
+ // |primary_ssrc| out parameter is filled with either
+ // |parameters.encodings[0].ssrc|, or a generated SSRC if that's left unset.
+ RTCError ValidateAndApplyAudioSenderParameters(
+ const RtpParameters& parameters,
+ uint32_t* primary_ssrc);
+ RTCError ValidateAndApplyVideoSenderParameters(
+ const RtpParameters& parameters,
+ uint32_t* primary_ssrc);
+ RTCError ValidateAndApplyAudioReceiverParameters(
+ const RtpParameters& parameters);
+ RTCError ValidateAndApplyVideoReceiverParameters(
+ const RtpParameters& parameters);
+
+ protected:
+ RtpTransportControllerAdapter* GetInternal() override { return this; }
+
+ private:
+ // Only expected to be called by RtpTransportControllerAdapter::CreateProxied.
+ RtpTransportControllerAdapter(const cricket::MediaConfig& config,
+ cricket::ChannelManager* channel_manager,
+ webrtc::RtcEventLog* event_log,
+ rtc::Thread* signaling_thread,
+ rtc::Thread* worker_thread);
+
+ // These return an error if another of the same type of object is already
+ // attached, or if |transport_proxy| can't be used with the sender/receiver
+ // due to the limitation that the sender/receiver of the same media type must
+ // use the same transport.
+ RTCError AttachAudioSender(OrtcRtpSenderAdapter* sender,
+ RtpTransportInterface* inner_transport);
+ RTCError AttachVideoSender(OrtcRtpSenderAdapter* sender,
+ RtpTransportInterface* inner_transport);
+ RTCError AttachAudioReceiver(OrtcRtpReceiverAdapter* receiver,
+ RtpTransportInterface* inner_transport);
+ RTCError AttachVideoReceiver(OrtcRtpReceiverAdapter* receiver,
+ RtpTransportInterface* inner_transport);
+
+ void OnRtpTransportDestroyed(RtpTransportAdapter* transport);
+
+ void OnAudioSenderDestroyed();
+ void OnVideoSenderDestroyed();
+ void OnAudioReceiverDestroyed();
+ void OnVideoReceiverDestroyed();
+
+ void CreateVoiceChannel();
+ void CreateVideoChannel();
+ void DestroyVoiceChannel();
+ void DestroyVideoChannel();
+
+ void CopyRtcpParametersToDescriptions(
+ const RtcpParameters& params,
+ cricket::MediaContentDescription* local,
+ cricket::MediaContentDescription* remote);
+
+ // Helper function to generate an SSRC that doesn't match one in any of the
+ // "content description" structs, or in |new_ssrcs| (which is needed since
+ // multiple SSRCs may be generated in one go).
+ uint32_t GenerateUnusedSsrc(std::set<uint32_t>* new_ssrcs) const;
+
+ // |description| is the matching description where existing SSRCs can be
+ // found.
+ //
+ // This is a member function because it may need to generate SSRCs that don't
+ // match existing ones, which is more than ToStreamParamsVec does.
+ RTCErrorOr<cricket::StreamParamsVec> MakeSendStreamParamsVec(
+ std::vector<RtpEncodingParameters> encodings,
+ const std::string& cname,
+ const cricket::MediaContentDescription& description) const;
+
+ rtc::Thread* signaling_thread_;
+ rtc::Thread* worker_thread_;
+ // |transport_proxies_| and |inner_audio_transport_|/|inner_audio_transport_|
+ // are somewhat redundant, but the latter are only set when
+ // RtpSenders/RtpReceivers are attached to the transport.
+ std::vector<RtpTransportInterface*> transport_proxies_;
+ RtpTransportInterface* inner_audio_transport_ = nullptr;
+ RtpTransportInterface* inner_video_transport_ = nullptr;
+ std::unique_ptr<MediaControllerInterface> media_controller_;
+
+ // BaseChannel takes content descriptions as input, so we store them here
+ // such that they can be updated when a new RtpSenderAdapter/
+ // RtpReceiverAdapter attaches itself.
+ cricket::AudioContentDescription local_audio_description_;
+ cricket::AudioContentDescription remote_audio_description_;
+ cricket::VideoContentDescription local_video_description_;
+ cricket::VideoContentDescription remote_video_description_;
+ cricket::VoiceChannel* voice_channel_ = nullptr;
+ cricket::VideoChannel* video_channel_ = nullptr;
+ bool have_audio_sender_ = false;
+ bool have_video_sender_ = false;
+ bool have_audio_receiver_ = false;
+ bool have_video_receiver_ = false;
+
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpTransportControllerAdapter);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_RTPTRANSPORTCONTROLLERADAPTER_H_
diff --git a/ortc/testrtpparameters.cc b/ortc/testrtpparameters.cc
new file mode 100644
index 0000000..de2e7d5
--- /dev/null
+++ b/ortc/testrtpparameters.cc
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/ortc/testrtpparameters.h"
+
+#include <algorithm>
+#include <utility>
+
+namespace webrtc {
+
+RtpParameters MakeMinimalOpusParameters() {
+ RtpParameters parameters;
+ RtpCodecParameters opus_codec;
+ opus_codec.name = "opus";
+ opus_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ opus_codec.payload_type = 111;
+ opus_codec.clock_rate.emplace(48000);
+ opus_codec.num_channels.emplace(2);
+ parameters.codecs.push_back(std::move(opus_codec));
+ RtpEncodingParameters encoding;
+ encoding.codec_payload_type.emplace(111);
+ parameters.encodings.push_back(std::move(encoding));
+ return parameters;
+}
+
+RtpParameters MakeMinimalIsacParameters() {
+ RtpParameters parameters;
+ RtpCodecParameters isac_codec;
+ isac_codec.name = "ISAC";
+ isac_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ isac_codec.payload_type = 103;
+ isac_codec.clock_rate.emplace(16000);
+ parameters.codecs.push_back(std::move(isac_codec));
+ RtpEncodingParameters encoding;
+ encoding.codec_payload_type.emplace(111);
+ parameters.encodings.push_back(std::move(encoding));
+ return parameters;
+}
+
+RtpParameters MakeMinimalOpusParametersWithSsrc(uint32_t ssrc) {
+ RtpParameters parameters = MakeMinimalOpusParameters();
+ parameters.encodings[0].ssrc.emplace(ssrc);
+ return parameters;
+}
+
+RtpParameters MakeMinimalIsacParametersWithSsrc(uint32_t ssrc) {
+ RtpParameters parameters = MakeMinimalIsacParameters();
+ parameters.encodings[0].ssrc.emplace(ssrc);
+ return parameters;
+}
+
+RtpParameters MakeMinimalVideoParameters(const char* codec_name) {
+ RtpParameters parameters;
+ RtpCodecParameters vp8_codec;
+ vp8_codec.name = codec_name;
+ vp8_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_codec.payload_type = 96;
+ parameters.codecs.push_back(std::move(vp8_codec));
+ RtpEncodingParameters encoding;
+ encoding.codec_payload_type.emplace(96);
+ parameters.encodings.push_back(std::move(encoding));
+ return parameters;
+}
+
+RtpParameters MakeMinimalVp8Parameters() {
+ return MakeMinimalVideoParameters("VP8");
+}
+
+RtpParameters MakeMinimalVp9Parameters() {
+ return MakeMinimalVideoParameters("VP9");
+}
+
+RtpParameters MakeMinimalVp8ParametersWithSsrc(uint32_t ssrc) {
+ RtpParameters parameters = MakeMinimalVp8Parameters();
+ parameters.encodings[0].ssrc.emplace(ssrc);
+ return parameters;
+}
+
+RtpParameters MakeMinimalVp9ParametersWithSsrc(uint32_t ssrc) {
+ RtpParameters parameters = MakeMinimalVp9Parameters();
+ parameters.encodings[0].ssrc.emplace(ssrc);
+ return parameters;
+}
+
+// Note: Currently, these "WithNoSsrc" methods are identical to the normal
+// "MakeMinimal" methods, but with the added guarantee that they will never be
+// changed to include an SSRC.
+
+RtpParameters MakeMinimalOpusParametersWithNoSsrc() {
+ RtpParameters parameters = MakeMinimalOpusParameters();
+ RTC_DCHECK(!parameters.encodings[0].ssrc);
+ return parameters;
+}
+
+RtpParameters MakeMinimalIsacParametersWithNoSsrc() {
+ RtpParameters parameters = MakeMinimalIsacParameters();
+ RTC_DCHECK(!parameters.encodings[0].ssrc);
+ return parameters;
+}
+
+RtpParameters MakeMinimalVp8ParametersWithNoSsrc() {
+ RtpParameters parameters = MakeMinimalVp8Parameters();
+ RTC_DCHECK(!parameters.encodings[0].ssrc);
+ return parameters;
+}
+
+RtpParameters MakeMinimalVp9ParametersWithNoSsrc() {
+ RtpParameters parameters = MakeMinimalVp9Parameters();
+ RTC_DCHECK(!parameters.encodings[0].ssrc);
+ return parameters;
+}
+
+// Make audio parameters with all the available properties configured and
+// features used, and with multiple codecs offered. Obtained by taking a
+// snapshot of a default PeerConnection offer (and adding other things, like
+// bitrate limit).
+//
+// See "MakeFullOpusParameters"/"MakeFullIsacParameters" below.
+RtpParameters MakeFullAudioParameters(int preferred_payload_type) {
+ RtpParameters parameters;
+
+ RtpCodecParameters opus_codec;
+ opus_codec.name = "opus";
+ opus_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ opus_codec.payload_type = 111;
+ opus_codec.clock_rate.emplace(48000);
+ opus_codec.num_channels.emplace(2);
+ opus_codec.parameters["minptime"] = "10";
+ opus_codec.parameters["useinbandfec"] = "1";
+ opus_codec.parameters["usedtx"] = "1";
+ opus_codec.parameters["stereo"] = "1";
+ opus_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ parameters.codecs.push_back(std::move(opus_codec));
+
+ RtpCodecParameters isac_codec;
+ isac_codec.name = "ISAC";
+ isac_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ isac_codec.payload_type = 103;
+ isac_codec.clock_rate.emplace(16000);
+ parameters.codecs.push_back(std::move(isac_codec));
+
+ RtpCodecParameters cn_codec;
+ cn_codec.name = "CN";
+ cn_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ cn_codec.payload_type = 106;
+ cn_codec.clock_rate.emplace(32000);
+ parameters.codecs.push_back(std::move(cn_codec));
+
+ RtpCodecParameters dtmf_codec;
+ dtmf_codec.name = "telephone-event";
+ dtmf_codec.kind = cricket::MEDIA_TYPE_AUDIO;
+ dtmf_codec.payload_type = 126;
+ dtmf_codec.clock_rate.emplace(8000);
+ parameters.codecs.push_back(std::move(dtmf_codec));
+
+ // "codec_payload_type" isn't implemented, so we need to reorder codecs to
+ // cause one to be used.
+ // TODO(deadbeef): Remove this when it becomes unnecessary.
+ std::sort(parameters.codecs.begin(), parameters.codecs.end(),
+ [preferred_payload_type](const RtpCodecParameters& a,
+ const RtpCodecParameters& b) {
+ return a.payload_type == preferred_payload_type;
+ });
+
+ // Intentionally leave out SSRC so one's chosen automatically.
+ RtpEncodingParameters encoding;
+ encoding.codec_payload_type.emplace(preferred_payload_type);
+ encoding.dtx.emplace(DtxStatus::ENABLED);
+ // 20 kbps.
+ encoding.max_bitrate_bps.emplace(20000);
+ parameters.encodings.push_back(std::move(encoding));
+
+ parameters.header_extensions.emplace_back(
+ "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 1);
+ return parameters;
+}
+
+RtpParameters MakeFullOpusParameters() {
+ return MakeFullAudioParameters(111);
+}
+
+RtpParameters MakeFullIsacParameters() {
+ return MakeFullAudioParameters(103);
+}
+
+// Make video parameters with all the available properties configured and
+// features used, and with multiple codecs offered. Obtained by taking a
+// snapshot of a default PeerConnection offer (and adding other things, like
+// bitrate limit).
+//
+// See "MakeFullVp8Parameters"/"MakeFullVp9Parameters" below.
+RtpParameters MakeFullVideoParameters(int preferred_payload_type) {
+ RtpParameters parameters;
+
+ RtpCodecParameters vp8_codec;
+ vp8_codec.name = "VP8";
+ vp8_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_codec.payload_type = 100;
+ vp8_codec.clock_rate.emplace(90000);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM,
+ RtcpFeedbackMessageType::FIR);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::GENERIC_NACK);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::PLI);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::REMB);
+ vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ parameters.codecs.push_back(std::move(vp8_codec));
+
+ RtpCodecParameters vp8_rtx_codec;
+ vp8_rtx_codec.name = "rtx";
+ vp8_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp8_rtx_codec.payload_type = 96;
+ vp8_rtx_codec.clock_rate.emplace(90000);
+ vp8_rtx_codec.parameters["apt"] = "100";
+ parameters.codecs.push_back(std::move(vp8_rtx_codec));
+
+ RtpCodecParameters vp9_codec;
+ vp9_codec.name = "VP9";
+ vp9_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp9_codec.payload_type = 101;
+ vp9_codec.clock_rate.emplace(90000);
+ vp9_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM,
+ RtcpFeedbackMessageType::FIR);
+ vp9_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::GENERIC_NACK);
+ vp9_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK,
+ RtcpFeedbackMessageType::PLI);
+ vp9_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::REMB);
+ vp9_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC);
+ parameters.codecs.push_back(std::move(vp9_codec));
+
+ RtpCodecParameters vp9_rtx_codec;
+ vp9_rtx_codec.name = "rtx";
+ vp9_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ vp9_rtx_codec.payload_type = 97;
+ vp9_rtx_codec.clock_rate.emplace(90000);
+ vp9_rtx_codec.parameters["apt"] = "101";
+ parameters.codecs.push_back(std::move(vp9_rtx_codec));
+
+ RtpCodecParameters red_codec;
+ red_codec.name = "red";
+ red_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ red_codec.payload_type = 116;
+ red_codec.clock_rate.emplace(90000);
+ parameters.codecs.push_back(std::move(red_codec));
+
+ RtpCodecParameters red_rtx_codec;
+ red_rtx_codec.name = "rtx";
+ red_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ red_rtx_codec.payload_type = 98;
+ red_rtx_codec.clock_rate.emplace(90000);
+ red_rtx_codec.parameters["apt"] = "116";
+ parameters.codecs.push_back(std::move(red_rtx_codec));
+
+ RtpCodecParameters ulpfec_codec;
+ ulpfec_codec.name = "ulpfec";
+ ulpfec_codec.kind = cricket::MEDIA_TYPE_VIDEO;
+ ulpfec_codec.payload_type = 117;
+ ulpfec_codec.clock_rate.emplace(90000);
+ parameters.codecs.push_back(std::move(ulpfec_codec));
+
+ // "codec_payload_type" isn't implemented, so we need to reorder codecs to
+ // cause one to be used.
+ // TODO(deadbeef): Remove this when it becomes unnecessary.
+ std::sort(parameters.codecs.begin(), parameters.codecs.end(),
+ [preferred_payload_type](const RtpCodecParameters& a,
+ const RtpCodecParameters& b) {
+ return a.payload_type == preferred_payload_type;
+ });
+
+ // Intentionally leave out SSRC so one's chosen automatically.
+ RtpEncodingParameters encoding;
+ encoding.codec_payload_type.emplace(preferred_payload_type);
+ encoding.fec.emplace(FecMechanism::RED_AND_ULPFEC);
+ // Will create default RtxParameters, with unset SSRC.
+ encoding.rtx.emplace();
+ // 100 kbps.
+ encoding.max_bitrate_bps.emplace(100000);
+ parameters.encodings.push_back(std::move(encoding));
+
+ parameters.header_extensions.emplace_back(
+ "urn:ietf:params:rtp-hdrext:toffset", 2);
+ parameters.header_extensions.emplace_back(
+ "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", 3);
+ parameters.header_extensions.emplace_back("urn:3gpp:video-orientation", 4);
+ parameters.header_extensions.emplace_back(
+ "http://www.ietf.org/id/"
+ "draft-holmer-rmcat-transport-wide-cc-extensions-01",
+ 5);
+ parameters.header_extensions.emplace_back(
+ "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", 6);
+ return parameters;
+}
+
+RtpParameters MakeFullVp8Parameters() {
+ return MakeFullVideoParameters(100);
+}
+
+RtpParameters MakeFullVp9Parameters() {
+ return MakeFullVideoParameters(101);
+}
+
+} // namespace webrtc
diff --git a/ortc/testrtpparameters.h b/ortc/testrtpparameters.h
new file mode 100644
index 0000000..87108ca
--- /dev/null
+++ b/ortc/testrtpparameters.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_ORTC_TESTRTPPARAMETERS_H_
+#define WEBRTC_ORTC_TESTRTPPARAMETERS_H_
+
+#include "webrtc/api/ortc/rtptransportinterface.h"
+#include "webrtc/api/rtpparameters.h"
+
+namespace webrtc {
+
+// Helper methods to create RtpParameters to use for sending/receiving.
+//
+// "MakeMinimal" methods contain the minimal necessary information for an
+// RtpSender or RtpReceiver to function. The "MakeFull" methods are the
+// opposite, and include all features that would normally be offered by a
+// PeerConnection, and in some cases additional ones.
+//
+// These methods are intended to be used for end-to-end testing (such as in
+// ortcfactory_integrationtest.cc), or unit testing that doesn't care about the
+// specific contents of the parameters. Tests should NOT assume that these
+// methods will not change; tests that are testing that a specific value in the
+// parameters is applied properly should construct the parameters in the test
+// itself.
+
+inline RtcpParameters MakeRtcpMuxParameters() {
+ RtcpParameters rtcp_parameters;
+ rtcp_parameters.mux = true;
+ return rtcp_parameters;
+}
+
+RtpParameters MakeMinimalOpusParameters();
+RtpParameters MakeMinimalIsacParameters();
+RtpParameters MakeMinimalOpusParametersWithSsrc(uint32_t ssrc);
+RtpParameters MakeMinimalIsacParametersWithSsrc(uint32_t ssrc);
+
+RtpParameters MakeMinimalVp8Parameters();
+RtpParameters MakeMinimalVp9Parameters();
+RtpParameters MakeMinimalVp8ParametersWithSsrc(uint32_t ssrc);
+RtpParameters MakeMinimalVp9ParametersWithSsrc(uint32_t ssrc);
+
+// Will create an encoding with no SSRC (meaning "match first SSRC seen" for a
+// receiver, or "pick one automatically" for a sender).
+RtpParameters MakeMinimalOpusParametersWithNoSsrc();
+RtpParameters MakeMinimalIsacParametersWithNoSsrc();
+RtpParameters MakeMinimalVp8ParametersWithNoSsrc();
+RtpParameters MakeMinimalVp9ParametersWithNoSsrc();
+
+// Make audio parameters with all the available properties configured and
+// features used, and with multiple codecs offered. Obtained by taking a
+// snapshot of a default PeerConnection offer (and adding other things, like
+// bitrate limit).
+RtpParameters MakeFullOpusParameters();
+RtpParameters MakeFullIsacParameters();
+
+// Make video parameters with all the available properties configured and
+// features used, and with multiple codecs offered. Obtained by taking a
+// snapshot of a default PeerConnection offer (and adding other things, like
+// bitrate limit).
+RtpParameters MakeFullVp8Parameters();
+RtpParameters MakeFullVp9Parameters();
+
+} // namespace webrtc
+
+#endif // WEBRTC_ORTC_TESTRTPPARAMETERS_H_
diff --git a/p2p/base/fakepackettransport.h b/p2p/base/fakepackettransport.h
index 03966f9..7e2f084 100644
--- a/p2p/base/fakepackettransport.h
+++ b/p2p/base/fakepackettransport.h
@@ -13,6 +13,7 @@
#include <string>
+#include "webrtc/api/ortc/packettransportinterface.h"
#include "webrtc/base/asyncinvoker.h"
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/p2p/base/packettransportinternal.h"
@@ -20,7 +21,8 @@
namespace rtc {
// Used to simulate a packet-based transport.
-class FakePacketTransport : public PacketTransportInternal {
+class FakePacketTransport : public PacketTransportInternal,
+ public webrtc::PacketTransportInterface {
public:
explicit FakePacketTransport(const std::string& debug_name)
: debug_name_(debug_name) {}
@@ -85,6 +87,9 @@
bool GetOption(Socket::Option opt, int* value) override { return true; }
int GetError() override { return 0; }
+ protected:
+ PacketTransportInternal* GetInternal() override { return this; }
+
private:
void set_writable(bool writable) {
if (writable_ == writable) {
diff --git a/p2p/base/packettransportinternal.h b/p2p/base/packettransportinternal.h
index 5789c62..325c00e 100644
--- a/p2p/base/packettransportinternal.h
+++ b/p2p/base/packettransportinternal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ * Copyright 2017 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
@@ -30,8 +30,6 @@
class PacketTransportInternal : public sigslot::has_slots<> {
public:
- virtual ~PacketTransportInternal() {}
-
// Identify the object for logging and debug purpose.
virtual std::string debug_name() const = 0;
@@ -59,7 +57,7 @@
// supported by all transport types.
virtual int SetOption(rtc::Socket::Option opt, int value) = 0;
- // TODO(pthatcher): Once Chrome's MockPacketTransportInternal implements
+ // TODO(pthatcher): Once Chrome's MockPacketTransportInterface implements
// this, remove the default implementation.
virtual bool GetOption(rtc::Socket::Option opt, int* value) { return false; }
diff --git a/p2p/base/udptransport.h b/p2p/base/udptransport.h
index e8bd493..1cf8e42 100644
--- a/p2p/base/udptransport.h
+++ b/p2p/base/udptransport.h
@@ -14,7 +14,7 @@
#include <memory>
#include <string>
-#include "webrtc/api/udptransportinterface.h"
+#include "webrtc/api/ortc/udptransportinterface.h"
#include "webrtc/base/asyncpacketsocket.h" // For PacketOptions.
#include "webrtc/base/optional.h"
#include "webrtc/base/thread_checker.h"
@@ -31,8 +31,8 @@
// Implementation of UdpTransportInterface.
// Used by OrtcFactory.
-class UdpTransport : public webrtc::UdpTransportInterface,
- public rtc::PacketTransportInternal {
+class UdpTransport : public rtc::PacketTransportInternal,
+ public webrtc::UdpTransportInterface {
public:
// |transport_name| is only used for identification/logging.
// |socket| must be non-null.
@@ -64,6 +64,9 @@
int GetError() override { return send_error_; }
+ protected:
+ PacketTransportInternal* GetInternal() override { return this; }
+
private:
void OnSocketReadPacket(rtc::AsyncPacketSocket* socket,
const char* data,
@@ -80,6 +83,7 @@
rtc::SocketAddress remote_address_;
rtc::ThreadChecker network_thread_checker_;
};
+
} // namespace cricket
#endif // WEBRTC_P2P_BASE_UDPTRANSPORT_H_
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 54471c6..0d86bc3 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -103,8 +103,6 @@
"mediastreamobserver.cc",
"mediastreamobserver.h",
"mediastreamtrack.h",
- "ortcfactory.cc",
- "ortcfactory.h",
"peerconnection.cc",
"peerconnection.h",
"peerconnectionfactory.cc",
@@ -225,6 +223,38 @@
}
}
+ rtc_source_set("pc_test_utils") {
+ testonly = true
+ sources = [
+ "test/fakeaudiocapturemodule.cc",
+ "test/fakeaudiocapturemodule.h",
+ "test/fakedatachannelprovider.h",
+ "test/fakeperiodicvideocapturer.h",
+ "test/fakertccertificategenerator.h",
+ "test/fakevideotrackrenderer.h",
+ "test/fakevideotracksource.h",
+ "test/mock_datachannel.h",
+ "test/mock_peerconnection.h",
+ "test/mock_webrtcsession.h",
+ "test/mockpeerconnectionobservers.h",
+ "test/peerconnectiontestwrapper.cc",
+ "test/peerconnectiontestwrapper.h",
+ "test/rtcstatsobtainer.h",
+ "test/testsdpstrings.h",
+ ]
+
+ deps = [
+ ":libjingle_peerconnection",
+ "../base:rtc_base_tests_utils",
+ "//testing/gmock",
+ ]
+
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+ }
+
config("peerconnection_unittests_config") {
# The warnings below are enabled by default. Since GN orders compiler flags
# for a target before flags from configs, the only way to disable such
@@ -256,7 +286,6 @@
"localaudiosource_unittest.cc",
"mediaconstraintsinterface_unittest.cc",
"mediastream_unittest.cc",
- "ortcfactory_unittest.cc",
"peerconnection_unittest.cc",
"peerconnectionendtoend_unittest.cc",
"peerconnectionfactory_unittest.cc",
@@ -267,21 +296,7 @@
"rtpsenderreceiver_unittest.cc",
"sctputils_unittest.cc",
"statscollector_unittest.cc",
- "test/fakeaudiocapturemodule.cc",
- "test/fakeaudiocapturemodule.h",
"test/fakeaudiocapturemodule_unittest.cc",
- "test/fakedatachannelprovider.h",
- "test/fakeperiodicvideocapturer.h",
- "test/fakertccertificategenerator.h",
- "test/fakevideotrackrenderer.h",
- "test/fakevideotracksource.h",
- "test/mock_datachannel.h",
- "test/mock_peerconnection.h",
- "test/mock_webrtcsession.h",
- "test/mockpeerconnectionobservers.h",
- "test/peerconnectiontestwrapper.cc",
- "test/peerconnectiontestwrapper.h",
- "test/rtcstatsobtainer.h",
"test/testsdpstrings.h",
"trackmediainfomap_unittest.cc",
"videocapturertracksource_unittest.cc",
@@ -336,6 +351,7 @@
deps += [
":libjingle_peerconnection",
+ ":pc_test_utils",
"..:webrtc_common",
"../api:fakemetricsobserver",
"../base:rtc_base_tests_utils",
diff --git a/pc/channel.cc b/pc/channel.cc
index 02d93f7..9ac9d20 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -1258,10 +1258,14 @@
case CA_ANSWER:
ret = rtcp_mux_filter_.SetAnswer(enable, src);
if (ret && rtcp_mux_filter_.IsActive()) {
- // We activated RTCP mux, close down the RTCP transport.
+ // We permanently activated RTCP muxing; signal that we no longer need
+ // the RTCP transport.
+ std::string debug_name = transport_name_.empty()
+ ? rtp_packet_transport_->debug_name()
+ : transport_name_;
+ ;
LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
- << " by destroying RTCP transport for "
- << transport_name();
+ << "; no longer need RTCP transport for " << debug_name;
if (rtcp_packet_transport_) {
SetTransport_n(true, nullptr, nullptr);
SignalRtcpMuxFullyActive(transport_name_);
diff --git a/pc/channelmanager.cc b/pc/channelmanager.cc
index 5362b0f..150dfd9 100644
--- a/pc/channelmanager.cc
+++ b/pc/channelmanager.cc
@@ -213,14 +213,31 @@
return worker_thread_->Invoke<VoiceChannel*>(
RTC_FROM_HERE,
Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller,
- rtp_transport, rtcp_transport, signaling_thread, content_name,
- srtp_required, options));
+ rtp_transport, rtcp_transport, rtp_transport, rtcp_transport,
+ signaling_thread, content_name, srtp_required, options));
+}
+
+VoiceChannel* ChannelManager::CreateVoiceChannel(
+ webrtc::MediaControllerInterface* media_controller,
+ rtc::PacketTransportInternal* rtp_transport,
+ rtc::PacketTransportInternal* rtcp_transport,
+ rtc::Thread* signaling_thread,
+ const std::string& content_name,
+ bool srtp_required,
+ const AudioOptions& options) {
+ return worker_thread_->Invoke<VoiceChannel*>(
+ RTC_FROM_HERE,
+ Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller,
+ nullptr, nullptr, rtp_transport, rtcp_transport, signaling_thread,
+ content_name, srtp_required, options));
}
VoiceChannel* ChannelManager::CreateVoiceChannel_w(
webrtc::MediaControllerInterface* media_controller,
- DtlsTransportInternal* rtp_transport,
- DtlsTransportInternal* rtcp_transport,
+ DtlsTransportInternal* rtp_dtls_transport,
+ DtlsTransportInternal* rtcp_dtls_transport,
+ rtc::PacketTransportInternal* rtp_packet_transport,
+ rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
@@ -234,13 +251,14 @@
if (!media_channel)
return nullptr;
- VoiceChannel* voice_channel = new VoiceChannel(
- worker_thread_, network_thread_, signaling_thread, media_engine_.get(),
- media_channel, content_name, rtcp_transport == nullptr, srtp_required);
+ VoiceChannel* voice_channel =
+ new VoiceChannel(worker_thread_, network_thread_, signaling_thread,
+ media_engine_.get(), media_channel, content_name,
+ rtcp_packet_transport == nullptr, srtp_required);
voice_channel->SetCryptoOptions(crypto_options_);
- if (!voice_channel->Init_w(rtp_transport, rtcp_transport, rtp_transport,
- rtcp_transport)) {
+ if (!voice_channel->Init_w(rtp_dtls_transport, rtcp_dtls_transport,
+ rtp_packet_transport, rtcp_packet_transport)) {
delete voice_channel;
return nullptr;
}
@@ -282,14 +300,31 @@
return worker_thread_->Invoke<VideoChannel*>(
RTC_FROM_HERE,
Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller,
- rtp_transport, rtcp_transport, signaling_thread, content_name,
- srtp_required, options));
+ rtp_transport, rtcp_transport, rtp_transport, rtcp_transport,
+ signaling_thread, content_name, srtp_required, options));
+}
+
+VideoChannel* ChannelManager::CreateVideoChannel(
+ webrtc::MediaControllerInterface* media_controller,
+ rtc::PacketTransportInternal* rtp_transport,
+ rtc::PacketTransportInternal* rtcp_transport,
+ rtc::Thread* signaling_thread,
+ const std::string& content_name,
+ bool srtp_required,
+ const VideoOptions& options) {
+ return worker_thread_->Invoke<VideoChannel*>(
+ RTC_FROM_HERE,
+ Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller,
+ nullptr, nullptr, rtp_transport, rtcp_transport, signaling_thread,
+ content_name, srtp_required, options));
}
VideoChannel* ChannelManager::CreateVideoChannel_w(
webrtc::MediaControllerInterface* media_controller,
- DtlsTransportInternal* rtp_transport,
- DtlsTransportInternal* rtcp_transport,
+ DtlsTransportInternal* rtp_dtls_transport,
+ DtlsTransportInternal* rtcp_dtls_transport,
+ rtc::PacketTransportInternal* rtp_packet_transport,
+ rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
@@ -305,10 +340,10 @@
VideoChannel* video_channel = new VideoChannel(
worker_thread_, network_thread_, signaling_thread, media_channel,
- content_name, rtcp_transport == nullptr, srtp_required);
+ content_name, rtcp_packet_transport == nullptr, srtp_required);
video_channel->SetCryptoOptions(crypto_options_);
- if (!video_channel->Init_w(rtp_transport, rtcp_transport, rtp_transport,
- rtcp_transport)) {
+ if (!video_channel->Init_w(rtp_dtls_transport, rtcp_dtls_transport,
+ rtp_packet_transport, rtcp_packet_transport)) {
delete video_channel;
return NULL;
}
diff --git a/pc/channelmanager.h b/pc/channelmanager.h
index 8c6ee7f..b763fa1 100644
--- a/pc/channelmanager.h
+++ b/pc/channelmanager.h
@@ -95,6 +95,15 @@
const std::string& content_name,
bool srtp_required,
const AudioOptions& options);
+ // Version of the above that takes PacketTransportInternal.
+ VoiceChannel* CreateVoiceChannel(
+ webrtc::MediaControllerInterface* media_controller,
+ rtc::PacketTransportInternal* rtp_transport,
+ rtc::PacketTransportInternal* rtcp_transport,
+ rtc::Thread* signaling_thread,
+ const std::string& content_name,
+ bool srtp_required,
+ const AudioOptions& options);
// Destroys a voice channel created with the Create API.
void DestroyVoiceChannel(VoiceChannel* voice_channel);
// Creates a video channel, synced with the specified voice channel, and
@@ -107,6 +116,15 @@
const std::string& content_name,
bool srtp_required,
const VideoOptions& options);
+ // Version of the above that takes PacketTransportInternal.
+ VideoChannel* CreateVideoChannel(
+ webrtc::MediaControllerInterface* media_controller,
+ rtc::PacketTransportInternal* rtp_transport,
+ rtc::PacketTransportInternal* rtcp_transport,
+ rtc::Thread* signaling_thread,
+ const std::string& content_name,
+ bool srtp_required,
+ const VideoOptions& options);
// Destroys a video channel created with the Create API.
void DestroyVideoChannel(VideoChannel* video_channel);
RtpDataChannel* CreateRtpDataChannel(
@@ -160,8 +178,10 @@
bool SetCryptoOptions_w(const rtc::CryptoOptions& crypto_options);
VoiceChannel* CreateVoiceChannel_w(
webrtc::MediaControllerInterface* media_controller,
- DtlsTransportInternal* rtp_transport,
- DtlsTransportInternal* rtcp_transport,
+ DtlsTransportInternal* rtp_dtls_transport,
+ DtlsTransportInternal* rtcp_dtls_transport,
+ rtc::PacketTransportInternal* rtp_packet_transport,
+ rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
@@ -169,8 +189,10 @@
void DestroyVoiceChannel_w(VoiceChannel* voice_channel);
VideoChannel* CreateVideoChannel_w(
webrtc::MediaControllerInterface* media_controller,
- DtlsTransportInternal* rtp_transport,
- DtlsTransportInternal* rtcp_transport,
+ DtlsTransportInternal* rtp_dtls_transport,
+ DtlsTransportInternal* rtcp_dtls_transport,
+ rtc::PacketTransportInternal* rtp_packet_transport,
+ rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
diff --git a/pc/mediacontroller.h b/pc/mediacontroller.h
index 85617af..f5a9034 100644
--- a/pc/mediacontroller.h
+++ b/pc/mediacontroller.h
@@ -23,10 +23,12 @@
class VoiceEngine;
class RtcEventLog;
-// The MediaController currently owns shared state between media channels, but
-// in the future will create and own RtpSenders and RtpReceivers.
+// The MediaController currently owns shared state between media channels.
+// Abstract interface is defined here such that it can be faked/mocked for
+// tests, but no other real reason.
class MediaControllerInterface {
public:
+ // Will never return nullptr.
static MediaControllerInterface* Create(
const cricket::MediaConfig& config,
rtc::Thread* worker_thread,
diff --git a/pc/mediasession.cc b/pc/mediasession.cc
index 5e38088..05e6ccb 100644
--- a/pc/mediasession.cc
+++ b/pc/mediasession.cc
@@ -404,12 +404,10 @@
class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
public:
UsedRtpHeaderExtensionIds()
- : UsedIds<webrtc::RtpExtension>(kLocalIdMin, kLocalIdMax) {}
+ : UsedIds<webrtc::RtpExtension>(webrtc::RtpExtension::kMinId,
+ webrtc::RtpExtension::kMaxId) {}
private:
- // Min and Max local identifier for one-byte header extensions, per RFC5285.
- static const int kLocalIdMin = 1;
- static const int kLocalIdMax = 14;
};
static bool IsSctp(const MediaContentDescription* desc) {
@@ -1281,7 +1279,6 @@
transport_desc_factory_(transport_desc_factory) {
channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
- channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
channel_manager->GetSupportedVideoCodecs(&video_codecs_);
channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
diff --git a/pc/mediasession.h b/pc/mediasession.h
index f864fe9..a901e1d 100644
--- a/pc/mediasession.h
+++ b/pc/mediasession.h
@@ -92,6 +92,10 @@
MediaContentDirection md);
MediaContentDirection ToMediaContentDirection() const;
+
+ RtpTransceiverDirection Reversed() const {
+ return RtpTransceiverDirection(recv, send);
+ }
};
RtpTransceiverDirection
diff --git a/pc/ortcfactory.cc b/pc/ortcfactory.cc
deleted file mode 100644
index aa5e181..0000000
--- a/pc/ortcfactory.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2017 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "webrtc/pc/ortcfactory.h"
-
-#include <string>
-#include <utility> // For std::move.
-
-#include "webrtc/base/bind.h"
-#include "webrtc/base/asyncpacketsocket.h"
-#include "webrtc/p2p/base/basicpacketsocketfactory.h"
-#include "webrtc/p2p/base/udptransport.h"
-
-namespace webrtc {
-
-// static
-std::unique_ptr<OrtcFactoryInterface> OrtcFactoryInterface::Create(
- rtc::Thread* network_thread,
- rtc::Thread* signaling_thread,
- rtc::NetworkManager* network_manager,
- rtc::PacketSocketFactory* socket_factory) {
- // Hop to signaling thread if needed.
- if (signaling_thread && !signaling_thread->IsCurrent()) {
- // The template parameters are necessary because there are two
- // OrtcFactoryInterface::Create methods, so the types can't be derived from
- // just the function pointer.
- return signaling_thread->Invoke<std::unique_ptr<OrtcFactoryInterface>>(
- RTC_FROM_HERE,
- rtc::Bind<std::unique_ptr<OrtcFactoryInterface>, rtc::Thread*,
- rtc::Thread*, rtc::NetworkManager*,
- rtc::PacketSocketFactory*>(&OrtcFactoryInterface::Create,
- network_thread, signaling_thread,
- network_manager, socket_factory));
- }
- OrtcFactory* new_factory =
- new OrtcFactory(network_thread, signaling_thread,
- network_manager, socket_factory);
- // Return a proxy so that any calls on the returned object (including
- // destructor) happen on the signaling thread.
- return OrtcFactoryProxy::Create(new_factory->signaling_thread(),
- new_factory->network_thread(), new_factory);
-}
-
-OrtcFactory::OrtcFactory(rtc::Thread* network_thread,
- rtc::Thread* signaling_thread,
- rtc::NetworkManager* network_manager,
- rtc::PacketSocketFactory* socket_factory)
- : network_thread_(network_thread),
- signaling_thread_(signaling_thread),
- network_manager_(network_manager),
- socket_factory_(socket_factory) {
- if (!network_thread_) {
- owned_network_thread_ = rtc::Thread::CreateWithSocketServer();
- owned_network_thread_->Start();
- network_thread_ = owned_network_thread_.get();
- }
-
- // The worker thread is created internally because it's an implementation
- // detail, and consumers of the API don't need to really know about it.
- owned_worker_thread_ = rtc::Thread::Create();
- owned_worker_thread_->Start();
-
- if (signaling_thread_) {
- RTC_DCHECK_RUN_ON(signaling_thread_);
- } else {
- signaling_thread_ = rtc::Thread::Current();
- if (!signaling_thread_) {
- // If this thread isn't already wrapped by an rtc::Thread, create a
- // wrapper and own it in this class.
- signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread();
- wraps_signaling_thread_ = true;
- }
- }
- if (!network_manager_) {
- owned_network_manager_.reset(new rtc::BasicNetworkManager());
- network_manager_ = owned_network_manager_.get();
- }
- if (!socket_factory_) {
- owned_socket_factory_.reset(
- new rtc::BasicPacketSocketFactory(network_thread_));
- socket_factory_ = owned_socket_factory_.get();
- }
-}
-
-OrtcFactory::~OrtcFactory() {
- RTC_DCHECK_RUN_ON(signaling_thread_);
- if (wraps_signaling_thread_) {
- rtc::ThreadManager::Instance()->UnwrapCurrentThread();
- }
-}
-
-std::unique_ptr<UdpTransportInterface> OrtcFactory::CreateUdpTransport(
- int family,
- uint16_t min_port,
- uint16_t max_port) {
- if (!network_thread_->IsCurrent()) {
- RTC_DCHECK_RUN_ON(signaling_thread_);
- return network_thread_->Invoke<std::unique_ptr<UdpTransportInterface>>(
- RTC_FROM_HERE, rtc::Bind(&OrtcFactory::CreateUdpTransport, this, family,
- min_port, max_port));
- }
- std::unique_ptr<rtc::AsyncPacketSocket> socket(
- socket_factory_->CreateUdpSocket(
- rtc::SocketAddress(rtc::GetAnyIP(family), 0), min_port, max_port));
- if (!socket) {
- LOG(LS_WARNING) << "Local socket allocation failure.";
- return nullptr;
- }
- LOG(LS_INFO) << "Created UDP socket with address "
- << socket->GetLocalAddress().ToSensitiveString() << ".";
- // Use proxy so that calls to the returned object are invoked on the network
- // thread.
- return UdpTransportProxy::Create(
- signaling_thread_, network_thread_,
- new cricket::UdpTransport(std::string(), std::move(socket)));
-}
-
-} // namespace webrtc
diff --git a/pc/ortcfactory.h b/pc/ortcfactory.h
deleted file mode 100644
index 65fe10f..0000000
--- a/pc/ortcfactory.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2017 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef WEBRTC_PC_ORTCFACTORY_H_
-#define WEBRTC_PC_ORTCFACTORY_H_
-
-#include <memory>
-
-#include "webrtc/api/ortcfactoryinterface.h"
-#include "webrtc/base/constructormagic.h"
-
-namespace webrtc {
-
-// Implementation of OrtcFactoryInterface.
-//
-// See ortcfactoryinterface.h for documentation.
-class OrtcFactory : public OrtcFactoryInterface {
- public:
- OrtcFactory(rtc::Thread* network_thread,
- rtc::Thread* signaling_thread,
- rtc::NetworkManager* network_manager,
- rtc::PacketSocketFactory* socket_factory);
- ~OrtcFactory() override;
- std::unique_ptr<UdpTransportInterface>
- CreateUdpTransport(int family, uint16_t min_port, uint16_t max_port) override;
-
- rtc::Thread* network_thread() { return network_thread_; }
- rtc::Thread* worker_thread() { return owned_worker_thread_.get(); }
- rtc::Thread* signaling_thread() { return signaling_thread_; }
-
- private:
- rtc::Thread* network_thread_;
- rtc::Thread* signaling_thread_;
- rtc::NetworkManager* network_manager_;
- rtc::PacketSocketFactory* socket_factory_;
- // If we created/own the objects above, these will be non-null and thus will
- // be released automatically upon destruction.
- std::unique_ptr<rtc::Thread> owned_network_thread_;
- std::unique_ptr<rtc::Thread> owned_worker_thread_;
- bool wraps_signaling_thread_ = false;
- std::unique_ptr<rtc::NetworkManager> owned_network_manager_;
- std::unique_ptr<rtc::PacketSocketFactory> owned_socket_factory_;
- RTC_DISALLOW_COPY_AND_ASSIGN(OrtcFactory);
-};
-
-BEGIN_OWNED_PROXY_MAP(OrtcFactory)
- PROXY_SIGNALING_THREAD_DESTRUCTOR()
- PROXY_METHOD3(std::unique_ptr<UdpTransportInterface>,
- CreateUdpTransport,
- int,
- uint16_t,
- uint16_t)
-END_PROXY_MAP()
-
-} // namespace webrtc
-
-#endif // WEBRTC_PC_ORTCFACTORY_H_
diff --git a/pc/ortcfactory_unittest.cc b/pc/ortcfactory_unittest.cc
deleted file mode 100644
index 5328631..0000000
--- a/pc/ortcfactory_unittest.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2017 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include <memory>
-
-#include "webrtc/api/ortcfactoryinterface.h"
-#include "webrtc/base/fakenetwork.h"
-#include "webrtc/base/gunit.h"
-#include "webrtc/base/physicalsocketserver.h"
-#include "webrtc/base/virtualsocketserver.h"
-#include "webrtc/p2p/base/udptransport.h"
-
-namespace {
-
-const int kDefaultTimeout = 10000; // 10 seconds.
-static const rtc::IPAddress kIPv4LocalHostAddress =
- rtc::IPAddress(0x7F000001); // 127.0.0.1
-
-class PacketReceiver : public sigslot::has_slots<> {
- public:
- explicit PacketReceiver(rtc::PacketTransportInternal* transport) {
- transport->SignalReadPacket.connect(this, &PacketReceiver::OnReadPacket);
- }
- int packets_read() const { return packets_read_; }
-
- private:
- void OnReadPacket(rtc::PacketTransportInternal*,
- const char*,
- size_t,
- const rtc::PacketTime&,
- int) {
- ++packets_read_;
- }
-
- int packets_read_ = 0;
-};
-
-} // namespace
-
-namespace webrtc {
-
-// Used to test that things work end-to-end when using the default
-// implementations of threads/etc. provided by OrtcFactory, with the exception
-// of using a virtual network.
-//
-// By default, the virtual network manager doesn't enumerate any networks, but
-// sockets can still be created in this state.
-class OrtcFactoryTest : public testing::Test {
- public:
- OrtcFactoryTest()
- : virtual_socket_server_(&physical_socket_server_),
- network_thread_(&virtual_socket_server_),
- ortc_factory_(OrtcFactoryInterface::Create(&network_thread_,
- nullptr,
- &fake_network_manager_,
- nullptr)) {
- // Sockets are bound to the ANY address, so this is needed to tell the
- // virtual network which address to use in this case.
- virtual_socket_server_.SetDefaultRoute(kIPv4LocalHostAddress);
- network_thread_.Start();
- }
-
- protected:
- rtc::PhysicalSocketServer physical_socket_server_;
- rtc::VirtualSocketServer virtual_socket_server_;
- rtc::Thread network_thread_;
- rtc::FakeNetworkManager fake_network_manager_;
- std::unique_ptr<OrtcFactoryInterface> ortc_factory_;
-};
-
-TEST_F(OrtcFactoryTest, EndToEndUdpTransport) {
- std::unique_ptr<UdpTransportInterface> transport1 =
- ortc_factory_->CreateUdpTransport(AF_INET);
- std::unique_ptr<UdpTransportInterface> transport2 =
- ortc_factory_->CreateUdpTransport(AF_INET);
- ASSERT_NE(nullptr, transport1);
- ASSERT_NE(nullptr, transport2);
- // Sockets are bound to the ANY address, so we need to provide the IP address
- // explicitly.
- transport1->SetRemoteAddress(
- rtc::SocketAddress(virtual_socket_server_.GetDefaultRoute(AF_INET),
- transport2->GetLocalAddress().port()));
- transport2->SetRemoteAddress(
- rtc::SocketAddress(virtual_socket_server_.GetDefaultRoute(AF_INET),
- transport1->GetLocalAddress().port()));
-
- // TODO(deadbeef): Once there's something (RTP senders/receivers) that can
- // use UdpTransport end-to-end, use that for this end-to-end test instead of
- // making assumptions about the implementation.
- //
- // For now, this assumes the returned object is a UdpTransportProxy that wraps
- // a UdpTransport.
- cricket::UdpTransport* internal_transport1 =
- static_cast<UdpTransportProxyWithInternal<cricket::UdpTransport>*>(
- transport1.get())
- ->internal();
- cricket::UdpTransport* internal_transport2 =
- static_cast<UdpTransportProxyWithInternal<cricket::UdpTransport>*>(
- transport2.get())
- ->internal();
- // Need to call internal "SendPacket" method on network thread.
- network_thread_.Invoke<void>(
- RTC_FROM_HERE, [internal_transport1, internal_transport2]() {
- PacketReceiver receiver1(internal_transport1);
- PacketReceiver receiver2(internal_transport2);
- internal_transport1->SendPacket("foo", sizeof("foo"),
- rtc::PacketOptions(), 0);
- internal_transport2->SendPacket("foo", sizeof("foo"),
- rtc::PacketOptions(), 0);
- EXPECT_EQ_WAIT(1, receiver1.packets_read(), kDefaultTimeout);
- EXPECT_EQ_WAIT(1, receiver2.packets_read(), kDefaultTimeout);
- });
-}
-
-} // namespace webrtc
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index ee9341e..c3ec0b2 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -1615,9 +1615,10 @@
uint32_t ssrc) {
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
- signaling_thread(), new AudioRtpReceiver(stream, track_id, ssrc,
- session_->voice_channel()));
-
+ signaling_thread(),
+ new AudioRtpReceiver(track_id, ssrc, session_->voice_channel()));
+ stream->AddTrack(
+ static_cast<AudioTrackInterface*>(receiver->internal()->track().get()));
receivers_.push_back(receiver);
std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams;
streams.push_back(rtc::scoped_refptr<MediaStreamInterface>(stream));
@@ -1630,8 +1631,10 @@
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
signaling_thread(),
- new VideoRtpReceiver(stream, track_id, factory_->worker_thread(),
- ssrc, session_->video_channel()));
+ new VideoRtpReceiver(track_id, factory_->worker_thread(), ssrc,
+ session_->video_channel()));
+ stream->AddTrack(
+ static_cast<VideoTrackInterface*>(receiver->internal()->track().get()));
receivers_.push_back(receiver);
std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams;
streams.push_back(rtc::scoped_refptr<MediaStreamInterface>(stream));
diff --git a/pc/proxy_unittest.cc b/pc/proxy_unittest.cc
index 148b742..d7acdc1 100644
--- a/pc/proxy_unittest.cc
+++ b/pc/proxy_unittest.cc
@@ -284,8 +284,9 @@
public:
OwnedProxyTest()
: foo_(new Foo()),
- foo_proxy_(
- FooProxy::Create(&signaling_thread_, &worker_thread_, foo_)) {
+ foo_proxy_(FooProxy::Create(&signaling_thread_,
+ &worker_thread_,
+ std::unique_ptr<FooInterface>(foo_))) {
signaling_thread_.Start();
worker_thread_.Start();
}
diff --git a/pc/rtpreceiver.cc b/pc/rtpreceiver.cc
index f57babb..6073b15 100644
--- a/pc/rtpreceiver.cc
+++ b/pc/rtpreceiver.cc
@@ -18,8 +18,7 @@
namespace webrtc {
-AudioRtpReceiver::AudioRtpReceiver(MediaStreamInterface* stream,
- const std::string& track_id,
+AudioRtpReceiver::AudioRtpReceiver(const std::string& track_id,
uint32_t ssrc,
cricket::VoiceChannel* channel)
: id_(track_id),
@@ -34,7 +33,6 @@
track_->RegisterObserver(this);
track_->GetSource()->RegisterAudioObserver(this);
Reconfigure();
- stream->AddTrack(track_);
if (channel_) {
channel_->SignalFirstPacketReceived.connect(
this, &AudioRtpReceiver::OnFirstPacketReceived);
@@ -137,8 +135,7 @@
received_first_packet_ = true;
}
-VideoRtpReceiver::VideoRtpReceiver(MediaStreamInterface* stream,
- const std::string& track_id,
+VideoRtpReceiver::VideoRtpReceiver(const std::string& track_id,
rtc::Thread* worker_thread,
uint32_t ssrc,
cricket::VideoChannel* channel)
@@ -164,7 +161,6 @@
RTC_NOTREACHED();
}
}
- stream->AddTrack(track_);
if (channel_) {
channel_->SignalFirstPacketReceived.connect(
this, &VideoRtpReceiver::OnFirstPacketReceived);
diff --git a/pc/rtpreceiver.h b/pc/rtpreceiver.h
index c135f22..513f90c 100644
--- a/pc/rtpreceiver.h
+++ b/pc/rtpreceiver.h
@@ -34,6 +34,9 @@
class RtpReceiverInternal : public RtpReceiverInterface {
public:
virtual void Stop() = 0;
+ // This SSRC is used as an identifier for the receiver between the API layer
+ // and the WebRtcVideoEngine2, WebRtcVoiceEngine layer.
+ virtual uint32_t ssrc() const = 0;
};
class AudioRtpReceiver : public ObserverInterface,
@@ -41,8 +44,11 @@
public rtc::RefCountedObject<RtpReceiverInternal>,
public sigslot::has_slots<> {
public:
- AudioRtpReceiver(MediaStreamInterface* stream,
- const std::string& track_id,
+ // An SSRC of 0 will create a receiver that will match the first SSRC it
+ // sees.
+ // TODO(deadbeef): Use rtc::Optional, or have another constructor that
+ // doesn't take an SSRC, and make this one DCHECK(ssrc != 0).
+ AudioRtpReceiver(const std::string& track_id,
uint32_t ssrc,
cricket::VoiceChannel* channel);
@@ -74,6 +80,7 @@
// RtpReceiverInternal implementation.
void Stop() override;
+ uint32_t ssrc() const override { return ssrc_; }
void SetObserver(RtpReceiverObserverInterface* observer) override;
@@ -99,8 +106,9 @@
class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInternal>,
public sigslot::has_slots<> {
public:
- VideoRtpReceiver(MediaStreamInterface* stream,
- const std::string& track_id,
+ // An SSRC of 0 will create a receiver that will match the first SSRC it
+ // sees.
+ VideoRtpReceiver(const std::string& track_id,
rtc::Thread* worker_thread,
uint32_t ssrc,
cricket::VideoChannel* channel);
@@ -127,6 +135,7 @@
// RtpReceiverInternal implementation.
void Stop() override;
+ uint32_t ssrc() const override { return ssrc_; }
void SetObserver(RtpReceiverObserverInterface* observer) override;
diff --git a/pc/rtpsenderreceiver_unittest.cc b/pc/rtpsenderreceiver_unittest.cc
index 105d9d3..5ddf6c4 100644
--- a/pc/rtpsenderreceiver_unittest.cc
+++ b/pc/rtpsenderreceiver_unittest.cc
@@ -59,7 +59,7 @@
public:
RtpSenderReceiverTest()
: // Create fake media engine/etc. so we can create channels to use to
- // test RtpSenders/RtpReceivers.
+ // test RtpSenders/RtpReceivers.
media_engine_(new cricket::FakeMediaEngine()),
channel_manager_(
std::unique_ptr<cricket::MediaEngineInterface>(media_engine_),
@@ -67,7 +67,7 @@
rtc::Thread::Current()),
fake_call_(Call::Config(&event_log_)),
fake_media_controller_(&channel_manager_, &fake_call_),
- stream_(MediaStream::Create(kStreamLabel1)) {
+ local_stream_(MediaStream::Create(kStreamLabel1)) {
// Create channels to be used by the RtpSenders and RtpReceivers.
channel_manager_.Init();
bool srtp_required = true;
@@ -126,17 +126,17 @@
rtc::scoped_refptr<VideoTrackSourceInterface> source(
FakeVideoTrackSource::Create(is_screencast));
video_track_ = VideoTrack::Create(kVideoTrackId, source);
- EXPECT_TRUE(stream_->AddTrack(video_track_));
+ EXPECT_TRUE(local_stream_->AddTrack(video_track_));
}
void CreateAudioRtpSender() { CreateAudioRtpSender(nullptr); }
void CreateAudioRtpSender(rtc::scoped_refptr<LocalAudioSource> source) {
audio_track_ = AudioTrack::Create(kAudioTrackId, source);
- EXPECT_TRUE(stream_->AddTrack(audio_track_));
+ EXPECT_TRUE(local_stream_->AddTrack(audio_track_));
audio_rtp_sender_ =
- new AudioRtpSender(stream_->GetAudioTracks()[0], stream_->label(),
- voice_channel_, nullptr);
+ new AudioRtpSender(local_stream_->GetAudioTracks()[0],
+ local_stream_->label(), voice_channel_, nullptr);
audio_rtp_sender_->SetSsrc(kAudioSsrc);
audio_rtp_sender_->GetOnDestroyedSignal()->connect(
this, &RtpSenderReceiverTest::OnAudioSenderDestroyed);
@@ -149,8 +149,9 @@
void CreateVideoRtpSender(bool is_screencast) {
AddVideoTrack(is_screencast);
- video_rtp_sender_ = new VideoRtpSender(stream_->GetVideoTracks()[0],
- stream_->label(), video_channel_);
+ video_rtp_sender_ =
+ new VideoRtpSender(local_stream_->GetVideoTracks()[0],
+ local_stream_->label(), video_channel_);
video_rtp_sender_->SetSsrc(kVideoSsrc);
VerifyVideoChannelInput();
}
@@ -166,19 +167,15 @@
}
void CreateAudioRtpReceiver() {
- audio_track_ = AudioTrack::Create(
- kAudioTrackId, RemoteAudioSource::Create(kAudioSsrc, NULL));
- EXPECT_TRUE(stream_->AddTrack(audio_track_));
- audio_rtp_receiver_ = new AudioRtpReceiver(stream_, kAudioTrackId,
- kAudioSsrc, voice_channel_);
+ audio_rtp_receiver_ =
+ new AudioRtpReceiver(kAudioTrackId, kAudioSsrc, voice_channel_);
audio_track_ = audio_rtp_receiver_->audio_track();
VerifyVoiceChannelOutput();
}
void CreateVideoRtpReceiver() {
- video_rtp_receiver_ =
- new VideoRtpReceiver(stream_, kVideoTrackId, rtc::Thread::Current(),
- kVideoSsrc, video_channel_);
+ video_rtp_receiver_ = new VideoRtpReceiver(
+ kVideoTrackId, rtc::Thread::Current(), kVideoSsrc, video_channel_);
video_track_ = video_rtp_receiver_->video_track();
VerifyVideoChannelOutput();
}
@@ -263,7 +260,7 @@
rtc::scoped_refptr<VideoRtpSender> video_rtp_sender_;
rtc::scoped_refptr<AudioRtpReceiver> audio_rtp_receiver_;
rtc::scoped_refptr<VideoRtpReceiver> video_rtp_receiver_;
- rtc::scoped_refptr<MediaStreamInterface> stream_;
+ rtc::scoped_refptr<MediaStreamInterface> local_stream_;
rtc::scoped_refptr<VideoTrackInterface> video_track_;
rtc::scoped_refptr<AudioTrackInterface> audio_track_;
bool audio_sender_destroyed_signal_fired_ = false;
@@ -717,8 +714,9 @@
// Setting detailed overrides the default non-screencast mode. This should be
// applied even if the track is set on construction.
video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed);
- video_rtp_sender_ = new VideoRtpSender(stream_->GetVideoTracks()[0],
- stream_->label(), video_channel_);
+ video_rtp_sender_ =
+ new VideoRtpSender(local_stream_->GetVideoTracks()[0],
+ local_stream_->label(), video_channel_);
video_track_->set_enabled(true);
// Sender is not ready to send (no SSRC) so no option should have been set.
diff --git a/pc/webrtcsdp.cc b/pc/webrtcsdp.cc
index b749d6f..13d09a6 100644
--- a/pc/webrtcsdp.cc
+++ b/pc/webrtcsdp.cc
@@ -204,8 +204,6 @@
// RFC 3556
static const char kApplicationSpecificMaximum[] = "AS";
-static const int kDefaultVideoClockrate = 90000;
-
static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
@@ -1737,8 +1735,8 @@
// [/<encodingparameters>]
if (it->id != kWildcardPayloadType) {
InitAttrLine(kAttributeRtpmap, &os);
- os << kSdpDelimiterColon << it->id << " " << it->name
- << "/" << kDefaultVideoClockrate;
+ os << kSdpDelimiterColon << it->id << " " << it->name << "/"
+ << cricket::kVideoCodecClockrate;
AddLine(os.str(), message);
}
AddRtcpFbLines(*it, message);