From 610c454cf9287a7f4ee9b2fbc56552f93dece09c Mon Sep 17 00:00:00 2001 From: hekra01 Date: Fri, 18 Nov 2016 00:10:57 -0800 Subject: [PATCH] Add Datachannel support to Android AppRTCMobile BUG=webrtc:6647 Review-Url: https://codereview.webrtc.org/2464243002 Cr-Commit-Position: refs/heads/master@{#15145} --- AUTHORS | 1 + .../androidapp/res/values/strings.xml | 38 +++++++++ .../androidapp/res/xml/preferences.xml | 48 +++++++++++ .../src/org/appspot/apprtc/CallActivity.java | 19 ++++- .../org/appspot/apprtc/ConnectActivity.java | 66 +++++++++++++++ .../appspot/apprtc/PeerConnectionClient.java | 84 ++++++++++++++++++- .../org/appspot/apprtc/SettingsActivity.java | 49 ++++++++++- 7 files changed, 300 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 89606dea37..51a48fb47a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,6 +14,7 @@ Colin Plumb Eric Rescorla, RTFM Inc. Giji Gangadharan Graham Yoakum +Hugues Ekra Jake Hilton James H. Brown Jiawei Ou diff --git a/webrtc/examples/androidapp/res/values/strings.xml b/webrtc/examples/androidapp/res/values/strings.xml index ef030bde12..8313526ef6 100644 --- a/webrtc/examples/androidapp/res/values/strings.xml +++ b/webrtc/examples/androidapp/res/values/strings.xml @@ -152,6 +152,44 @@ Speakerphone. auto + data_settings_key + WebRTC data channel settings. + + enable_datachannel_preference + Enable datachannel. + Enable datachannel. + true + + ordered_preference + Order messages. + Order messages. + true + + Subprotocol + Subprotocol. + Enter subprotocol. + + + negotiated_preference + Negotiated. + Negotiated. + false + + max_retransmit_time_ms_preference + Max delay to retransmit. + Enter max delay to retransmit (in ms). + -1 + + max_retransmits_preference + Max attempts to retransmit. + Enter max attempts to retransmit. + -1 + + data_id_preference + Data id. + Enter data channel id. + -1 + misc_settings_key Miscellaneous settings. diff --git a/webrtc/examples/androidapp/res/xml/preferences.xml b/webrtc/examples/androidapp/res/xml/preferences.xml index 35a6e6ef4e..0fe9abe2f0 100644 --- a/webrtc/examples/androidapp/res/xml/preferences.xml +++ b/webrtc/examples/androidapp/res/xml/preferences.xml @@ -155,6 +155,54 @@ android:entryValues="@array/speakerphoneValues" /> + + + + + + + + + + + + + + + + + diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java index ed31ba1ab3..d851843bf2 100644 --- a/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java +++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java @@ -36,6 +36,7 @@ import java.util.List; import org.appspot.apprtc.AppRTCClient.RoomConnectionParameters; import org.appspot.apprtc.AppRTCClient.SignalingParameters; import org.appspot.apprtc.PeerConnectionClient.PeerConnectionParameters; +import org.appspot.apprtc.PeerConnectionClient.DataChannelParameters; import org.webrtc.Camera1Enumerator; import org.webrtc.Camera2Enumerator; import org.webrtc.CameraEnumerator; @@ -97,6 +98,14 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT"; public static final String EXTRA_USE_VALUES_FROM_INTENT = "org.appspot.apprtc.USE_VALUES_FROM_INTENT"; + public static final String EXTRA_DATA_CHANNEL_ENABLED = "org.appspot.apprtc.DATA_CHANNEL_ENABLED"; + public static final String EXTRA_ORDERED = "org.appspot.apprtc.ORDERED"; + public static final String EXTRA_MAX_RETRANSMITS_MS = "org.appspot.apprtc.MAX_RETRANSMITS_MS"; + public static final String EXTRA_MAX_RETRANSMITS = "org.appspot.apprtc.MAX_RETRANSMITS"; + public static final String EXTRA_PROTOCOL = "org.appspot.apprtc.PROTOCOL"; + public static final String EXTRA_NEGOTIATED = "org.appspot.apprtc.NEGOTIATED"; + public static final String EXTRA_ID = "org.appspot.apprtc.ID"; + private static final String TAG = "CallRTCClient"; private static final int CAPTURE_PERMISSION_REQUEST_CODE = 1; @@ -264,7 +273,13 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven videoWidth = displayMetrics.widthPixels; videoHeight = displayMetrics.heightPixels; } - + DataChannelParameters dataChannelParameters = null; + if (intent.getBooleanExtra(EXTRA_DATA_CHANNEL_ENABLED, true)) { + dataChannelParameters = new DataChannelParameters(intent.getBooleanExtra(EXTRA_ORDERED, true), + intent.getIntExtra(EXTRA_MAX_RETRANSMITS_MS, -1), + intent.getIntExtra(EXTRA_MAX_RETRANSMITS, -1), intent.getStringExtra(EXTRA_PROTOCOL), + intent.getBooleanExtra(EXTRA_NEGOTIATED, false), intent.getIntExtra(EXTRA_ID, -1)); + } peerConnectionParameters = new PeerConnectionParameters(intent.getBooleanExtra(EXTRA_VIDEO_CALL, true), loopback, tracing, videoWidth, videoHeight, intent.getIntExtra(EXTRA_VIDEO_FPS, 0), @@ -277,7 +292,7 @@ public class CallActivity extends Activity implements AppRTCClient.SignalingEven intent.getBooleanExtra(EXTRA_DISABLE_BUILT_IN_AEC, false), intent.getBooleanExtra(EXTRA_DISABLE_BUILT_IN_AGC, false), intent.getBooleanExtra(EXTRA_DISABLE_BUILT_IN_NS, false), - intent.getBooleanExtra(EXTRA_ENABLE_LEVEL_CONTROL, false)); + intent.getBooleanExtra(EXTRA_ENABLE_LEVEL_CONTROL, false), dataChannelParameters); commandLineRun = intent.getBooleanExtra(EXTRA_CMDLINE, false); runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0); diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java index 8d1668894d..4514503d7d 100644 --- a/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java +++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java @@ -80,6 +80,13 @@ public class ConnectActivity extends Activity { private String keyprefRoomList; private ArrayList roomList; private ArrayAdapter adapter; + private String keyprefEnableDataChannel; + private String keyprefOrdered; + private String keyprefMaxRetransmitTimeMs; + private String keyprefMaxRetransmits; + private String keyprefDataProtocol; + private String keyprefNegotiated; + private String keyprefDataId; @Override public void onCreate(Bundle savedInstanceState) { @@ -114,6 +121,13 @@ public class ConnectActivity extends Activity { keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key); keyprefRoom = getString(R.string.pref_room_key); keyprefRoomList = getString(R.string.pref_room_list_key); + keyprefEnableDataChannel = getString(R.string.pref_enable_datachannel_key); + keyprefOrdered = getString(R.string.pref_ordered_key); + keyprefMaxRetransmitTimeMs = getString(R.string.pref_max_retransmit_time_ms_key); + keyprefMaxRetransmits = getString(R.string.pref_max_retransmits_key); + keyprefDataProtocol = getString(R.string.pref_data_protocol_key); + keyprefNegotiated = getString(R.string.pref_negotiated_key); + keyprefDataId = getString(R.string.pref_data_id_key); setContentView(R.layout.activity_connect); @@ -279,6 +293,28 @@ public class ConnectActivity extends Activity { } } + /** + * Get a value from the shared preference or from the intent, if it does not + * exist the default is used. + */ + private int sharedPrefGetInteger( + int attributeId, String intentName, int defaultId, boolean useFromIntent) { + String defaultString = getString(defaultId); + int defaultValue = Integer.parseInt(defaultString); + if (useFromIntent) { + return getIntent().getIntExtra(intentName, defaultValue); + } else { + String attributeName = getString(attributeId); + String value = sharedPref.getString(attributeName, defaultString); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + Log.e(TAG, "Wrong setting for: " + attributeName + ":" + value); + return defaultValue; + } + } + } + private void connectToRoom(String roomId, boolean commandLineRun, boolean loopback, boolean useValuesFromIntent, int runTimeMs) { this.commandLineRun = commandLineRun; @@ -433,6 +469,25 @@ public class ConnectActivity extends Activity { boolean tracing = sharedPrefGetBoolean(R.string.pref_tracing_key, CallActivity.EXTRA_TRACING, R.string.pref_tracing_default, useValuesFromIntent); + // Get datachannel options + boolean dataChannelEnabled = sharedPrefGetBoolean(R.string.pref_enable_datachannel_key, + CallActivity.EXTRA_DATA_CHANNEL_ENABLED, R.string.pref_enable_datachannel_default, + useValuesFromIntent); + boolean ordered = sharedPrefGetBoolean(R.string.pref_ordered_key, CallActivity.EXTRA_ORDERED, + R.string.pref_ordered_default, useValuesFromIntent); + boolean negotiated = sharedPrefGetBoolean(R.string.pref_negotiated_key, + CallActivity.EXTRA_NEGOTIATED, R.string.pref_negotiated_default, useValuesFromIntent); + int maxRetrMs = sharedPrefGetInteger(R.string.pref_max_retransmit_time_ms_key, + CallActivity.EXTRA_MAX_RETRANSMITS_MS, R.string.pref_max_retransmit_time_ms_default, + useValuesFromIntent); + int maxRetr = + sharedPrefGetInteger(R.string.pref_max_retransmits_key, CallActivity.EXTRA_MAX_RETRANSMITS, + R.string.pref_max_retransmits_default, useValuesFromIntent); + int id = sharedPrefGetInteger(R.string.pref_data_id_key, CallActivity.EXTRA_ID, + R.string.pref_data_id_default, useValuesFromIntent); + String protocol = sharedPrefGetString(R.string.pref_data_protocol_key, + CallActivity.EXTRA_PROTOCOL, R.string.pref_data_protocol_default, useValuesFromIntent); + // Start AppRTCMobile activity. Log.d(TAG, "Connecting to room " + roomId + " at URL " + roomUrl); if (validateUrl(roomUrl)) { @@ -466,6 +521,17 @@ public class ConnectActivity extends Activity { intent.putExtra(CallActivity.EXTRA_CMDLINE, commandLineRun); intent.putExtra(CallActivity.EXTRA_RUNTIME, runTimeMs); + intent.putExtra(CallActivity.EXTRA_DATA_CHANNEL_ENABLED, dataChannelEnabled); + + if (dataChannelEnabled) { + intent.putExtra(CallActivity.EXTRA_ORDERED, ordered); + intent.putExtra(CallActivity.EXTRA_MAX_RETRANSMITS_MS, maxRetrMs); + intent.putExtra(CallActivity.EXTRA_MAX_RETRANSMITS, maxRetr); + intent.putExtra(CallActivity.EXTRA_PROTOCOL, protocol); + intent.putExtra(CallActivity.EXTRA_NEGOTIATED, negotiated); + intent.putExtra(CallActivity.EXTRA_ID, id); + } + if (useValuesFromIntent) { if (getIntent().hasExtra(CallActivity.EXTRA_VIDEO_FILE_AS_CAMERA)) { String videoFileAsCamera = diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java index a26c86d039..1a650ff264 100644 --- a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java @@ -16,6 +16,7 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Collections; import java.util.EnumSet; import java.util.LinkedList; @@ -126,6 +127,30 @@ public class PeerConnectionClient { // enableAudio is set to true if audio should be sent. private boolean enableAudio; private AudioTrack localAudioTrack; + private DataChannel dataChannel; + private boolean dataChannelEnabled; + + /** + * Peer connection parameters. + */ + public static class DataChannelParameters { + public final boolean ordered; + public final int maxRetransmitTimeMs; + public final int maxRetransmits; + public final String protocol; + public final boolean negotiated; + public final int id; + + public DataChannelParameters(boolean ordered, int maxRetransmitTimeMs, int maxRetransmits, + String protocol, boolean negotiated, int id) { + this.ordered = ordered; + this.maxRetransmitTimeMs = maxRetransmitTimeMs; + this.maxRetransmits = maxRetransmits; + this.protocol = protocol; + this.negotiated = negotiated; + this.id = id; + } + } /** * Peer connection parameters. @@ -149,12 +174,25 @@ public class PeerConnectionClient { public final boolean disableBuiltInAGC; public final boolean disableBuiltInNS; public final boolean enableLevelControl; + private final DataChannelParameters dataChannelParameters; public PeerConnectionParameters(boolean videoCallEnabled, boolean loopback, boolean tracing, int videoWidth, int videoHeight, int videoFps, int videoMaxBitrate, String videoCodec, boolean videoCodecHwAcceleration, int audioStartBitrate, String audioCodec, boolean noAudioProcessing, boolean aecDump, boolean useOpenSLES, boolean disableBuiltInAEC, boolean disableBuiltInAGC, boolean disableBuiltInNS, boolean enableLevelControl) { + this(videoCallEnabled, loopback, tracing, videoWidth, videoHeight, videoFps, videoMaxBitrate, + videoCodec, videoCodecHwAcceleration, audioStartBitrate, audioCodec, noAudioProcessing, + aecDump, useOpenSLES, disableBuiltInAEC, disableBuiltInAGC, disableBuiltInNS, + enableLevelControl, null); + } + + public PeerConnectionParameters(boolean videoCallEnabled, boolean loopback, boolean tracing, + int videoWidth, int videoHeight, int videoFps, int videoMaxBitrate, String videoCodec, + boolean videoCodecHwAcceleration, int audioStartBitrate, String audioCodec, + boolean noAudioProcessing, boolean aecDump, boolean useOpenSLES, boolean disableBuiltInAEC, + boolean disableBuiltInAGC, boolean disableBuiltInNS, boolean enableLevelControl, + DataChannelParameters dataChannelParameters) { this.videoCallEnabled = videoCallEnabled; this.loopback = loopback; this.tracing = tracing; @@ -173,6 +211,7 @@ public class PeerConnectionClient { this.disableBuiltInAGC = disableBuiltInAGC; this.disableBuiltInNS = disableBuiltInNS; this.enableLevelControl = enableLevelControl; + this.dataChannelParameters = dataChannelParameters; } } @@ -243,6 +282,7 @@ public class PeerConnectionClient { this.peerConnectionParameters = peerConnectionParameters; this.events = events; videoCallEnabled = peerConnectionParameters.videoCallEnabled; + dataChannelEnabled = peerConnectionParameters.dataChannelParameters != null; // Reset variables to initial states. this.context = null; factory = null; @@ -484,6 +524,17 @@ public class PeerConnectionClient { rtcConfig.keyType = PeerConnection.KeyType.ECDSA; peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver); + + if (dataChannelEnabled) { + DataChannel.Init init = new DataChannel.Init(); + init.ordered = peerConnectionParameters.dataChannelParameters.ordered; + init.negotiated = peerConnectionParameters.dataChannelParameters.negotiated; + init.maxRetransmits = peerConnectionParameters.dataChannelParameters.maxRetransmits; + init.maxRetransmitTimeMs = peerConnectionParameters.dataChannelParameters.maxRetransmitTimeMs; + init.id = peerConnectionParameters.dataChannelParameters.id; + init.protocol = peerConnectionParameters.dataChannelParameters.protocol; + dataChannel = peerConnection.createDataChannel("ApprtcDemo data", init); + } isInitiator = false; // Set default WebRTC tracing and INFO libjingle logging. @@ -524,6 +575,10 @@ public class PeerConnectionClient { } Log.d(TAG, "Closing peer connection."); statsTimer.cancel(); + if (dataChannel != null) { + dataChannel.dispose(); + dataChannel = null; + } if (peerConnection != null) { peerConnection.dispose(); peerConnection = null; @@ -1076,7 +1131,34 @@ public class PeerConnectionClient { @Override public void onDataChannel(final DataChannel dc) { - reportError("AppRTC doesn't use data channels, but got: " + dc.label() + " anyway!"); + Log.d(TAG, "New Data channel " + dc.label()); + + if (!dataChannelEnabled) + return; + + dc.registerObserver(new DataChannel.Observer() { + public void onBufferedAmountChange(long previousAmount) { + Log.d(TAG, "Data channel buffered amount changed: " + dc.label() + ": " + dc.state()); + } + + @Override + public void onStateChange() { + Log.d(TAG, "Data channel state changed: " + dc.label() + ": " + dc.state()); + } + + @Override + public void onMessage(final DataChannel.Buffer buffer) { + if (buffer.binary) { + Log.d(TAG, "Received binary msg over " + dc); + return; + } + ByteBuffer data = buffer.data; + final byte[] bytes = new byte[data.capacity()]; + data.get(bytes); + String strData = new String(bytes); + Log.d(TAG, "Got msg: " + strData + " over " + dc); + } + }); } @Override diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java index aee778bcfc..0448372fb4 100644 --- a/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java +++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java @@ -52,6 +52,14 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan private String keyPrefDisplayHud; private String keyPrefTracing; + private String keyprefEnableDataChannel; + private String keyprefOrdered; + private String keyprefMaxRetransmitTimeMs; + private String keyprefMaxRetransmits; + private String keyprefDataProtocol; + private String keyprefNegotiated; + private String keyprefDataId; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -79,6 +87,14 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan keyprefEnableLevelControl = getString(R.string.pref_enable_level_control_key); keyprefSpeakerphone = getString(R.string.pref_speakerphone_key); + keyprefEnableDataChannel = getString(R.string.pref_enable_datachannel_key); + keyprefOrdered = getString(R.string.pref_ordered_key); + keyprefMaxRetransmitTimeMs = getString(R.string.pref_max_retransmit_time_ms_key); + keyprefMaxRetransmits = getString(R.string.pref_max_retransmits_key); + keyprefDataProtocol = getString(R.string.pref_data_protocol_key); + keyprefNegotiated = getString(R.string.pref_negotiated_key); + keyprefDataId = getString(R.string.pref_data_id_key); + keyPrefRoomServerUrl = getString(R.string.pref_room_server_url_key); keyPrefDisplayHud = getString(R.string.pref_displayhud_key); keyPrefTracing = getString(R.string.pref_tracing_key); @@ -124,6 +140,15 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan updateSummaryB(sharedPreferences, keyprefEnableLevelControl); updateSummaryList(sharedPreferences, keyprefSpeakerphone); + updateSummaryB(sharedPreferences, keyprefEnableDataChannel); + updateSummaryB(sharedPreferences, keyprefOrdered); + updateSummary(sharedPreferences, keyprefMaxRetransmitTimeMs); + updateSummary(sharedPreferences, keyprefMaxRetransmits); + updateSummary(sharedPreferences, keyprefDataProtocol); + updateSummaryB(sharedPreferences, keyprefNegotiated); + updateSummary(sharedPreferences, keyprefDataId); + setDataChannelEnable(sharedPreferences); + updateSummary(sharedPreferences, keyPrefRoomServerUrl); updateSummaryB(sharedPreferences, keyPrefDisplayHud); updateSummaryB(sharedPreferences, keyPrefTracing); @@ -182,7 +207,11 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan || key.equals(keyPrefVideoCodec) || key.equals(keyprefStartAudioBitrateType) || key.equals(keyPrefAudioCodec) - || key.equals(keyPrefRoomServerUrl)) { + || key.equals(keyPrefRoomServerUrl) + || key.equals(keyprefMaxRetransmitTimeMs) + || key.equals(keyprefMaxRetransmits) + || key.equals(keyprefDataProtocol) + || key.equals(keyprefDataId)) { updateSummary(sharedPreferences, key); } else if (key.equals(keyprefMaxVideoBitrateValue) || key.equals(keyprefStartAudioBitrateValue)) { @@ -201,7 +230,10 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan || key.equals(keyprefDisableBuiltInAGC) || key.equals(keyprefDisableBuiltInNS) || key.equals(keyprefEnableLevelControl) - || key.equals(keyPrefDisplayHud)) { + || key.equals(keyPrefDisplayHud) + || key.equals(keyprefEnableDataChannel) + || key.equals(keyprefOrdered) + || key.equals(keyprefNegotiated)) { updateSummaryB(sharedPreferences, key); } else if (key.equals(keyprefSpeakerphone)) { updateSummaryList(sharedPreferences, key); @@ -213,6 +245,9 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan if (key.equals(keyprefStartAudioBitrateType)) { setAudioBitrateEnable(sharedPreferences); } + if (key.equals(keyprefEnableDataChannel)) { + setDataChannelEnable(sharedPreferences); + } } private void updateSummary(SharedPreferences sharedPreferences, String key) { @@ -263,4 +298,14 @@ public class SettingsActivity extends Activity implements OnSharedPreferenceChan bitratePreferenceValue.setEnabled(true); } } + + private void setDataChannelEnable(SharedPreferences sharedPreferences) { + boolean enabled = sharedPreferences.getBoolean(keyprefEnableDataChannel, true); + settingsFragment.findPreference(keyprefOrdered).setEnabled(enabled); + settingsFragment.findPreference(keyprefMaxRetransmitTimeMs).setEnabled(enabled); + settingsFragment.findPreference(keyprefMaxRetransmits).setEnabled(enabled); + settingsFragment.findPreference(keyprefDataProtocol).setEnabled(enabled); + settingsFragment.findPreference(keyprefNegotiated).setEnabled(enabled); + settingsFragment.findPreference(keyprefDataId).setEnabled(enabled); + } }