From dea5173edfcc6fed0572ff61bbc116918988bd16 Mon Sep 17 00:00:00 2001 From: "glaznev@webrtc.org" Date: Mon, 1 Dec 2014 20:02:13 +0000 Subject: [PATCH] Add start bitrate and vp8 hw acceleration option to Android AppRTCDemo. - Add an option to set VP8 encoder start bitrate usig x-google-start-bitrate line in remote SDP. - Allow to enabled/disable VP8 hw decoder and encoder acceleration using appRTC settings. BUG=4046 R=jiayl@webrtc.org Review URL: https://webrtc-codereview.appspot.com/32539004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7775 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../org/webrtc/PeerConnectionAndroidTest.java | 2 +- .../app/webrtc/java/jni/peerconnection_jni.cc | 10 ++-- .../src/org/webrtc/PeerConnectionFactory.java | 2 +- .../res/layout/activity_fullscreen.xml | 2 +- talk/examples/android/res/values/arrays.xml | 6 +++ talk/examples/android/res/values/strings.xml | 22 +++++++- talk/examples/android/res/xml/preferences.xml | 24 +++++++++ .../appspot/apprtc/AppRTCDemoActivity.java | 14 ++++-- .../org/appspot/apprtc/ConnectActivity.java | 34 +++++++++++-- .../appspot/apprtc/PeerConnectionClient.java | 50 +++++++++++++++++-- .../org/appspot/apprtc/SettingsActivity.java | 44 ++++++++++++++-- 11 files changed, 183 insertions(+), 27 deletions(-) diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/PeerConnectionAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/PeerConnectionAndroidTest.java index 228d35c12..dcaba185e 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/PeerConnectionAndroidTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/PeerConnectionAndroidTest.java @@ -36,7 +36,7 @@ public class PeerConnectionAndroidTest extends ActivityTestCase { protected void setUp() { assertTrue(PeerConnectionFactory.initializeAndroidGlobals( getInstrumentation().getContext(), true, - true, null)); + true, true, null)); } public void testCompleteSession() throws Exception { diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index a6ba799cc..43faa9ce7 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -163,6 +163,7 @@ static pthread_key_t g_jni_ptr; #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) // Set in PeerConnectionFactory_initializeAndroidGlobals(). static bool factory_static_initialized = false; +static bool vp8_hw_acceleration_enabled = true; #endif @@ -2814,9 +2815,10 @@ JOW(jlong, PeerConnectionFactory_nativeCreateObserver)( JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)( JNIEnv* jni, jclass, jobject context, jboolean initialize_audio, jboolean initialize_video, - jobject render_egl_context) { + jboolean vp8_hw_acceleration, jobject render_egl_context) { CHECK(g_jvm) << "JNI_OnLoad failed to run?"; bool failure = false; + vp8_hw_acceleration_enabled = vp8_hw_acceleration; if (!factory_static_initialized) { if (initialize_video) { failure |= webrtc::SetCaptureAndroidVM(g_jvm, context); @@ -2876,8 +2878,10 @@ JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)( scoped_ptr encoder_factory; scoped_ptr decoder_factory; #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) - encoder_factory.reset(new MediaCodecVideoEncoderFactory()); - decoder_factory.reset(new MediaCodecVideoDecoderFactory()); + if (vp8_hw_acceleration_enabled) { + encoder_factory.reset(new MediaCodecVideoEncoderFactory()); + decoder_factory.reset(new MediaCodecVideoDecoderFactory()); + } #endif rtc::scoped_refptr factory( webrtc::CreatePeerConnectionFactory(worker_thread, diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java index f9f96e79a..9db170947 100644 --- a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java +++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java @@ -51,7 +51,7 @@ public class PeerConnectionFactory { // decoding thread. public static native boolean initializeAndroidGlobals( Object context, boolean initializeAudio, boolean initializeVideo, - Object renderEGLContext); + boolean vp8HwAcceleration, Object renderEGLContext); public PeerConnectionFactory() { nativeFactory = nativeCreatePeerConnectionFactory(); diff --git a/talk/examples/android/res/layout/activity_fullscreen.xml b/talk/examples/android/res/layout/activity_fullscreen.xml index f3fc003e8..d12ccfe3f 100644 --- a/talk/examples/android/res/layout/activity_fullscreen.xml +++ b/talk/examples/android/res/layout/activity_fullscreen.xml @@ -18,7 +18,7 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:textStyle="bold" - android:textColor="#800000FF" + android:textColor="#C000FF00" android:textSize="12dp" android:layout_margin="8dp"/> 30 fps 15 fps + + + Default + Manual + + diff --git a/talk/examples/android/res/values/strings.xml b/talk/examples/android/res/values/strings.xml index e96d6bfe0..08ff54190 100644 --- a/talk/examples/android/res/values/strings.xml +++ b/talk/examples/android/res/values/strings.xml @@ -41,6 +41,24 @@ CPU overuse detection. Adapt transmission to CPU status. true - Enabled - Disabled + + startbitrate_preference + Start bitrate setting. + Start bitrate setting. + Default + + startbitratevalue_preference + Video encoder start bitrate. + Enter video encoder start bitrate in kbps. + 1000 + + hwcodec_preference + VP8 hardware acceleration. + Use VP8 VP8 hardware accelerated codec (if available). + true + + Enabled + Disabled + + diff --git a/talk/examples/android/res/xml/preferences.xml b/talk/examples/android/res/xml/preferences.xml index c5c3a1e9e..94894bc22 100644 --- a/talk/examples/android/res/xml/preferences.xml +++ b/talk/examples/android/res/xml/preferences.xml @@ -7,6 +7,7 @@ android:dialogTitle="@string/pref_resolution_dlg" android:entries="@array/videoResolutions" android:entryValues="@array/videoResolutionsValues" /> + + + + + + + + + diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java index 5743a4763..85e81e7d1 100644 --- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java @@ -91,6 +91,8 @@ public class AppRTCDemoActivity extends Activity private ImageButton videoScalingButton; private boolean commandLineRun; private int runTimeMs; + private int startBitrate; + private boolean hwCodec; private boolean iceConnected; private boolean isError; @@ -208,8 +210,10 @@ public class AppRTCDemoActivity extends Activity ConnectActivity.EXTRA_LOOPBACK, false); commandLineRun = intent.getBooleanExtra( ConnectActivity.EXTRA_CMDLINE, false); - runTimeMs = intent.getIntExtra( - ConnectActivity.EXTRA_RUNTIME, 0); + runTimeMs = intent.getIntExtra(ConnectActivity.EXTRA_RUNTIME, 0); + startBitrate = intent.getIntExtra(ConnectActivity.EXTRA_BITRATE, 0); + hwCodec = intent.getBooleanExtra(ConnectActivity.EXTRA_HWCODEC, true); + if (url != null) { String room = url.getQueryParameter("r"); if (loopback || (room != null && !room.equals(""))) { @@ -452,11 +456,11 @@ public class AppRTCDemoActivity extends Activity } signalingParameters = params; abortUnless(PeerConnectionFactory.initializeAndroidGlobals( - this, true, true, VideoRendererGui.getEGLContext()), + this, true, true, hwCodec, VideoRendererGui.getEGLContext()), "Failed to initializeAndroidGlobals"); logAndToast("Creating peer connection..."); - pc = new PeerConnectionClient( - this, localRender, remoteRender, signalingParameters, this); + pc = new PeerConnectionClient( this, localRender, remoteRender, + signalingParameters, this, startBitrate); if (pc.isHDVideo()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { diff --git a/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java b/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java index 903ecf724..9fe6f5191 100644 --- a/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java @@ -65,6 +65,8 @@ public class ConnectActivity extends Activity { public static final String EXTRA_LOOPBACK = "org.appspot.apprtc.LOOPBACK"; public static final String EXTRA_CMDLINE = "org.appspot.apprtc.CMDLINE"; public static final String EXTRA_RUNTIME = "org.appspot.apprtc.RUNTIME"; + public static final String EXTRA_BITRATE = "org.appspot.apprtc.BITRATE"; + public static final String EXTRA_HWCODEC = "org.appspot.apprtc.HWCODEC"; private static final String TAG = "ConnectActivity"; private final boolean USE_WEBSOCKETS = false; private final String APPRTC_SERVER = "https://apprtc.appspot.com"; @@ -80,6 +82,9 @@ public class ConnectActivity extends Activity { private SharedPreferences sharedPref; private String keyprefResolution; private String keyprefFps; + private String keyprefBitrateType; + private String keyprefBitrateValue; + private String keyprefHwCodec; private String keyprefCpuUsageDetection; private String keyprefRoom; private String keyprefRoomList; @@ -97,6 +102,9 @@ public class ConnectActivity extends Activity { sharedPref = PreferenceManager.getDefaultSharedPreferences(this); 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); + keyprefHwCodec = getString(R.string.pref_hwcodec_key); keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key); keyprefRoom = getString(R.string.pref_room_key); keyprefRoomList = getString(R.string.pref_room_list_key); @@ -142,7 +150,7 @@ public class ConnectActivity extends Activity { if (loopback && !url.contains("debug=loopback")) { url += "/?debug=loopback"; } - connectToRoom(url, loopback); + connectToRoom(url, loopback, 0, true); return; } } @@ -234,9 +242,12 @@ public class ConnectActivity extends Activity { } url += "/?r=" + roomName; } + // Check HW codec flag. + boolean hwCodec = sharedPref.getBoolean(keyprefHwCodec, + Boolean.valueOf(getString(R.string.pref_hwcodec_default))); + // Add video resolution constraints. String parametersResolution = null; String parametersFps = null; - // Add video resolution constraints. String resolution = sharedPref.getString(keyprefResolution, getString(R.string.pref_resolution_default)); String[] dimensions = resolution.split("[ x]+"); @@ -280,10 +291,20 @@ public class ConnectActivity extends Activity { url += parametersFps; } } else { - if (MediaCodecVideoEncoder.isPlatformSupported()) { + if (hwCodec && MediaCodecVideoEncoder.isPlatformSupported()) { url += "&hd=true"; } } + // Get start bitrate. + int startBitrate = 0; + String bitrateTypeDefault = getString(R.string.pref_startbitrate_default); + String bitrateType = sharedPref.getString( + keyprefBitrateType, bitrateTypeDefault); + if (!bitrateType.equals(bitrateTypeDefault)) { + String bitrateValue = sharedPref.getString(keyprefBitrateValue, + getString(R.string.pref_startbitratevalue_default)); + startBitrate = Integer.parseInt(bitrateValue); + } // Test if CpuOveruseDetection should be disabled. By default is on. boolean cpuOveruseDetection = sharedPref.getBoolean( keyprefCpuUsageDetection, @@ -293,11 +314,12 @@ public class ConnectActivity extends Activity { url += "&googCpuOveruseDetection=false"; } // TODO(kjellander): Add support for custom parameters to the URL. - connectToRoom(url, loopback); + connectToRoom(url, loopback, startBitrate, hwCodec); } }; - private void connectToRoom(String roomUrl, boolean loopback) { + private void connectToRoom( + String roomUrl, boolean loopback, int startBitrate, boolean hwCodec) { if (validateUrl(roomUrl)) { Uri url = Uri.parse(roomUrl); Intent intent = new Intent(this, AppRTCDemoActivity.class); @@ -305,6 +327,8 @@ public class ConnectActivity extends Activity { intent.putExtra(EXTRA_LOOPBACK, loopback); intent.putExtra(EXTRA_CMDLINE, commandLineRun); intent.putExtra(EXTRA_RUNTIME, runTimeMs); + intent.putExtra(EXTRA_BITRATE, startBitrate); + intent.putExtra(EXTRA_HWCODEC, hwCodec); startActivityForResult(intent, CONNECTION_REQUEST); } } diff --git a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java index 0b357e02d..dfc0dd56c 100644 --- a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java +++ b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java @@ -73,6 +73,7 @@ public class PeerConnectionClient { private MediaConstraints sdpMediaConstraints; private MediaConstraints videoConstraints; private PeerConnectionEvents events; + private int startBitrate; private boolean isInitiator; private boolean useFrontFacingCamera = true; private SessionDescription localSdp = null; // either offer or answer SDP @@ -83,11 +84,13 @@ public class PeerConnectionClient { VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender, SignalingParameters signalingParameters, - PeerConnectionEvents events) { + PeerConnectionEvents events, + int startBitrate) { this.activity = activity; this.localRender = localRender; this.remoteRender = remoteRender; this.events = events; + this.startBitrate = startBitrate; isInitiator = signalingParameters.initiator; queuedRemoteCandidates = new LinkedList(); @@ -200,10 +203,14 @@ public class PeerConnectionClient { activity.runOnUiThread(new Runnable() { public void run() { if (pc != null) { - SessionDescription sdpISAC = new SessionDescription( - sdp.type, preferISAC(sdp.description)); - Log.d(TAG, "Set remote SDP"); - pc.setRemoteDescription(sdpObserver, sdpISAC); + String sdpDescription = preferISAC(sdp.description); + if (startBitrate > 0) { + sdpDescription = setStartBitrate(sdpDescription, startBitrate); + } + Log.d(TAG, "Set remote SDP."); + SessionDescription sdpRemote = new SessionDescription( + sdp.type, sdpDescription); + pc.setRemoteDescription(sdpObserver, sdpRemote); } } }); @@ -335,6 +342,39 @@ public class PeerConnectionClient { } } + private static String setStartBitrate( + String sdpDescription, int bitrateKbps) { + String[] lines = sdpDescription.split("\r\n"); + int lineIndex = -1; + String vp8RtpMap = null; + Pattern vp8Pattern = + Pattern.compile("^a=rtpmap:(\\d+) VP8/90000[\r]?$"); + for (int i = 0; i < lines.length; i++) { + Matcher vp8Matcher = vp8Pattern.matcher(lines[i]); + if (vp8Matcher.matches()) { + vp8RtpMap = vp8Matcher.group(1); + lineIndex = i; + break; + } + } + if (vp8RtpMap == null) { + Log.e(TAG, "No rtpmap for VP8 codec"); + return sdpDescription; + } + Log.d(TAG, "Found rtpmap " + vp8RtpMap + " at " + lines[lineIndex]); + 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:" + vp8RtpMap + + " x-google-start-bitrate=" + bitrateKbps; + Log.d(TAG, "Add remote SDP line: " + bitrateSet); + newSdpDescription.append(bitrateSet).append("\r\n"); + } + } + return newSdpDescription.toString(); + } + // Mangle SDP to prefer ISAC/16000 over any other audio codec. private static String preferISAC(String sdpDescription) { String[] lines = sdpDescription.split("\r\n"); diff --git a/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java b/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java index 367c834b9..4ca81ef00 100644 --- a/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java @@ -38,6 +38,9 @@ public class SettingsActivity extends Activity private SettingsFragment settingsFragment; private String keyprefResolution; private String keyprefFps; + private String keyprefStartBitrateType; + private String keyprefStartBitrateValue; + private String keyprefHwCodec; private String keyprefCpuUsageDetection; @Override @@ -45,6 +48,9 @@ public class SettingsActivity extends Activity super.onCreate(savedInstanceState); 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); + keyprefHwCodec = getString(R.string.pref_hwcodec_key); keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key); // Display the fragment as the main content. @@ -63,6 +69,10 @@ public class SettingsActivity extends Activity sharedPreferences.registerOnSharedPreferenceChangeListener(this); updateSummary(sharedPreferences, keyprefResolution); updateSummary(sharedPreferences, keyprefFps); + updateSummary(sharedPreferences, keyprefStartBitrateType); + updateSummaryBitrate(sharedPreferences, keyprefStartBitrateValue); + setBitrateEnable(sharedPreferences); + updateSummaryB(sharedPreferences, keyprefHwCodec); updateSummaryB(sharedPreferences, keyprefCpuUsageDetection); } @@ -77,11 +87,18 @@ public class SettingsActivity extends Activity @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(keyprefResolution) || key.equals(keyprefFps)) { + if (key.equals(keyprefResolution) || key.equals(keyprefFps) || + key.equals(keyprefStartBitrateType)) { updateSummary(sharedPreferences, key); - } else if (key.equals(keyprefCpuUsageDetection)) { + } else if (key.equals(keyprefStartBitrateValue)) { + updateSummaryBitrate(sharedPreferences, key); + } else if (key.equals(keyprefCpuUsageDetection) || + key.equals(keyprefHwCodec)) { updateSummaryB(sharedPreferences, key); } + if (key.equals(keyprefStartBitrateType)) { + setBitrateEnable(sharedPreferences); + } } private void updateSummary(SharedPreferences sharedPreferences, String key) { @@ -90,11 +107,30 @@ public class SettingsActivity extends Activity updatedPref.setSummary(sharedPreferences.getString(key, "")); } + private void updateSummaryBitrate( + SharedPreferences sharedPreferences, String key) { + Preference updatedPref = settingsFragment.findPreference(key); + updatedPref.setSummary(sharedPreferences.getString(key, "") + " kbps"); + } + private void updateSummaryB(SharedPreferences sharedPreferences, String key) { Preference updatedPref = settingsFragment.findPreference(key); updatedPref.setSummary(sharedPreferences.getBoolean(key, true) - ? getString(R.string.pref_cpu_usage_detection_on) - : getString(R.string.pref_cpu_usage_detection_off)); + ? getString(R.string.pref_value_enabled) + : getString(R.string.pref_value_disabled)); + } + + private void setBitrateEnable(SharedPreferences sharedPreferences) { + Preference bitratePreferenceValue = + settingsFragment.findPreference(keyprefStartBitrateValue); + String bitrateTypeDefault = getString(R.string.pref_startbitrate_default); + String bitrateType = sharedPreferences.getString( + keyprefStartBitrateType, bitrateTypeDefault); + if (bitrateType.equals(bitrateTypeDefault)) { + bitratePreferenceValue.setEnabled(false); + } else { + bitratePreferenceValue.setEnabled(true); + } } }