diff --git a/talk/examples/android/res/values/arrays.xml b/talk/examples/android/res/values/arrays.xml index d6ec4d2522..ba8c8918ce 100644 --- a/talk/examples/android/res/values/arrays.xml +++ b/talk/examples/android/res/values/arrays.xml @@ -28,6 +28,12 @@ VP8 VP9 + H264 + + + + OPUS + ISAC diff --git a/talk/examples/android/res/values/strings.xml b/talk/examples/android/res/values/strings.xml index b4d9095fba..428b0eb8be 100644 --- a/talk/examples/android/res/values/strings.xml +++ b/talk/examples/android/res/values/strings.xml @@ -28,6 +28,14 @@ room_preference room_list_preference + video_settings_key + WebRTC video settings. + + videocall_preference + Video call. + Enable video in a call. + true + resolution_preference Video resolution. Enter AppRTC local video resolution. @@ -38,20 +46,15 @@ Enter local camera fps. Default - cpu_usage_detection - CPU overuse detection. - Adapt transmission to CPU status. - true + startvideobitrate_preference + Start video bitrate setting. + Start video bitrate setting. + Default - startbitrate_preference - Start bitrate setting. - Start bitrate setting. - Default - - startbitratevalue_preference - Video encoder start bitrate. - Enter video encoder start bitrate in kbps. - 1000 + startvideobitratevalue_preference + Video encoder start bitrate. + Enter video encoder start bitrate in kbps. + 1000 videocodec_preference Default video codec. @@ -66,6 +69,32 @@ Enabled Disabled + audio_settings_key + WebRTC audio settings. + + startaudiobitrate_preference + Audio bitrate setting. + Audio bitrate setting. + Default + + startaudiobitratevalue_preference + Audio codec bitrate. + Enter audio codec bitrate in kbps. + 32 + + audiocodec_preference + Default audio codec. + Select default audio codec. + OPUS + + misc_settings_key + Miscellaneous settings. + + cpu_usage_detection + CPU overuse detection. + Adapt transmission to CPU status. + true + room_server_url_preference Room server URL. Enter a room server URL. diff --git a/talk/examples/android/res/xml/preferences.xml b/talk/examples/android/res/xml/preferences.xml index 3d77418437..ad0affb478 100644 --- a/talk/examples/android/res/xml/preferences.xml +++ b/talk/examples/android/res/xml/preferences.xml @@ -1,67 +1,111 @@ - + - + - + - + - + - + - + - + + - + + + + + + + + + + + + + + + + + diff --git a/talk/examples/android/src/org/appspot/apprtc/CallActivity.java b/talk/examples/android/src/org/appspot/apprtc/CallActivity.java index 1c80266038..815081183a 100644 --- a/talk/examples/android/src/org/appspot/apprtc/CallActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/CallActivity.java @@ -66,18 +66,24 @@ public class CallActivity extends Activity "org.appspot.apprtc.ROOMID"; public static final String EXTRA_LOOPBACK = "org.appspot.apprtc.LOOPBACK"; - public static final String EXTRA_HWCODEC = - "org.appspot.apprtc.HWCODEC"; - public static final String EXTRA_VIDEO_BITRATE = - "org.appspot.apprtc.VIDEO_BITRATE"; + public static final String EXTRA_VIDEO_CALL = + "org.appspot.apprtc.VIDEO_CALL"; public static final String EXTRA_VIDEO_WIDTH = "org.appspot.apprtc.VIDEO_WIDTH"; public static final String EXTRA_VIDEO_HEIGHT = "org.appspot.apprtc.VIDEO_HEIGHT"; public static final String EXTRA_VIDEO_FPS = "org.appspot.apprtc.VIDEO_FPS"; + public static final String EXTRA_VIDEO_BITRATE = + "org.appspot.apprtc.VIDEO_BITRATE"; public static final String EXTRA_VIDEOCODEC = "org.appspot.apprtc.VIDEOCODEC"; + public static final String EXTRA_HWCODEC_ENABLED = + "org.appspot.apprtc.HWCODEC"; + public static final String EXTRA_AUDIO_BITRATE = + "org.appspot.apprtc.AUDIO_BITRATE"; + public static final String EXTRA_AUDIOCODEC = + "org.appspot.apprtc.AUDIOCODEC"; public static final String EXTRA_CPUOVERUSE_DETECTION = "org.appspot.apprtc.CPUOVERUSE_DETECTION"; public static final String EXTRA_DISPLAY_HUD = @@ -118,11 +124,10 @@ public class CallActivity extends Activity private boolean activityRunning; private RoomConnectionParameters roomConnectionParameters; private PeerConnectionParameters peerConnectionParameters; - private boolean hwCodecAcceleration; - private String videoCodec; private boolean iceConnected; private boolean isError; private boolean callControlFragmentVisible = true; + private long callStartedTimeMs = 0; // Controls private GLSurfaceView videoView; @@ -194,17 +199,17 @@ public class CallActivity extends Activity return; } boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false); - hwCodecAcceleration = intent.getBooleanExtra(EXTRA_HWCODEC, true); - if (intent.hasExtra(EXTRA_VIDEOCODEC)) { - videoCodec = intent.getStringExtra(EXTRA_VIDEOCODEC); - } else { - videoCodec = PeerConnectionClient.VIDEO_CODEC_VP8; // use VP8 by default. - } peerConnectionParameters = new PeerConnectionParameters( + intent.getBooleanExtra(EXTRA_VIDEO_CALL, true), + loopback, intent.getIntExtra(EXTRA_VIDEO_WIDTH, 0), intent.getIntExtra(EXTRA_VIDEO_HEIGHT, 0), intent.getIntExtra(EXTRA_VIDEO_FPS, 0), intent.getIntExtra(EXTRA_VIDEO_BITRATE, 0), + intent.getStringExtra(EXTRA_VIDEOCODEC), + intent.getBooleanExtra(EXTRA_HWCODEC_ENABLED, true), + intent.getIntExtra(EXTRA_AUDIO_BITRATE, 0), + intent.getStringExtra(EXTRA_AUDIOCODEC), intent.getBooleanExtra(EXTRA_CPUOVERUSE_DETECTION, true)); commandLineRun = intent.getBooleanExtra(EXTRA_CMDLINE, false); runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0); @@ -319,6 +324,8 @@ public class CallActivity extends Activity Log.e(TAG, "AppRTC client is not allocated for a call."); return; } + callStartedTimeMs = System.currentTimeMillis(); + // Start room connection. logAndToast(getString(R.string.connecting_to, roomConnectionParameters.roomUrl)); @@ -343,6 +350,9 @@ public class CallActivity extends Activity // Should be called from UI thread private void callConnected() { + final long delta = System.currentTimeMillis() - callStartedTimeMs; + Log.i(TAG, "Call connected: delay=" + delta + "ms"); + // Update video view. updateVideoView(); // Enable statistics callback. @@ -360,10 +370,12 @@ public class CallActivity extends Activity @Override public void run() { if (peerConnectionClient == null) { + final long delta = System.currentTimeMillis() - callStartedTimeMs; + Log.d(TAG, "Creating peer connection factory, delay=" + delta + "ms"); peerConnectionClient = new PeerConnectionClient(); peerConnectionClient.createPeerConnectionFactory(CallActivity.this, - videoCodec, hwCodecAcceleration, - VideoRendererGui.getEGLContext(), CallActivity.this); + VideoRendererGui.getEGLContext(), peerConnectionParameters, + CallActivity.this); } if (signalingParameters != null) { Log.w(TAG, "EGL context is ready after room connection."); @@ -428,15 +440,16 @@ public class CallActivity extends Activity // All callbacks are invoked from websocket signaling looper thread and // are routed to UI thread. private void onConnectedToRoomInternal(final SignalingParameters params) { + final long delta = System.currentTimeMillis() - callStartedTimeMs; + signalingParameters = params; if (peerConnectionClient == null) { Log.w(TAG, "Room is connected, but EGL context is not ready yet."); return; } - logAndToast("Creating peer connection..."); + logAndToast("Creating peer connection, delay=" + delta + "ms"); peerConnectionClient.createPeerConnection( - localRender, remoteRender, - signalingParameters, peerConnectionParameters); + localRender, remoteRender, signalingParameters); if (signalingParameters.initiator) { logAndToast("Creating OFFER..."); @@ -472,6 +485,7 @@ public class CallActivity extends Activity @Override public void onRemoteDescription(final SessionDescription sdp) { + final long delta = System.currentTimeMillis() - callStartedTimeMs; runOnUiThread(new Runnable() { @Override public void run() { @@ -479,7 +493,7 @@ public class CallActivity extends Activity Log.e(TAG, "Received remote SDP for non-initilized peer connection."); return; } - logAndToast("Received remote " + sdp.type + " ..."); + logAndToast("Received remote " + sdp.type + ", delay=" + delta + "ms"); peerConnectionClient.setRemoteDescription(sdp); if (!signalingParameters.initiator) { logAndToast("Creating ANSWER..."); @@ -536,11 +550,12 @@ public class CallActivity extends Activity // are routed to UI thread. @Override public void onLocalDescription(final SessionDescription sdp) { + final long delta = System.currentTimeMillis() - callStartedTimeMs; runOnUiThread(new Runnable() { @Override public void run() { if (appRtcClient != null) { - logAndToast("Sending " + sdp.type + " ..."); + logAndToast("Sending " + sdp.type + ", delay=" + delta + "ms"); if (signalingParameters.initiator) { appRtcClient.sendOfferSdp(sdp); } else { @@ -565,10 +580,11 @@ public class CallActivity extends Activity @Override public void onIceConnected() { + final long delta = System.currentTimeMillis() - callStartedTimeMs; runOnUiThread(new Runnable() { @Override public void run() { - logAndToast("ICE connected"); + logAndToast("ICE connected, delay=" + delta + "ms"); iceConnected = true; callConnected(); } diff --git a/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java b/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java index 8c2323dd87..5a6f99ca9e 100644 --- a/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java @@ -71,11 +71,15 @@ public class ConnectActivity extends Activity { private EditText roomEditText; private ListView roomListView; private SharedPreferences sharedPref; + private String keyprefVideoCallEnabled; private String keyprefResolution; private String keyprefFps; - private String keyprefBitrateType; - private String keyprefBitrateValue; + private String keyprefVideoBitrateType; + private String keyprefVideoBitrateValue; private String keyprefVideoCodec; + private String keyprefAudioBitrateType; + private String keyprefAudioBitrateValue; + private String keyprefAudioCodec; private String keyprefHwCodecAcceleration; private String keyprefCpuUsageDetection; private String keyprefDisplayHud; @@ -92,12 +96,16 @@ public class ConnectActivity extends Activity { // Get setting keys. PreferenceManager.setDefaultValues(this, R.xml.preferences, false); sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + keyprefVideoCallEnabled = getString(R.string.pref_videocall_key); keyprefResolution = getString(R.string.pref_resolution_key); keyprefFps = getString(R.string.pref_fps_key); - keyprefBitrateType = getString(R.string.pref_startbitrate_key); - keyprefBitrateValue = getString(R.string.pref_startbitratevalue_key); + keyprefVideoBitrateType = getString(R.string.pref_startvideobitrate_key); + keyprefVideoBitrateValue = getString(R.string.pref_startvideobitratevalue_key); keyprefVideoCodec = getString(R.string.pref_videocodec_key); keyprefHwCodecAcceleration = getString(R.string.pref_hwcodec_key); + keyprefAudioBitrateType = getString(R.string.pref_startaudiobitrate_key); + keyprefAudioBitrateValue = getString(R.string.pref_startaudiobitratevalue_key); + keyprefAudioCodec = getString(R.string.pref_audiocodec_key); keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key); keyprefDisplayHud = getString(R.string.pref_displayhud_key); keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key); @@ -244,9 +252,15 @@ public class ConnectActivity extends Activity { keyprefRoomServerUrl, getString(R.string.pref_room_server_url_default)); - // Get default video codec. + // Video call enabled flag. + boolean videoCallEnabled = sharedPref.getBoolean(keyprefVideoCallEnabled, + Boolean.valueOf(getString(R.string.pref_videocall_default))); + + // Get default codecs. String videoCodec = sharedPref.getString(keyprefVideoCodec, getString(R.string.pref_videocodec_default)); + String audioCodec = sharedPref.getString(keyprefAudioCodec, + getString(R.string.pref_audiocodec_default)); // Check HW codec flag. boolean hwCodec = sharedPref.getBoolean(keyprefHwCodecAcceleration, @@ -282,15 +296,25 @@ public class ConnectActivity extends Activity { } } - // Get start bitrate. - int startBitrate = 0; - String bitrateTypeDefault = getString(R.string.pref_startbitrate_default); + // Get video and audio start bitrate. + int videoStartBitrate = 0; + String bitrateTypeDefault = getString( + R.string.pref_startvideobitrate_default); String bitrateType = sharedPref.getString( - keyprefBitrateType, bitrateTypeDefault); + keyprefVideoBitrateType, bitrateTypeDefault); if (!bitrateType.equals(bitrateTypeDefault)) { - String bitrateValue = sharedPref.getString(keyprefBitrateValue, - getString(R.string.pref_startbitratevalue_default)); - startBitrate = Integer.parseInt(bitrateValue); + String bitrateValue = sharedPref.getString(keyprefVideoBitrateValue, + getString(R.string.pref_startvideobitratevalue_default)); + videoStartBitrate = Integer.parseInt(bitrateValue); + } + int audioStartBitrate = 0; + bitrateTypeDefault = getString(R.string.pref_startaudiobitrate_default); + bitrateType = sharedPref.getString( + keyprefAudioBitrateType, bitrateTypeDefault); + if (!bitrateType.equals(bitrateTypeDefault)) { + String bitrateValue = sharedPref.getString(keyprefAudioBitrateValue, + getString(R.string.pref_startaudiobitratevalue_default)); + audioStartBitrate = Integer.parseInt(bitrateValue); } // Test if CpuOveruseDetection should be disabled. By default is on. @@ -311,12 +335,15 @@ public class ConnectActivity extends Activity { intent.setData(uri); intent.putExtra(CallActivity.EXTRA_ROOMID, roomId); intent.putExtra(CallActivity.EXTRA_LOOPBACK, loopback); - intent.putExtra(CallActivity.EXTRA_VIDEOCODEC, videoCodec); - intent.putExtra(CallActivity.EXTRA_HWCODEC, hwCodec); - intent.putExtra(CallActivity.EXTRA_VIDEO_BITRATE, startBitrate); + intent.putExtra(CallActivity.EXTRA_VIDEO_CALL, videoCallEnabled); intent.putExtra(CallActivity.EXTRA_VIDEO_WIDTH, videoWidth); intent.putExtra(CallActivity.EXTRA_VIDEO_HEIGHT, videoHeight); intent.putExtra(CallActivity.EXTRA_VIDEO_FPS, cameraFps); + intent.putExtra(CallActivity.EXTRA_VIDEO_BITRATE, videoStartBitrate); + intent.putExtra(CallActivity.EXTRA_VIDEOCODEC, videoCodec); + intent.putExtra(CallActivity.EXTRA_HWCODEC_ENABLED, hwCodec); + intent.putExtra(CallActivity.EXTRA_AUDIO_BITRATE, audioStartBitrate); + intent.putExtra(CallActivity.EXTRA_AUDIOCODEC, audioCodec); intent.putExtra(CallActivity.EXTRA_CPUOVERUSE_DETECTION, cpuOveruseDetection); intent.putExtra(CallActivity.EXTRA_DISPLAY_HUD, displayHud); diff --git a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java index 78fe1762dd..7910023845 100644 --- a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java @@ -67,13 +67,15 @@ public class PeerConnectionClient { public static final String VIDEO_TRACK_ID = "ARDAMSv0"; public static final String AUDIO_TRACK_ID = "ARDAMSa0"; private static final String TAG = "PCRTCClient"; - private static final boolean PREFER_ISAC = false; - private static final boolean PREFER_H264 = false; - public static final String AUDIO_CODEC_ISAC = "ISAC"; - public static final String VIDEO_CODEC_VP8 = "VP8"; - public static final String VIDEO_CODEC_VP9 = "VP9"; - public static final String VIDEO_CODEC_H264 = "H264"; private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/"; + private static final String VIDEO_CODEC_VP8 = "VP8"; + private static final String VIDEO_CODEC_VP9 = "VP9"; + private static final String VIDEO_CODEC_H264 = "H264"; + private static final String AUDIO_CODEC_OPUS = "opus"; + private static final String AUDIO_CODEC_ISAC = "ISAC"; + private static final String VIDEO_CODEC_PARAM_START_BITRATE = + "x-google-start-bitrate"; + private static final String AUDIO_CODEC_PARAM_BITRATE = "maxaveragebitrate"; private static final String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth"; private static final String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth"; private static final String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight"; @@ -90,9 +92,11 @@ public class PeerConnectionClient { private PeerConnectionFactory factory = null; private PeerConnection peerConnection = null; private VideoSource videoSource; + private boolean videoCallEnabled = true; + private boolean preferIsac = false; + private boolean preferH264 = false; private boolean videoSourceStopped = false; private boolean isError = false; - private boolean videoCodecHwAcceleration; private final Timer statsTimer = new Timer(); private final PCObserver pcObserver = new PCObserver(); private final SDPObserver sdpObserver = new SDPObserver(); @@ -120,18 +124,34 @@ public class PeerConnectionClient { * Peer connection parameters. */ public static class PeerConnectionParameters { + public final boolean videoCallEnabled; + public final boolean loopback; public final int videoWidth; public final int videoHeight; public final int videoFps; public final int videoStartBitrate; + public final String videoCodec; + public final boolean videoCodecHwAcceleration; + public final int audioStartBitrate; + public final String audioCodec; public final boolean cpuOveruseDetection; - public PeerConnectionParameters(int videoWidth, int videoHeight, - int videoFps, int videoStartBitrate, boolean cpuOveruseDetection) { + public PeerConnectionParameters( + boolean videoCallEnabled, boolean loopback, + int videoWidth, int videoHeight, int videoFps, int videoStartBitrate, + String videoCodec, boolean videoCodecHwAcceleration, + int audioStartBitrate, String audioCodec, + boolean cpuOveruseDetection) { + this.videoCallEnabled = videoCallEnabled; + this.loopback = loopback; this.videoWidth = videoWidth; this.videoHeight = videoHeight; this.videoFps = videoFps; this.videoStartBitrate = videoStartBitrate; + this.videoCodec = videoCodec; + this.videoCodecHwAcceleration = videoCodecHwAcceleration; + this.audioStartBitrate = audioStartBitrate; + this.audioCodec = audioCodec; this.cpuOveruseDetection = cpuOveruseDetection; } } @@ -184,18 +204,16 @@ public class PeerConnectionClient { public void createPeerConnectionFactory( final Context context, - final String videoCodec, - final boolean videoCodecHwAcceleration, final EGLContext renderEGLContext, + final PeerConnectionParameters peerConnectionParameters, final PeerConnectionEvents events) { + this.peerConnectionParameters = peerConnectionParameters; this.events = events; - this.videoCodecHwAcceleration = videoCodecHwAcceleration; executor.requestStart(); executor.execute(new Runnable() { @Override public void run() { - createPeerConnectionFactoryInternal( - context, videoCodec, renderEGLContext); + createPeerConnectionFactoryInternal(context, renderEGLContext); } }); } @@ -203,23 +221,29 @@ public class PeerConnectionClient { public void createPeerConnection( final VideoRenderer.Callbacks localRender, final VideoRenderer.Callbacks remoteRender, - final SignalingParameters signalingParameters, - final PeerConnectionParameters peerConnectionParameters) { + final SignalingParameters signalingParameters) { + if (peerConnectionParameters == null) { + Log.e(TAG, "Creating peer connection without initializing factory."); + return; + } this.localRender = localRender; this.remoteRender = remoteRender; this.signalingParameters = signalingParameters; - this.peerConnectionParameters = peerConnectionParameters; // Merge video constraints from signaling parameters and peer connection // parameters. videoConstraints = signalingParameters.videoConstraints; - if (videoConstraints != null && peerConnectionParameters != null) { + if (signalingParameters.videoConstraints == null) { + videoCallEnabled = false; + } + if (videoCallEnabled) { int videoWidth = peerConnectionParameters.videoWidth; int videoHeight = peerConnectionParameters.videoHeight; // If HW video encoder is supported and video resolution is not // specified force it to HD. - if ((videoWidth == 0 || videoHeight == 0) && videoCodecHwAcceleration && - MediaCodecVideoEncoder.isVp8HwSupported()) { + if ((videoWidth == 0 || videoHeight == 0) + && peerConnectionParameters.videoCodecHwAcceleration + && MediaCodecVideoEncoder.isVp8HwSupported()) { videoWidth = HD_VIDEO_WIDTH; videoHeight = HD_VIDEO_HEIGHT; } @@ -268,19 +292,34 @@ public class PeerConnectionClient { } private void createPeerConnectionFactoryInternal( - Context context, - String videoCodec, - EGLContext renderEGLContext) { + Context context, EGLContext renderEGLContext) { Log.d(TAG, "Create peer connection factory with EGLContext " - + renderEGLContext); + + renderEGLContext + ". Use video: " + + peerConnectionParameters.videoCallEnabled); + videoCallEnabled = peerConnectionParameters.videoCallEnabled; isError = false; - if (videoCodec.equals(VIDEO_CODEC_VP9)) { + // Check if VP9 is used by default. + if (videoCallEnabled && peerConnectionParameters.videoCodec != null + && peerConnectionParameters.videoCodec.equals(VIDEO_CODEC_VP9)) { PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9); } else { PeerConnectionFactory.initializeFieldTrials(null); } + // Check if H.264 is used by default. + preferH264 = false; + if (videoCallEnabled && peerConnectionParameters.videoCodec != null + && peerConnectionParameters.videoCodec.equals(VIDEO_CODEC_H264)) { + preferH264 = true; + } + // Check if ISAC is used by default. + preferIsac = false; + if (peerConnectionParameters.audioCodec != null + && peerConnectionParameters.audioCodec.equals(AUDIO_CODEC_ISAC)) { + preferIsac = true; + } if (!PeerConnectionFactory.initializeAndroidGlobals( - context, true, true, videoCodecHwAcceleration, renderEGLContext)) { + context, true, true, + peerConnectionParameters.videoCodecHwAcceleration, renderEGLContext)) { events.onPeerConnectionError("Failed to initializeAndroidGlobals"); } factory = new PeerConnectionFactory(); @@ -292,16 +331,23 @@ public class PeerConnectionClient { Log.e(TAG, "Peerconnection factory is not created"); return; } - Log.d(TAG, "Create peer connection. VideoConstraints: " - + videoConstraints.toString()); + Log.d(TAG, "Create peer connection"); + if (videoConstraints != null) { + Log.d(TAG, "VideoConstraints: " + videoConstraints.toString()); + } isInitiator = signalingParameters.initiator; queuedRemoteCandidates = new LinkedList(); sdpMediaConstraints = new MediaConstraints(); sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair( "OfferToReceiveAudio", "true")); - sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair( - "OfferToReceiveVideo", "true")); + if (videoCallEnabled || peerConnectionParameters.loopback) { + sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair( + "OfferToReceiveVideo", "true")); + } else { + sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair( + "OfferToReceiveVideo", "false")); + } MediaConstraints pcConstraints = signalingParameters.pcConstraints; pcConstraints.optional.add( @@ -318,7 +364,7 @@ public class PeerConnectionClient { // Logging.Severity.LS_SENSITIVE); mediaStream = factory.createLocalMediaStream("ARDAMS"); - if (videoConstraints != null) { + if (videoCallEnabled) { videoCapturer = VideoCapturerAndroid.create( VideoCapturerAndroid.getNameOfFrontFacingDevice()); mediaStream.addTrack(createVideoTrack(videoCapturer)); @@ -355,7 +401,7 @@ public class PeerConnectionClient { } public boolean isHDVideo() { - if (videoConstraints == null) { + if (!videoCallEnabled) { return false; } int minWidth = 0; @@ -479,17 +525,23 @@ public class PeerConnectionClient { return; } String sdpDescription = sdp.description; - if (PREFER_ISAC) { + if (preferIsac) { sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true); } - if (PREFER_H264) { + if (videoCallEnabled && preferH264) { sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false); } - if (peerConnectionParameters.videoStartBitrate > 0) { - sdpDescription = setStartBitrate(VIDEO_CODEC_VP8, + if (videoCallEnabled && peerConnectionParameters.videoStartBitrate > 0) { + sdpDescription = setStartBitrate(VIDEO_CODEC_VP8, true, sdpDescription, peerConnectionParameters.videoStartBitrate); - sdpDescription = setStartBitrate(VIDEO_CODEC_VP9, + sdpDescription = setStartBitrate(VIDEO_CODEC_VP9, true, sdpDescription, peerConnectionParameters.videoStartBitrate); + sdpDescription = setStartBitrate(VIDEO_CODEC_H264, true, + sdpDescription, peerConnectionParameters.videoStartBitrate); + } + if (peerConnectionParameters.audioStartBitrate > 0) { + sdpDescription = setStartBitrate(AUDIO_CODEC_OPUS, false, + sdpDescription, peerConnectionParameters.audioStartBitrate); } Log.d(TAG, "Set remote SDP."); SessionDescription sdpRemote = new SessionDescription( @@ -549,12 +601,13 @@ public class PeerConnectionClient { return localVideoTrack; } - // Mangle SDP to add video start bitrate. - private static String setStartBitrate(String codec, + private static String setStartBitrate(String codec, boolean isVideoCodec, String sdpDescription, int bitrateKbps) { String[] lines = sdpDescription.split("\r\n"); - int lineIndex = -1; + int rtpmapLineIndex = -1; + boolean sdpFormatUpdated = false; String codecRtpMap = null; + // Search for codec rtpmap in format // a=rtpmap: / [/] String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"; Pattern codecPattern = Pattern.compile(regex); @@ -562,7 +615,7 @@ public class PeerConnectionClient { Matcher codecMatcher = codecPattern.matcher(lines[i]); if (codecMatcher.matches()) { codecRtpMap = codecMatcher.group(1); - lineIndex = i; + rtpmapLineIndex = i; break; } } @@ -571,16 +624,46 @@ public class PeerConnectionClient { return sdpDescription; } Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap - + " at " + lines[lineIndex]); + + " at " + lines[rtpmapLineIndex]); + + // Check if a=fmtp string already exist in remote SDP for this codec and + // update it with new bitrate parameter. + regex = "^a=fmtp:" + codecRtpMap + " \\w+=\\d+.*[\r]?$"; + codecPattern = Pattern.compile(regex); + for (int i = 0; i < lines.length; i++) { + Matcher codecMatcher = codecPattern.matcher(lines[i]); + if (codecMatcher.matches()) { + Log.d(TAG, "Found " + codec + " " + lines[i]); + if (isVideoCodec) { + lines[i] += "; " + VIDEO_CODEC_PARAM_START_BITRATE + + "=" + bitrateKbps; + } else { + lines[i] += "; " + AUDIO_CODEC_PARAM_BITRATE + + "=" + (bitrateKbps * 1000); + } + Log.d(TAG, "Update remote SDP line: " + lines[i]); + sdpFormatUpdated = true; + break; + } + } + StringBuilder newSdpDescription = new StringBuilder(); for (int i = 0; i < lines.length; i++) { newSdpDescription.append(lines[i]).append("\r\n"); - if (i == lineIndex) { - String bitrateSet = "a=fmtp:" + codecRtpMap - + " x-google-start-bitrate=" + bitrateKbps; - Log.d(TAG, "Add bitrate SDP line: " + bitrateSet); + // Append new a=fmtp line if no such line exist for a codec. + if (!sdpFormatUpdated && i == rtpmapLineIndex) { + String bitrateSet; + if (isVideoCodec) { + bitrateSet = "a=fmtp:" + codecRtpMap + " " + + VIDEO_CODEC_PARAM_START_BITRATE + "=" + bitrateKbps; + } else { + bitrateSet = "a=fmtp:" + codecRtpMap + " " + + AUDIO_CODEC_PARAM_BITRATE + "=" + (bitrateKbps * 1000); + } + Log.d(TAG, "Add remote SDP line: " + bitrateSet); newSdpDescription.append(bitrateSet).append("\r\n"); } + } return newSdpDescription.toString(); } @@ -652,7 +735,7 @@ public class PeerConnectionClient { } private void switchCameraInternal() { - if (videoConstraints == null) { + if (!videoCallEnabled) { return; // No video is sent. } Log.d(TAG, "Switch camera"); @@ -770,10 +853,10 @@ public class PeerConnectionClient { return; } String sdpDescription = origSdp.description; - if (PREFER_ISAC) { + if (preferIsac) { sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true); } - if (PREFER_H264) { + if (videoCallEnabled && preferH264) { sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false); } final SessionDescription sdp = new SessionDescription( diff --git a/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java b/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java index 0e9278d778..1a017001e2 100644 --- a/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java @@ -39,12 +39,18 @@ import android.preference.Preference; public class SettingsActivity extends Activity implements OnSharedPreferenceChangeListener{ private SettingsFragment settingsFragment; + private String keyprefVideoCall; private String keyprefResolution; private String keyprefFps; - private String keyprefStartBitrateType; - private String keyprefStartBitrateValue; + private String keyprefStartVideoBitrateType; + private String keyprefStartVideoBitrateValue; private String keyPrefVideoCodec; private String keyprefHwCodec; + + private String keyprefStartAudioBitrateType; + private String keyprefStartAudioBitrateValue; + private String keyPrefAudioCodec; + private String keyprefCpuUsageDetection; private String keyPrefRoomServerUrl; private String keyPrefDisplayHud; @@ -52,12 +58,18 @@ public class SettingsActivity extends Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + keyprefVideoCall = getString(R.string.pref_videocall_key); keyprefResolution = getString(R.string.pref_resolution_key); keyprefFps = getString(R.string.pref_fps_key); - keyprefStartBitrateType = getString(R.string.pref_startbitrate_key); - keyprefStartBitrateValue = getString(R.string.pref_startbitratevalue_key); + keyprefStartVideoBitrateType = getString(R.string.pref_startvideobitrate_key); + keyprefStartVideoBitrateValue = getString(R.string.pref_startvideobitratevalue_key); keyPrefVideoCodec = getString(R.string.pref_videocodec_key); keyprefHwCodec = getString(R.string.pref_hwcodec_key); + + keyprefStartAudioBitrateType = getString(R.string.pref_startaudiobitrate_key); + keyprefStartAudioBitrateValue = getString(R.string.pref_startaudiobitratevalue_key); + keyPrefAudioCodec = getString(R.string.pref_audiocodec_key); + keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key); keyPrefRoomServerUrl = getString(R.string.pref_room_server_url_key); keyPrefDisplayHud = getString(R.string.pref_displayhud_key); @@ -76,13 +88,20 @@ public class SettingsActivity extends Activity SharedPreferences sharedPreferences = settingsFragment.getPreferenceScreen().getSharedPreferences(); sharedPreferences.registerOnSharedPreferenceChangeListener(this); + updateSummaryB(sharedPreferences, keyprefVideoCall); updateSummary(sharedPreferences, keyprefResolution); updateSummary(sharedPreferences, keyprefFps); - updateSummary(sharedPreferences, keyprefStartBitrateType); - updateSummaryBitrate(sharedPreferences, keyprefStartBitrateValue); - setBitrateEnable(sharedPreferences); + updateSummary(sharedPreferences, keyprefStartVideoBitrateType); + updateSummaryBitrate(sharedPreferences, keyprefStartVideoBitrateValue); + setVideoBitrateEnable(sharedPreferences); updateSummary(sharedPreferences, keyPrefVideoCodec); updateSummaryB(sharedPreferences, keyprefHwCodec); + + updateSummary(sharedPreferences, keyprefStartAudioBitrateType); + updateSummaryBitrate(sharedPreferences, keyprefStartAudioBitrateValue); + setAudioBitrateEnable(sharedPreferences); + updateSummary(sharedPreferences, keyPrefAudioCodec); + updateSummaryB(sharedPreferences, keyprefCpuUsageDetection); updateSummary(sharedPreferences, keyPrefRoomServerUrl); updateSummaryB(sharedPreferences, keyPrefDisplayHud); @@ -101,18 +120,26 @@ public class SettingsActivity extends Activity String key) { if (key.equals(keyprefResolution) || key.equals(keyprefFps) - || key.equals(keyprefStartBitrateType) - || key.equals(keyPrefRoomServerUrl) - || key.equals(keyPrefVideoCodec)) { + || key.equals(keyprefStartVideoBitrateType) + || key.equals(keyPrefVideoCodec) + || key.equals(keyprefStartAudioBitrateType) + || key.equals(keyPrefAudioCodec) + || key.equals(keyPrefRoomServerUrl)) { updateSummary(sharedPreferences, key); - } else if (key.equals(keyprefStartBitrateValue)) { + } else if (key.equals(keyprefStartVideoBitrateValue) + || key.equals(keyprefStartAudioBitrateValue)) { updateSummaryBitrate(sharedPreferences, key); - } else if (key.equals(keyprefCpuUsageDetection) - || key.equals(keyprefHwCodec) || key.equals(keyPrefDisplayHud)) { + } else if (key.equals(keyprefVideoCall) + || key.equals(keyprefHwCodec) + || key.equals(keyprefCpuUsageDetection) + || key.equals(keyPrefDisplayHud)) { updateSummaryB(sharedPreferences, key); } - if (key.equals(keyprefStartBitrateType)) { - setBitrateEnable(sharedPreferences); + if (key.equals(keyprefStartVideoBitrateType)) { + setVideoBitrateEnable(sharedPreferences); + } + if (key.equals(keyprefStartAudioBitrateType)) { + setAudioBitrateEnable(sharedPreferences); } } @@ -135,12 +162,25 @@ public class SettingsActivity extends Activity : getString(R.string.pref_value_disabled)); } - private void setBitrateEnable(SharedPreferences sharedPreferences) { + private void setVideoBitrateEnable(SharedPreferences sharedPreferences) { Preference bitratePreferenceValue = - settingsFragment.findPreference(keyprefStartBitrateValue); - String bitrateTypeDefault = getString(R.string.pref_startbitrate_default); + settingsFragment.findPreference(keyprefStartVideoBitrateValue); + String bitrateTypeDefault = getString(R.string.pref_startvideobitrate_default); String bitrateType = sharedPreferences.getString( - keyprefStartBitrateType, bitrateTypeDefault); + keyprefStartVideoBitrateType, bitrateTypeDefault); + if (bitrateType.equals(bitrateTypeDefault)) { + bitratePreferenceValue.setEnabled(false); + } else { + bitratePreferenceValue.setEnabled(true); + } + } + + private void setAudioBitrateEnable(SharedPreferences sharedPreferences) { + Preference bitratePreferenceValue = + settingsFragment.findPreference(keyprefStartAudioBitrateValue); + String bitrateTypeDefault = getString(R.string.pref_startaudiobitrate_default); + String bitrateType = sharedPreferences.getString( + keyprefStartAudioBitrateType, bitrateTypeDefault); if (bitrateType.equals(bitrateTypeDefault)) { bitratePreferenceValue.setEnabled(false); } else { diff --git a/talk/examples/android/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java b/talk/examples/android/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java index 120644c5d6..888434f881 100644 --- a/talk/examples/android/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java +++ b/talk/examples/android/src/org/appspot/apprtc/util/AsyncHttpURLConnection.java @@ -39,7 +39,7 @@ import java.util.Scanner; * Asynchronous http requests implementation. */ public class AsyncHttpURLConnection { - private static final int HTTP_TIMEOUT_MS = 5000; + private static final int HTTP_TIMEOUT_MS = 8000; private final String method; private final String url; private final String message;