Add video encoder fps and bitrate statistics to

Android AppRTCDemo UI.

BUG=4045
R=jiayl@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/25229004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7747 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
glaznev@webrtc.org 2014-11-26 00:39:42 +00:00
parent 008731868a
commit 58edb83fd4
3 changed files with 145 additions and 78 deletions

View File

@ -12,6 +12,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<TextView
android:id="@+id/encoder_stat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textStyle="bold"
android:textColor="#800000FF"
android:textSize="12dp"
android:layout_margin="8dp"/>
<TextView <TextView
android:id="@+id/room_name" android:id="@+id/room_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -27,6 +27,9 @@
package org.appspot.apprtc; package org.appspot.apprtc;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Fragment; import android.app.Fragment;
@ -83,6 +86,7 @@ public class AppRTCDemoActivity extends Activity
private final LayoutParams hudLayout = private final LayoutParams hudLayout =
new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
private TextView hudView; private TextView hudView;
private TextView encoderStatView;
private TextView roomName; private TextView roomName;
private ImageButton videoScalingButton; private ImageButton videoScalingButton;
private boolean commandLineRun; private boolean commandLineRun;
@ -111,6 +115,7 @@ public class AppRTCDemoActivity extends Activity
iceConnected = false; iceConnected = false;
rootView = findViewById(android.R.id.content); rootView = findViewById(android.R.id.content);
encoderStatView = (TextView)findViewById(R.id.encoder_stat);
menuBar = findViewById(R.id.menubar_fragment); menuBar = findViewById(R.id.menubar_fragment);
roomName = (TextView) findViewById(R.id.room_name); roomName = (TextView) findViewById(R.id.room_name);
videoView = (GLSurfaceView) findViewById(R.id.glview); videoView = (GLSurfaceView) findViewById(R.id.glview);
@ -126,9 +131,11 @@ public class AppRTCDemoActivity extends Activity
public void onClick(View view) { public void onClick(View view) {
int visibility = menuBar.getVisibility() == View.VISIBLE int visibility = menuBar.getVisibility() == View.VISIBLE
? View.INVISIBLE : View.VISIBLE; ? View.INVISIBLE : View.VISIBLE;
encoderStatView.setVisibility(visibility);
menuBar.setVisibility(visibility); menuBar.setVisibility(visibility);
roomName.setVisibility(visibility); roomName.setVisibility(visibility);
if (visibility == View.VISIBLE) { if (visibility == View.VISIBLE) {
encoderStatView.bringToFront();
menuBar.bringToFront(); menuBar.bringToFront();
roomName.bringToFront(); roomName.bringToFront();
rootView.invalidate(); rootView.invalidate();
@ -283,53 +290,6 @@ public class AppRTCDemoActivity extends Activity
} }
} }
// Update the heads-up display with information from |reports|.
private void updateHUD(StatsReport[] reports) {
StringBuilder builder = new StringBuilder();
for (StatsReport report : reports) {
// bweforvideo to show statistics for video Bandwidth Estimation,
// which is global per-session.
if (report.id.equals("bweforvideo")) {
for (StatsReport.Value value : report.values) {
String name = value.name.replace("goog", "")
.replace("Available", "").replace("Bandwidth", "")
.replace("Bitrate", "").replace("Enc", "");
builder.append(name).append("=").append(value.value)
.append(" ");
}
builder.append("\n");
} else if (report.type.equals("googCandidatePair")) {
String activeConnectionStats = getActiveConnectionStats(report);
if (activeConnectionStats == null) {
continue;
}
builder.append(activeConnectionStats);
} else {
continue;
}
builder.append("\n");
}
hudView.setText(builder.toString() + hudView.getText());
}
// Return the active connection stats else return null
private String getActiveConnectionStats(StatsReport report) {
StringBuilder activeConnectionbuilder = new StringBuilder();
// googCandidatePair to show information about the active
// connection.
for (StatsReport.Value value : report.values) {
if (value.name.equals("googActiveConnection")
&& value.value.equals("false")) {
return null;
}
String name = value.name.replace("goog", "");
activeConnectionbuilder.append(name).append("=")
.append(value.value).append("\n");
}
return activeConnectionbuilder.toString();
}
// Disconnect from remote resources, dispose of local resources, and exit. // Disconnect from remote resources, dispose of local resources, and exit.
private void disconnect() { private void disconnect() {
if (appRtcClient != null) { if (appRtcClient != null) {
@ -387,6 +347,99 @@ public class AppRTCDemoActivity extends Activity
logToast.show(); logToast.show();
} }
// Return the active connection stats,
// or null if active connection is not found.
private String getActiveConnectionStats(StatsReport report) {
StringBuilder activeConnectionbuilder = new StringBuilder();
// googCandidatePair to show information about the active
// connection.
for (StatsReport.Value value : report.values) {
if (value.name.equals("googActiveConnection")
&& value.value.equals("false")) {
return null;
}
String name = value.name.replace("goog", "");
activeConnectionbuilder.append(name).append("=")
.append(value.value).append("\n");
}
return activeConnectionbuilder.toString();
}
// Update the heads-up display with information from |reports|.
private void updateHUD(StatsReport[] reports) {
StringBuilder builder = new StringBuilder();
for (StatsReport report : reports) {
Log.d(TAG, "Stats: " + report.toString());
// bweforvideo to show statistics for video Bandwidth Estimation,
// which is global per-session.
if (report.id.equals("bweforvideo")) {
for (StatsReport.Value value : report.values) {
String name = value.name.replace("goog", "")
.replace("Available", "").replace("Bandwidth", "")
.replace("Bitrate", "").replace("Enc", "");
builder.append(name).append("=").append(value.value)
.append(" ");
}
builder.append("\n");
} else if (report.type.equals("googCandidatePair")) {
String activeConnectionStats = getActiveConnectionStats(report);
if (activeConnectionStats == null) {
continue;
}
builder.append(activeConnectionStats);
} else {
continue;
}
builder.append("\n");
}
hudView.setText(builder.toString() + hudView.getText());
}
private Map<String, String> getReportMap(StatsReport report) {
Map<String, String> reportMap = new HashMap<String, String>();
for (StatsReport.Value value : report.values) {
reportMap.put(value.name, value.value);
}
return reportMap;
}
// Update encoder statistics view with information from |reports|.
private void updateEncoderStatistics(StatsReport[] reports) {
if (!iceConnected) {
return;
}
String fps = null;
String targetBitrate = null;
String actualBitrate = null;
for (StatsReport report : reports) {
if (report.type.equals("ssrc") && report.id.contains("ssrc") &&
report.id.contains("send")) {
Map<String, String> reportMap = getReportMap(report);
String trackId = reportMap.get("googTrackId");
if (trackId != null &&
trackId.contains(PeerConnectionClient.VIDEO_TRACK_ID)) {
fps = reportMap.get("googFrameRateSent");
}
} else if (report.id.equals("bweforvideo")) {
Map<String, String> reportMap = getReportMap(report);
targetBitrate = reportMap.get("googTargetEncBitrate");
actualBitrate = reportMap.get("googActualEncBitrate");
}
}
String stat = "";
if (fps != null) {
stat += "Fps: " + fps + "\n";
}
if (targetBitrate != null) {
stat += "Target BR: " + targetBitrate + "\n";
}
if (actualBitrate != null) {
stat += "Actual BR: " + actualBitrate;
}
encoderStatView.setText(stat);
}
// -----Implementation of AppRTCClient.AppRTCSignalingEvents --------------- // -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
// All events are called from UI thread. // All events are called from UI thread.
@Override @Override
@ -410,37 +463,39 @@ public class AppRTCDemoActivity extends Activity
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
} }
{ // Schedule statistics display.
final Runnable repeatedStatsLogger = new Runnable() { final Runnable repeatedStatsLogger = new Runnable() {
public void run() { public void run() {
if (pc == null) { if (pc == null) {
return; return;
} }
final Runnable runnableThis = this; final Runnable runnableThis = this;
if (hudView.getVisibility() == View.INVISIBLE) { if (hudView.getVisibility() == View.INVISIBLE &&
videoView.postDelayed(runnableThis, 1000); encoderStatView.getVisibility() == View.INVISIBLE) {
return; videoView.postDelayed(runnableThis, 1000);
} return;
boolean success = pc.getStats(new StatsObserver() { }
public void onComplete(final StatsReport[] reports) { boolean success = pc.getStats(new StatsObserver() {
runOnUiThread(new Runnable() { public void onComplete(final StatsReport[] reports) {
public void run() { runOnUiThread(new Runnable() {
updateHUD(reports); public void run() {
} if (hudView.getVisibility() == View.VISIBLE) {
}); updateHUD(reports);
for (StatsReport report : reports) { }
Log.d(TAG, "Stats: " + report.toString()); if (encoderStatView.getVisibility() == View.VISIBLE) {
updateEncoderStatistics(reports);
}
} }
videoView.postDelayed(runnableThis, 1000); });
} videoView.postDelayed(runnableThis, 1000);
}, null);
if (!success) {
throw new RuntimeException("getStats() return false!");
} }
} }, null);
}; if (!success) {
videoView.postDelayed(repeatedStatsLogger, 1000); throw new RuntimeException("getStats() return false!");
} }
}
};
videoView.postDelayed(repeatedStatsLogger, 1000);
logAndToast("Waiting for remote connection..."); logAndToast("Waiting for remote connection...");
} }

View File

@ -54,6 +54,9 @@ import java.util.regex.Pattern;
public class PeerConnectionClient { public class PeerConnectionClient {
private static final String TAG = "PCRTCClient"; private static final String TAG = "PCRTCClient";
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
public static final String AUDIO_TRACK_ID = "ARDAMSa0";
private final Activity activity; private final Activity activity;
private PeerConnectionFactory factory; private PeerConnectionFactory factory;
private PeerConnection pc; private PeerConnection pc;
@ -119,7 +122,7 @@ public class PeerConnectionClient {
if (signalingParameters.audioConstraints != null) { if (signalingParameters.audioConstraints != null) {
MediaStream lMS = factory.createLocalMediaStream("ARDAMSAudio"); MediaStream lMS = factory.createLocalMediaStream("ARDAMSAudio");
lMS.addTrack(factory.createAudioTrack( lMS.addTrack(factory.createAudioTrack(
"ARDAMSa0", AUDIO_TRACK_ID,
factory.createAudioSource(signalingParameters.audioConstraints))); factory.createAudioSource(signalingParameters.audioConstraints)));
pc.addStream(lMS); pc.addStream(lMS);
} }
@ -320,7 +323,7 @@ public class PeerConnectionClient {
capturer, videoConstraints); capturer, videoConstraints);
String trackExtension = frontFacing ? "frontFacing" : "backFacing"; String trackExtension = frontFacing ? "frontFacing" : "backFacing";
VideoTrack videoTrack = VideoTrack videoTrack =
factory.createVideoTrack("ARDAMSv0" + trackExtension, videoSource); factory.createVideoTrack(VIDEO_TRACK_ID + trackExtension, videoSource);
videoTrack.addRenderer(new VideoRenderer(localRender)); videoTrack.addRenderer(new VideoRenderer(localRender));
return videoTrack; return videoTrack;
} }