1 /* 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.appspot.apprtc; 12 13 import android.app.Fragment; 14 import android.os.Bundle; 15 import android.util.TypedValue; 16 import android.view.LayoutInflater; 17 import android.view.View; 18 import android.view.ViewGroup; 19 import android.widget.ImageButton; 20 import android.widget.TextView; 21 22 import org.webrtc.StatsReport; 23 24 import java.util.HashMap; 25 import java.util.Map; 26 27 /** 28 * Fragment for HUD statistics display. 29 */ 30 public class HudFragment extends Fragment { 31 private TextView encoderStatView; 32 private TextView hudViewBwe; 33 private TextView hudViewConnection; 34 private TextView hudViewVideoSend; 35 private TextView hudViewVideoRecv; 36 private ImageButton toggleDebugButton; 37 private boolean videoCallEnabled; 38 private boolean displayHud; 39 private volatile boolean isRunning; 40 private CpuMonitor cpuMonitor; 41 42 @Override onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)43 public View onCreateView( 44 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 45 View controlView = inflater.inflate(R.layout.fragment_hud, container, false); 46 47 // Create UI controls. 48 encoderStatView = controlView.findViewById(R.id.encoder_stat_call); 49 hudViewBwe = controlView.findViewById(R.id.hud_stat_bwe); 50 hudViewConnection = controlView.findViewById(R.id.hud_stat_connection); 51 hudViewVideoSend = controlView.findViewById(R.id.hud_stat_video_send); 52 hudViewVideoRecv = controlView.findViewById(R.id.hud_stat_video_recv); 53 toggleDebugButton = controlView.findViewById(R.id.button_toggle_debug); 54 55 toggleDebugButton.setOnClickListener(new View.OnClickListener() { 56 @Override 57 public void onClick(View view) { 58 if (displayHud) { 59 int visibility = 60 (hudViewBwe.getVisibility() == View.VISIBLE) ? View.INVISIBLE : View.VISIBLE; 61 hudViewsSetProperties(visibility); 62 } 63 } 64 }); 65 66 return controlView; 67 } 68 69 @Override onStart()70 public void onStart() { 71 super.onStart(); 72 73 Bundle args = getArguments(); 74 if (args != null) { 75 videoCallEnabled = args.getBoolean(CallActivity.EXTRA_VIDEO_CALL, true); 76 displayHud = args.getBoolean(CallActivity.EXTRA_DISPLAY_HUD, false); 77 } 78 int visibility = displayHud ? View.VISIBLE : View.INVISIBLE; 79 encoderStatView.setVisibility(visibility); 80 toggleDebugButton.setVisibility(visibility); 81 hudViewsSetProperties(View.INVISIBLE); 82 isRunning = true; 83 } 84 85 @Override onStop()86 public void onStop() { 87 isRunning = false; 88 super.onStop(); 89 } 90 setCpuMonitor(CpuMonitor cpuMonitor)91 public void setCpuMonitor(CpuMonitor cpuMonitor) { 92 this.cpuMonitor = cpuMonitor; 93 } 94 hudViewsSetProperties(int visibility)95 private void hudViewsSetProperties(int visibility) { 96 hudViewBwe.setVisibility(visibility); 97 hudViewConnection.setVisibility(visibility); 98 hudViewVideoSend.setVisibility(visibility); 99 hudViewVideoRecv.setVisibility(visibility); 100 hudViewBwe.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5); 101 hudViewConnection.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5); 102 hudViewVideoSend.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5); 103 hudViewVideoRecv.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5); 104 } 105 getReportMap(StatsReport report)106 private Map<String, String> getReportMap(StatsReport report) { 107 Map<String, String> reportMap = new HashMap<>(); 108 for (StatsReport.Value value : report.values) { 109 reportMap.put(value.name, value.value); 110 } 111 return reportMap; 112 } 113 updateEncoderStatistics(final StatsReport[] reports)114 public void updateEncoderStatistics(final StatsReport[] reports) { 115 if (!isRunning || !displayHud) { 116 return; 117 } 118 StringBuilder encoderStat = new StringBuilder(128); 119 StringBuilder bweStat = new StringBuilder(); 120 StringBuilder connectionStat = new StringBuilder(); 121 StringBuilder videoSendStat = new StringBuilder(); 122 StringBuilder videoRecvStat = new StringBuilder(); 123 String fps = null; 124 String targetBitrate = null; 125 String actualBitrate = null; 126 127 for (StatsReport report : reports) { 128 if (report.type.equals("ssrc") && report.id.contains("ssrc") && report.id.contains("send")) { 129 // Send video statistics. 130 Map<String, String> reportMap = getReportMap(report); 131 String trackId = reportMap.get("googTrackId"); 132 if (trackId != null && trackId.contains(PeerConnectionClient.VIDEO_TRACK_ID)) { 133 fps = reportMap.get("googFrameRateSent"); 134 videoSendStat.append(report.id).append("\n"); 135 for (StatsReport.Value value : report.values) { 136 String name = value.name.replace("goog", ""); 137 videoSendStat.append(name).append("=").append(value.value).append("\n"); 138 } 139 } 140 } else if (report.type.equals("ssrc") && report.id.contains("ssrc") 141 && report.id.contains("recv")) { 142 // Receive video statistics. 143 Map<String, String> reportMap = getReportMap(report); 144 // Check if this stat is for video track. 145 String frameWidth = reportMap.get("googFrameWidthReceived"); 146 if (frameWidth != null) { 147 videoRecvStat.append(report.id).append("\n"); 148 for (StatsReport.Value value : report.values) { 149 String name = value.name.replace("goog", ""); 150 videoRecvStat.append(name).append("=").append(value.value).append("\n"); 151 } 152 } 153 } else if (report.id.equals("bweforvideo")) { 154 // BWE statistics. 155 Map<String, String> reportMap = getReportMap(report); 156 targetBitrate = reportMap.get("googTargetEncBitrate"); 157 actualBitrate = reportMap.get("googActualEncBitrate"); 158 159 bweStat.append(report.id).append("\n"); 160 for (StatsReport.Value value : report.values) { 161 String name = value.name.replace("goog", "").replace("Available", ""); 162 bweStat.append(name).append("=").append(value.value).append("\n"); 163 } 164 } else if (report.type.equals("googCandidatePair")) { 165 // Connection statistics. 166 Map<String, String> reportMap = getReportMap(report); 167 String activeConnection = reportMap.get("googActiveConnection"); 168 if (activeConnection != null && activeConnection.equals("true")) { 169 connectionStat.append(report.id).append("\n"); 170 for (StatsReport.Value value : report.values) { 171 String name = value.name.replace("goog", ""); 172 connectionStat.append(name).append("=").append(value.value).append("\n"); 173 } 174 } 175 } 176 } 177 hudViewBwe.setText(bweStat.toString()); 178 hudViewConnection.setText(connectionStat.toString()); 179 hudViewVideoSend.setText(videoSendStat.toString()); 180 hudViewVideoRecv.setText(videoRecvStat.toString()); 181 182 if (videoCallEnabled) { 183 if (fps != null) { 184 encoderStat.append("Fps: ").append(fps).append("\n"); 185 } 186 if (targetBitrate != null) { 187 encoderStat.append("Target BR: ").append(targetBitrate).append("\n"); 188 } 189 if (actualBitrate != null) { 190 encoderStat.append("Actual BR: ").append(actualBitrate).append("\n"); 191 } 192 } 193 194 if (cpuMonitor != null) { 195 encoderStat.append("CPU%: ") 196 .append(cpuMonitor.getCpuUsageCurrent()) 197 .append("/") 198 .append(cpuMonitor.getCpuUsageAverage()) 199 .append(". Freq: ") 200 .append(cpuMonitor.getFrequencyScaleAverage()); 201 } 202 encoderStatView.setText(encoderStat.toString()); 203 } 204 } 205