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