webrtc/sdk/android/api/org/webrtc/PeerConnection.java
Qingsi Wang e6826d2461 Add configurable connectivity check intervals.
The connectivity check intervals for candidate pairs with strong and
weak connectivity are currently constants in the ICE implementation. A
set of suboptimal value of these constants for a given application may
result in undesirable behavior including excessive network switching
latency. This CL adds these intervals to RTCConfiguration that is
available to applications to configure, while maintaining the original
constants as their default value for compatibility with existing
applications.

Bug: webrtc:8988
Change-Id: I804b0f4cf7881be7d3c8aec2776bc9596de72482
Reviewed-on: https://webrtc-review.googlesource.com/60585
Commit-Queue: Qingsi Wang <qingsi@google.com>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22351}
2018-03-09 08:09:43 +00:00

1093 lines
38 KiB
Java

/*
* Copyright 2013 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.
*/
package org.webrtc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Java-land version of the PeerConnection APIs; wraps the C++ API
* http://www.webrtc.org/reference/native-apis, which in turn is inspired by the
* JS APIs: http://dev.w3.org/2011/webrtc/editor/webrtc.html and
* http://www.w3.org/TR/mediacapture-streams/
*/
@JNINamespace("webrtc::jni")
public class PeerConnection {
/** Tracks PeerConnectionInterface::IceGatheringState */
public enum IceGatheringState {
NEW,
GATHERING,
COMPLETE;
@CalledByNative("IceGatheringState")
static IceGatheringState fromNativeIndex(int nativeIndex) {
return values()[nativeIndex];
}
}
/** Tracks PeerConnectionInterface::IceConnectionState */
public enum IceConnectionState {
NEW,
CHECKING,
CONNECTED,
COMPLETED,
FAILED,
DISCONNECTED,
CLOSED;
@CalledByNative("IceConnectionState")
static IceConnectionState fromNativeIndex(int nativeIndex) {
return values()[nativeIndex];
}
}
/** Tracks PeerConnectionInterface::TlsCertPolicy */
public enum TlsCertPolicy {
TLS_CERT_POLICY_SECURE,
TLS_CERT_POLICY_INSECURE_NO_CHECK,
}
/** Tracks PeerConnectionInterface::SignalingState */
public enum SignalingState {
STABLE,
HAVE_LOCAL_OFFER,
HAVE_LOCAL_PRANSWER,
HAVE_REMOTE_OFFER,
HAVE_REMOTE_PRANSWER,
CLOSED;
@CalledByNative("SignalingState")
static SignalingState fromNativeIndex(int nativeIndex) {
return values()[nativeIndex];
}
}
/** Java version of PeerConnectionObserver. */
public static interface Observer {
/** Triggered when the SignalingState changes. */
@CalledByNative("Observer") void onSignalingChange(SignalingState newState);
/** Triggered when the IceConnectionState changes. */
@CalledByNative("Observer") void onIceConnectionChange(IceConnectionState newState);
/** Triggered when the ICE connection receiving status changes. */
@CalledByNative("Observer") void onIceConnectionReceivingChange(boolean receiving);
/** Triggered when the IceGatheringState changes. */
@CalledByNative("Observer") void onIceGatheringChange(IceGatheringState newState);
/** Triggered when a new ICE candidate has been found. */
@CalledByNative("Observer") void onIceCandidate(IceCandidate candidate);
/** Triggered when some ICE candidates have been removed. */
@CalledByNative("Observer") void onIceCandidatesRemoved(IceCandidate[] candidates);
/** Triggered when media is received on a new stream from remote peer. */
@CalledByNative("Observer") void onAddStream(MediaStream stream);
/** Triggered when a remote peer close a stream. */
@CalledByNative("Observer") void onRemoveStream(MediaStream stream);
/** Triggered when a remote peer opens a DataChannel. */
@CalledByNative("Observer") void onDataChannel(DataChannel dataChannel);
/** Triggered when renegotiation is necessary. */
@CalledByNative("Observer") void onRenegotiationNeeded();
/**
* Triggered when a new track is signaled by the remote peer, as a result of
* setRemoteDescription.
*/
@CalledByNative("Observer") void onAddTrack(RtpReceiver receiver, MediaStream[] mediaStreams);
}
/** Java version of PeerConnectionInterface.IceServer. */
public static class IceServer {
// List of URIs associated with this server. Valid formats are described
// in RFC7064 and RFC7065, and more may be added in the future. The "host"
// part of the URI may contain either an IP address or a hostname.
@Deprecated public final String uri;
public final List<String> urls;
public final String username;
public final String password;
public final TlsCertPolicy tlsCertPolicy;
// If the URIs in |urls| only contain IP addresses, this field can be used
// to indicate the hostname, which may be necessary for TLS (using the SNI
// extension). If |urls| itself contains the hostname, this isn't
// necessary.
public final String hostname;
// List of protocols to be used in the TLS ALPN extension.
public final List<String> tlsAlpnProtocols;
// List of elliptic curves to be used in the TLS elliptic curves extension.
// Only curve names supported by OpenSSL should be used (eg. "P-256","X25519").
public final List<String> tlsEllipticCurves;
/** Convenience constructor for STUN servers. */
@Deprecated
public IceServer(String uri) {
this(uri, "", "");
}
@Deprecated
public IceServer(String uri, String username, String password) {
this(uri, username, password, TlsCertPolicy.TLS_CERT_POLICY_SECURE);
}
@Deprecated
public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy) {
this(uri, username, password, tlsCertPolicy, "");
}
@Deprecated
public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy,
String hostname) {
this(uri, Collections.singletonList(uri), username, password, tlsCertPolicy, hostname, null,
null);
}
private IceServer(String uri, List<String> urls, String username, String password,
TlsCertPolicy tlsCertPolicy, String hostname, List<String> tlsAlpnProtocols,
List<String> tlsEllipticCurves) {
if (uri == null || urls == null || urls.isEmpty()) {
throw new IllegalArgumentException("uri == null || urls == null || urls.isEmpty()");
}
for (String it : urls) {
if (it == null) {
throw new IllegalArgumentException("urls element is null: " + urls);
}
}
if (username == null) {
throw new IllegalArgumentException("username == null");
}
if (password == null) {
throw new IllegalArgumentException("password == null");
}
if (hostname == null) {
throw new IllegalArgumentException("hostname == null");
}
this.uri = uri;
this.urls = urls;
this.username = username;
this.password = password;
this.tlsCertPolicy = tlsCertPolicy;
this.hostname = hostname;
this.tlsAlpnProtocols = tlsAlpnProtocols;
this.tlsEllipticCurves = tlsEllipticCurves;
}
@Override
public String toString() {
return urls + " [" + username + ":" + password + "] [" + tlsCertPolicy + "] [" + hostname
+ "] [" + tlsAlpnProtocols + "] [" + tlsEllipticCurves + "]";
}
public static Builder builder(String uri) {
return new Builder(Collections.singletonList(uri));
}
public static Builder builder(List<String> urls) {
return new Builder(urls);
}
public static class Builder {
private final List<String> urls;
private String username = "";
private String password = "";
private TlsCertPolicy tlsCertPolicy = TlsCertPolicy.TLS_CERT_POLICY_SECURE;
private String hostname = "";
private List<String> tlsAlpnProtocols;
private List<String> tlsEllipticCurves;
private Builder(List<String> urls) {
if (urls == null || urls.isEmpty()) {
throw new IllegalArgumentException("urls == null || urls.isEmpty(): " + urls);
}
this.urls = urls;
}
public Builder setUsername(String username) {
this.username = username;
return this;
}
public Builder setPassword(String password) {
this.password = password;
return this;
}
public Builder setTlsCertPolicy(TlsCertPolicy tlsCertPolicy) {
this.tlsCertPolicy = tlsCertPolicy;
return this;
}
public Builder setHostname(String hostname) {
this.hostname = hostname;
return this;
}
public Builder setTlsAlpnProtocols(List<String> tlsAlpnProtocols) {
this.tlsAlpnProtocols = tlsAlpnProtocols;
return this;
}
public Builder setTlsEllipticCurves(List<String> tlsEllipticCurves) {
this.tlsEllipticCurves = tlsEllipticCurves;
return this;
}
public IceServer createIceServer() {
return new IceServer(urls.get(0), urls, username, password, tlsCertPolicy, hostname,
tlsAlpnProtocols, tlsEllipticCurves);
}
}
@CalledByNative("IceServer")
List<String> getUrls() {
return urls;
}
@CalledByNative("IceServer")
String getUsername() {
return username;
}
@CalledByNative("IceServer")
String getPassword() {
return password;
}
@CalledByNative("IceServer")
TlsCertPolicy getTlsCertPolicy() {
return tlsCertPolicy;
}
@CalledByNative("IceServer")
String getHostname() {
return hostname;
}
@CalledByNative("IceServer")
List<String> getTlsAlpnProtocols() {
return tlsAlpnProtocols;
}
@CalledByNative("IceServer")
List<String> getTlsEllipticCurves() {
return tlsEllipticCurves;
}
}
/** Java version of PeerConnectionInterface.IceTransportsType */
public enum IceTransportsType { NONE, RELAY, NOHOST, ALL }
/** Java version of PeerConnectionInterface.BundlePolicy */
public enum BundlePolicy { BALANCED, MAXBUNDLE, MAXCOMPAT }
/** Java version of PeerConnectionInterface.RtcpMuxPolicy */
public enum RtcpMuxPolicy { NEGOTIATE, REQUIRE }
/** Java version of PeerConnectionInterface.TcpCandidatePolicy */
public enum TcpCandidatePolicy { ENABLED, DISABLED }
/** Java version of PeerConnectionInterface.CandidateNetworkPolicy */
public enum CandidateNetworkPolicy { ALL, LOW_COST }
// Keep in sync with webrtc/rtc_base/network_constants.h.
public enum AdapterType {
UNKNOWN,
ETHERNET,
WIFI,
CELLULAR,
VPN,
LOOPBACK,
}
/** Java version of rtc::KeyType */
public enum KeyType { RSA, ECDSA }
/** Java version of PeerConnectionInterface.ContinualGatheringPolicy */
public enum ContinualGatheringPolicy { GATHER_ONCE, GATHER_CONTINUALLY }
/** Java version of rtc::IntervalRange */
public static class IntervalRange {
private final int min;
private final int max;
public IntervalRange(int min, int max) {
this.min = min;
this.max = max;
}
@CalledByNative("IntervalRange")
public int getMin() {
return min;
}
@CalledByNative("IntervalRange")
public int getMax() {
return max;
}
}
/**
* Java version of webrtc::SdpSemantics.
*
* Configure the SDP semantics used by this PeerConnection. Note that the
* WebRTC 1.0 specification requires UNIFIED_PLAN semantics. The
* RtpTransceiver API is only available with UNIFIED_PLAN semantics.
*
* <p>PLAN_B will cause PeerConnection to create offers and answers with at
* most one audio and one video m= section with multiple RtpSenders and
* RtpReceivers specified as multiple a=ssrc lines within the section. This
* will also cause PeerConnection to ignore all but the first m= section of
* the same media type.
*
* <p>UNIFIED_PLAN will cause PeerConnection to create offers and answers with
* multiple m= sections where each m= section maps to one RtpSender and one
* RtpReceiver (an RtpTransceiver), either both audio or both video. This
* will also cause PeerConnection to ignore all but the first a=ssrc lines
* that form a Plan B stream.
*
* <p>For users who wish to send multiple audio/video streams and need to stay
* interoperable with legacy WebRTC implementations, specify PLAN_B.
*
* <p>For users who wish to send multiple audio/video streams and/or wish to
* use the new RtpTransceiver API, specify UNIFIED_PLAN.
*/
public enum SdpSemantics { PLAN_B, UNIFIED_PLAN }
/** Java version of PeerConnectionInterface.RTCConfiguration */
// TODO(qingsi): Resolve the naming inconsistency of fields with/without units.
public static class RTCConfiguration {
public IceTransportsType iceTransportsType;
public List<IceServer> iceServers;
public BundlePolicy bundlePolicy;
public RtcpMuxPolicy rtcpMuxPolicy;
public TcpCandidatePolicy tcpCandidatePolicy;
public CandidateNetworkPolicy candidateNetworkPolicy;
public int audioJitterBufferMaxPackets;
public boolean audioJitterBufferFastAccelerate;
public int iceConnectionReceivingTimeout;
public int iceBackupCandidatePairPingInterval;
public KeyType keyType;
public ContinualGatheringPolicy continualGatheringPolicy;
public int iceCandidatePoolSize;
public boolean pruneTurnPorts;
public boolean presumeWritableWhenFullyRelayed;
// The following fields define intervals in milliseconds at which ICE
// connectivity checks are sent.
//
// We consider ICE is "strongly connected" for an agent when there is at
// least one candidate pair that currently succeeds in connectivity check
// from its direction i.e. sending a ping and receives a ping response, AND
// all candidate pairs have sent a minimum number of pings for connectivity
// (this number is implementation-specific). Otherwise, ICE is considered in
// "weak connectivity".
//
// Note that the above notion of strong and weak connectivity is not defined
// in RFC 5245, and they apply to our current ICE implementation only.
//
// 1) iceCheckIntervalStrongConnectivityMs defines the interval applied to
// ALL candidate pairs when ICE is strongly connected,
// 2) iceCheckIntervalWeakConnectivityMs defines the counterpart for ALL
// pairs when ICE is weakly connected, and
// 3) iceCheckMinInterval defines the minimal interval (equivalently the
// maximum rate) that overrides the above two intervals when either of them
// is less.
public Integer iceCheckIntervalStrongConnectivityMs;
public Integer iceCheckIntervalWeakConnectivityMs;
public Integer iceCheckMinInterval;
// The interval in milliseconds at which STUN candidates will resend STUN binding requests
// to keep NAT bindings open.
// The default value in the implementation is used if this field is null.
public Integer stunCandidateKeepaliveIntervalMs;
public boolean disableIPv6OnWifi;
// By default, PeerConnection will use a limited number of IPv6 network
// interfaces, in order to avoid too many ICE candidate pairs being created
// and delaying ICE completion.
//
// Can be set to Integer.MAX_VALUE to effectively disable the limit.
public int maxIPv6Networks;
public IntervalRange iceRegatherIntervalRange;
// These values will be overridden by MediaStream constraints if deprecated constraints-based
// create peerconnection interface is used.
public boolean disableIpv6;
public boolean enableDscp;
public boolean enableCpuOveruseDetection;
public boolean enableRtpDataChannel;
public boolean suspendBelowMinBitrate;
public Integer screencastMinBitrate;
public Boolean combinedAudioVideoBwe;
public Boolean enableDtlsSrtp;
// Use "Unknown" to represent no preference of adapter types, not the
// preference of adapters of unknown types.
public AdapterType networkPreference;
public SdpSemantics sdpSemantics;
// This is an optional wrapper for the C++ webrtc::TurnCustomizer.
public TurnCustomizer turnCustomizer;
// TODO(deadbeef): Instead of duplicating the defaults here, we should do
// something to pick up the defaults from C++. The Objective-C equivalent
// of RTCConfiguration does that.
public RTCConfiguration(List<IceServer> iceServers) {
iceTransportsType = IceTransportsType.ALL;
bundlePolicy = BundlePolicy.BALANCED;
rtcpMuxPolicy = RtcpMuxPolicy.REQUIRE;
tcpCandidatePolicy = TcpCandidatePolicy.ENABLED;
candidateNetworkPolicy = CandidateNetworkPolicy.ALL;
this.iceServers = iceServers;
audioJitterBufferMaxPackets = 50;
audioJitterBufferFastAccelerate = false;
iceConnectionReceivingTimeout = -1;
iceBackupCandidatePairPingInterval = -1;
keyType = KeyType.ECDSA;
continualGatheringPolicy = ContinualGatheringPolicy.GATHER_ONCE;
iceCandidatePoolSize = 0;
pruneTurnPorts = false;
presumeWritableWhenFullyRelayed = false;
iceCheckIntervalStrongConnectivityMs = null;
iceCheckIntervalWeakConnectivityMs = null;
iceCheckMinInterval = null;
stunCandidateKeepaliveIntervalMs = null;
disableIPv6OnWifi = false;
maxIPv6Networks = 5;
iceRegatherIntervalRange = null;
disableIpv6 = false;
enableDscp = false;
enableCpuOveruseDetection = true;
enableRtpDataChannel = false;
suspendBelowMinBitrate = false;
screencastMinBitrate = null;
combinedAudioVideoBwe = null;
enableDtlsSrtp = null;
networkPreference = AdapterType.UNKNOWN;
sdpSemantics = SdpSemantics.PLAN_B;
}
@CalledByNative("RTCConfiguration")
IceTransportsType getIceTransportsType() {
return iceTransportsType;
}
@CalledByNative("RTCConfiguration")
List<IceServer> getIceServers() {
return iceServers;
}
@CalledByNative("RTCConfiguration")
BundlePolicy getBundlePolicy() {
return bundlePolicy;
}
@CalledByNative("RTCConfiguration")
RtcpMuxPolicy getRtcpMuxPolicy() {
return rtcpMuxPolicy;
}
@CalledByNative("RTCConfiguration")
TcpCandidatePolicy getTcpCandidatePolicy() {
return tcpCandidatePolicy;
}
@CalledByNative("RTCConfiguration")
CandidateNetworkPolicy getCandidateNetworkPolicy() {
return candidateNetworkPolicy;
}
@CalledByNative("RTCConfiguration")
int getAudioJitterBufferMaxPackets() {
return audioJitterBufferMaxPackets;
}
@CalledByNative("RTCConfiguration")
boolean getAudioJitterBufferFastAccelerate() {
return audioJitterBufferFastAccelerate;
}
@CalledByNative("RTCConfiguration")
int getIceConnectionReceivingTimeout() {
return iceConnectionReceivingTimeout;
}
@CalledByNative("RTCConfiguration")
int getIceBackupCandidatePairPingInterval() {
return iceBackupCandidatePairPingInterval;
}
@CalledByNative("RTCConfiguration")
KeyType getKeyType() {
return keyType;
}
@CalledByNative("RTCConfiguration")
ContinualGatheringPolicy getContinualGatheringPolicy() {
return continualGatheringPolicy;
}
@CalledByNative("RTCConfiguration")
int getIceCandidatePoolSize() {
return iceCandidatePoolSize;
}
@CalledByNative("RTCConfiguration")
boolean getPruneTurnPorts() {
return pruneTurnPorts;
}
@CalledByNative("RTCConfiguration")
boolean getPresumeWritableWhenFullyRelayed() {
return presumeWritableWhenFullyRelayed;
}
@CalledByNative("RTCConfiguration")
Integer getIceCheckIntervalStrongConnectivity() {
return iceCheckIntervalStrongConnectivityMs;
}
@CalledByNative("RTCConfiguration")
Integer getIceCheckIntervalWeakConnectivity() {
return iceCheckIntervalWeakConnectivityMs;
}
@CalledByNative("RTCConfiguration")
Integer getIceCheckMinInterval() {
return iceCheckMinInterval;
}
@CalledByNative("RTCConfiguration")
Integer getStunCandidateKeepaliveInterval() {
return stunCandidateKeepaliveIntervalMs;
}
@CalledByNative("RTCConfiguration")
boolean getDisableIPv6OnWifi() {
return disableIPv6OnWifi;
}
@CalledByNative("RTCConfiguration")
int getMaxIPv6Networks() {
return maxIPv6Networks;
}
@CalledByNative("RTCConfiguration")
IntervalRange getIceRegatherIntervalRange() {
return iceRegatherIntervalRange;
}
@CalledByNative("RTCConfiguration")
TurnCustomizer getTurnCustomizer() {
return turnCustomizer;
}
@CalledByNative("RTCConfiguration")
boolean getDisableIpv6() {
return disableIpv6;
}
@CalledByNative("RTCConfiguration")
boolean getEnableDscp() {
return enableDscp;
}
@CalledByNative("RTCConfiguration")
boolean getEnableCpuOveruseDetection() {
return enableCpuOveruseDetection;
}
@CalledByNative("RTCConfiguration")
boolean getEnableRtpDataChannel() {
return enableRtpDataChannel;
}
@CalledByNative("RTCConfiguration")
boolean getSuspendBelowMinBitrate() {
return suspendBelowMinBitrate;
}
@CalledByNative("RTCConfiguration")
Integer getScreencastMinBitrate() {
return screencastMinBitrate;
}
@CalledByNative("RTCConfiguration")
Boolean getCombinedAudioVideoBwe() {
return combinedAudioVideoBwe;
}
@CalledByNative("RTCConfiguration")
Boolean getEnableDtlsSrtp() {
return enableDtlsSrtp;
}
@CalledByNative("RTCConfiguration")
AdapterType getNetworkPreference() {
return networkPreference;
}
@CalledByNative("RTCConfiguration")
SdpSemantics getSdpSemantics() {
return sdpSemantics;
}
};
private final List<MediaStream> localStreams = new ArrayList<>();
private final long nativePeerConnection;
private List<RtpSender> senders = new ArrayList<>();
private List<RtpReceiver> receivers = new ArrayList<>();
private List<RtpTransceiver> transceivers = new ArrayList<>();
/**
* Wraps a PeerConnection created by the factory. Can be used by clients that want to implement
* their PeerConnection creation in JNI.
*/
public PeerConnection(NativePeerConnectionFactory factory) {
this(factory.createNativePeerConnection());
}
PeerConnection(long nativePeerConnection) {
this.nativePeerConnection = nativePeerConnection;
}
// JsepInterface.
public SessionDescription getLocalDescription() {
return nativeGetLocalDescription();
}
public SessionDescription getRemoteDescription() {
return nativeGetRemoteDescription();
}
public DataChannel createDataChannel(String label, DataChannel.Init init) {
return nativeCreateDataChannel(label, init);
}
public void createOffer(SdpObserver observer, MediaConstraints constraints) {
nativeCreateOffer(observer, constraints);
}
public void createAnswer(SdpObserver observer, MediaConstraints constraints) {
nativeCreateAnswer(observer, constraints);
}
public void setLocalDescription(SdpObserver observer, SessionDescription sdp) {
nativeSetLocalDescription(observer, sdp);
}
public void setRemoteDescription(SdpObserver observer, SessionDescription sdp) {
nativeSetRemoteDescription(observer, sdp);
}
/**
* Enables/disables playout of received audio streams. Enabled by default.
*
* Note that even if playout is enabled, streams will only be played out if
* the appropriate SDP is also applied. The main purpose of this API is to
* be able to control the exact time when audio playout starts.
*/
public void setAudioPlayout(boolean playout) {
nativeSetAudioPlayout(playout);
}
/**
* Enables/disables recording of transmitted audio streams. Enabled by default.
*
* Note that even if recording is enabled, streams will only be recorded if
* the appropriate SDP is also applied. The main purpose of this API is to
* be able to control the exact time when audio recording starts.
*/
public void setAudioRecording(boolean recording) {
nativeSetAudioRecording(recording);
}
public boolean setConfiguration(RTCConfiguration config) {
return nativeSetConfiguration(config);
}
public boolean addIceCandidate(IceCandidate candidate) {
return nativeAddIceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
}
public boolean removeIceCandidates(final IceCandidate[] candidates) {
return nativeRemoveIceCandidates(candidates);
}
/**
* Adds a new MediaStream to be sent on this peer connection.
* Note: This method is not supported with SdpSemantics.UNIFIED_PLAN. Please
* use addTrack instead.
*/
public boolean addStream(MediaStream stream) {
boolean ret = nativeAddLocalStream(stream.nativeStream);
if (!ret) {
return false;
}
localStreams.add(stream);
return true;
}
/**
* Removes the given media stream from this peer connection.
* This method is not supported with SdpSemantics.UNIFIED_PLAN. Please use
* removeTrack instead.
*/
public void removeStream(MediaStream stream) {
nativeRemoveLocalStream(stream.nativeStream);
localStreams.remove(stream);
}
/**
* Creates an RtpSender without a track.
*
* <p>This method allows an application to cause the PeerConnection to negotiate
* sending/receiving a specific media type, but without having a track to
* send yet.
*
* <p>When the application does want to begin sending a track, it can call
* RtpSender.setTrack, which doesn't require any additional SDP negotiation.
*
* <p>Example use:
* <pre>
* {@code
* audioSender = pc.createSender("audio", "stream1");
* videoSender = pc.createSender("video", "stream1");
* // Do normal SDP offer/answer, which will kick off ICE/DTLS and negotiate
* // media parameters....
* // Later, when the endpoint is ready to actually begin sending:
* audioSender.setTrack(audioTrack, false);
* videoSender.setTrack(videoTrack, false);
* }
* </pre>
* <p>Note: This corresponds most closely to "addTransceiver" in the official
* WebRTC API, in that it creates a sender without a track. It was
* implemented before addTransceiver because it provides useful
* functionality, and properly implementing transceivers would have required
* a great deal more work.
*
* <p>Note: This is only available with SdpSemantics.PLAN_B specified. Please use
* addTransceiver instead.
*
* @param kind Corresponds to MediaStreamTrack kinds (must be "audio" or
* "video").
* @param stream_id The ID of the MediaStream that this sender's track will
* be associated with when SDP is applied to the remote
* PeerConnection. If createSender is used to create an
* audio and video sender that should be synchronized, they
* should use the same stream ID.
* @return A new RtpSender object if successful, or null otherwise.
*/
public RtpSender createSender(String kind, String stream_id) {
RtpSender newSender = nativeCreateSender(kind, stream_id);
if (newSender != null) {
senders.add(newSender);
}
return newSender;
}
/**
* Gets all RtpSenders associated with this peer connection.
* Note that calling getSenders will dispose of the senders previously
* returned.
*/
public List<RtpSender> getSenders() {
for (RtpSender sender : senders) {
sender.dispose();
}
senders = nativeGetSenders();
return Collections.unmodifiableList(senders);
}
/**
* Gets all RtpReceivers associated with this peer connection.
* Note that calling getReceivers will dispose of the receivers previously
* returned.
*/
public List<RtpReceiver> getReceivers() {
for (RtpReceiver receiver : receivers) {
receiver.dispose();
}
receivers = nativeGetReceivers();
return Collections.unmodifiableList(receivers);
}
/**
* Gets all RtpTransceivers associated with this peer connection.
* Note that calling getTransceivers will dispose of the transceivers previously
* returned.
* Note: This is only available with SdpSemantics.UNIFIED_PLAN specified.
*/
public List<RtpTransceiver> getTransceivers() {
for (RtpTransceiver transceiver : transceivers) {
transceiver.dispose();
}
transceivers = nativeGetTransceivers();
return Collections.unmodifiableList(transceivers);
}
/**
* Adds a new media stream track to be sent on this peer connection, and returns
* the newly created RtpSender. If streamIds are specified, the RtpSender will
* be associated with the streams specified in the streamIds list.
*
* @throws IllegalStateException if an error accors in C++ addTrack.
* An error can occur if:
* - A sender already exists for the track.
* - The peer connection is closed.
*/
public RtpSender addTrack(MediaStreamTrack track) {
return addTrack(track, Collections.emptyList());
}
public RtpSender addTrack(MediaStreamTrack track, List<String> streamIds) {
if (track == null || streamIds == null) {
throw new NullPointerException("No MediaStreamTrack specified in addTrack.");
}
RtpSender newSender = nativeAddTrack(track.nativeTrack, streamIds);
if (newSender == null) {
throw new IllegalStateException("C++ addTrack failed.");
}
senders.add(newSender);
return newSender;
}
/**
* Stops sending media from sender. The sender will still appear in getSenders. Future
* calls to createOffer will mark the m section for the corresponding transceiver as
* receive only or inactive, as defined in JSEP. Returns true on success.
*/
public boolean removeTrack(RtpSender sender) {
if (sender == null) {
throw new NullPointerException("No RtpSender specified for removeTrack.");
}
return nativeRemoveTrack(sender.nativeRtpSender);
}
/**
* Creates a new RtpTransceiver and adds it to the set of transceivers. Adding a
* transceiver will cause future calls to CreateOffer to add a media description
* for the corresponding transceiver.
*
* <p>The initial value of |mid| in the returned transceiver is null. Setting a
* new session description may change it to a non-null value.
*
* <p>https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver
*
* <p>If a MediaStreamTrack is specified then a transceiver will be added with a
* sender set to transmit the given track. The kind
* of the transceiver (and sender/receiver) will be derived from the kind of
* the track.
*
* <p>If MediaType is specified then a transceiver will be added based upon that type.
* This can be either MEDIA_TYPE_AUDIO or MEDIA_TYPE_VIDEO.
*
* <p>Optionally, an RtpTransceiverInit structure can be specified to configure
* the transceiver from construction. If not specified, the transceiver will
* default to having a direction of kSendRecv and not be part of any streams.
*
* <p>Note: These methods are only available with SdpSemantics.UNIFIED_PLAN specified.
* @throws IllegalStateException if an error accors in C++ addTransceiver
*/
public RtpTransceiver addTransceiver(MediaStreamTrack track) {
return addTransceiver(track, new RtpTransceiver.RtpTransceiverInit());
}
public RtpTransceiver addTransceiver(
MediaStreamTrack track, RtpTransceiver.RtpTransceiverInit init) {
if (track == null) {
throw new NullPointerException("No MediaStreamTrack specified for addTransceiver.");
}
if (init == null) {
init = new RtpTransceiver.RtpTransceiverInit();
}
RtpTransceiver newTransceiver = nativeAddTransceiverWithTrack(track.nativeTrack, init);
if (newTransceiver == null) {
throw new IllegalStateException("C++ addTransceiver failed.");
}
transceivers.add(newTransceiver);
return newTransceiver;
}
public RtpTransceiver addTransceiver(MediaStreamTrack.MediaType mediaType) {
return addTransceiver(mediaType, new RtpTransceiver.RtpTransceiverInit());
}
public RtpTransceiver addTransceiver(
MediaStreamTrack.MediaType mediaType, RtpTransceiver.RtpTransceiverInit init) {
if (mediaType == null) {
throw new NullPointerException("No MediaType specified for addTransceiver.");
}
if (init == null) {
init = new RtpTransceiver.RtpTransceiverInit();
}
RtpTransceiver newTransceiver = nativeAddTransceiverOfType(mediaType, init);
if (newTransceiver == null) {
throw new IllegalStateException("C++ addTransceiver failed.");
}
transceivers.add(newTransceiver);
return newTransceiver;
}
// Older, non-standard implementation of getStats.
@Deprecated
public boolean getStats(StatsObserver observer, MediaStreamTrack track) {
return nativeOldGetStats(observer, (track == null) ? 0 : track.nativeTrack);
}
/**
* Gets stats using the new stats collection API, see webrtc/api/stats/. These
* will replace old stats collection API when the new API has matured enough.
*/
public void getStats(RTCStatsCollectorCallback callback) {
nativeNewGetStats(callback);
}
/**
* Limits the bandwidth allocated for all RTP streams sent by this
* PeerConnection. Pass null to leave a value unchanged.
*/
public boolean setBitrate(Integer min, Integer current, Integer max) {
return nativeSetBitrate(min, current, max);
}
/**
* Starts recording an RTC event log.
*
* Ownership of the file is transfered to the native code. If an RTC event
* log is already being recorded, it will be stopped and a new one will start
* using the provided file. Logging will continue until the stopRtcEventLog
* function is called. The max_size_bytes argument is ignored, it is added
* for future use.
*/
public boolean startRtcEventLog(int file_descriptor, int max_size_bytes) {
return nativeStartRtcEventLog(file_descriptor, max_size_bytes);
}
/**
* Stops recording an RTC event log. If no RTC event log is currently being
* recorded, this call will have no effect.
*/
public void stopRtcEventLog() {
nativeStopRtcEventLog();
}
// TODO(fischman): add support for DTMF-related methods once that API
// stabilizes.
public SignalingState signalingState() {
return nativeSignalingState();
}
public IceConnectionState iceConnectionState() {
return nativeIceConnectionState();
}
public IceGatheringState iceGatheringState() {
return nativeIceGatheringState();
}
public void close() {
nativeClose();
}
/**
* Free native resources associated with this PeerConnection instance.
*
* This method removes a reference count from the C++ PeerConnection object,
* which should result in it being destroyed. It also calls equivalent
* "dispose" methods on the Java objects attached to this PeerConnection
* (streams, senders, receivers), such that their associated C++ objects
* will also be destroyed.
*
* <p>Note that this method cannot be safely called from an observer callback
* (PeerConnection.Observer, DataChannel.Observer, etc.). If you want to, for
* example, destroy the PeerConnection after an "ICE failed" callback, you
* must do this asynchronously (in other words, unwind the stack first). See
* <a href="https://bugs.chromium.org/p/webrtc/issues/detail?id=3721">bug
* 3721</a> for more details.
*/
public void dispose() {
close();
for (MediaStream stream : localStreams) {
nativeRemoveLocalStream(stream.nativeStream);
stream.dispose();
}
localStreams.clear();
for (RtpSender sender : senders) {
sender.dispose();
}
senders.clear();
for (RtpReceiver receiver : receivers) {
receiver.dispose();
}
for (RtpTransceiver transceiver : transceivers) {
transceiver.dispose();
}
transceivers.clear();
receivers.clear();
nativeFreeOwnedPeerConnection(nativePeerConnection);
}
/** Returns a pointer to the native webrtc::PeerConnectionInterface. */
public long getNativePeerConnection() {
return nativeGetNativePeerConnection();
}
@CalledByNative
long getNativeOwnedPeerConnection() {
return nativePeerConnection;
}
public static long createNativePeerConnectionObserver(Observer observer) {
return nativeCreatePeerConnectionObserver(observer);
}
private native long nativeGetNativePeerConnection();
private native SessionDescription nativeGetLocalDescription();
private native SessionDescription nativeGetRemoteDescription();
private native DataChannel nativeCreateDataChannel(String label, DataChannel.Init init);
private native void nativeCreateOffer(SdpObserver observer, MediaConstraints constraints);
private native void nativeCreateAnswer(SdpObserver observer, MediaConstraints constraints);
private native void nativeSetLocalDescription(SdpObserver observer, SessionDescription sdp);
private native void nativeSetRemoteDescription(SdpObserver observer, SessionDescription sdp);
private native void nativeSetAudioPlayout(boolean playout);
private native void nativeSetAudioRecording(boolean recording);
private native boolean nativeSetBitrate(Integer min, Integer current, Integer max);
private native SignalingState nativeSignalingState();
private native IceConnectionState nativeIceConnectionState();
private native IceGatheringState nativeIceGatheringState();
private native void nativeClose();
private static native long nativeCreatePeerConnectionObserver(Observer observer);
private static native void nativeFreeOwnedPeerConnection(long ownedPeerConnection);
private native boolean nativeSetConfiguration(RTCConfiguration config);
private native boolean nativeAddIceCandidate(
String sdpMid, int sdpMLineIndex, String iceCandidateSdp);
private native boolean nativeRemoveIceCandidates(final IceCandidate[] candidates);
private native boolean nativeAddLocalStream(long stream);
private native void nativeRemoveLocalStream(long stream);
private native boolean nativeOldGetStats(StatsObserver observer, long nativeTrack);
private native void nativeNewGetStats(RTCStatsCollectorCallback callback);
private native RtpSender nativeCreateSender(String kind, String stream_id);
private native List<RtpSender> nativeGetSenders();
private native List<RtpReceiver> nativeGetReceivers();
private native List<RtpTransceiver> nativeGetTransceivers();
private native RtpSender nativeAddTrack(long track, List<String> streamIds);
private native boolean nativeRemoveTrack(long sender);
private native RtpTransceiver nativeAddTransceiverWithTrack(
long track, RtpTransceiver.RtpTransceiverInit init);
private native RtpTransceiver nativeAddTransceiverOfType(
MediaStreamTrack.MediaType mediaType, RtpTransceiver.RtpTransceiverInit init);
private native boolean nativeStartRtcEventLog(int file_descriptor, int max_size_bytes);
private native void nativeStopRtcEventLog();
}