/* * libjingle * Copyright 2015 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.appspot.apprtc; import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; import org.webrtc.StatsReport; import org.webrtc.VideoRendererGui.ScalingType; import java.util.HashMap; import java.util.Map; /** * Fragment for call control. */ public class CallFragment extends Fragment { private View controlView; private TextView encoderStatView; private TextView roomIdView; private ImageButton disconnectButton; private ImageButton cameraSwitchButton; private ImageButton videoScalingButton; private ImageButton toggleDebugButton; private OnCallEvents callEvents; private ScalingType scalingType; private boolean displayHud; private volatile boolean isRunning; private TextView hudView; private final CpuMonitor cpuMonitor = new CpuMonitor(); /** * Call control interface for container activity. */ public interface OnCallEvents { public void onCallHangUp(); public void onCameraSwitch(); public void onVideoScalingSwitch(ScalingType scalingType); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { controlView = inflater.inflate(R.layout.fragment_call, container, false); // Create UI controls. encoderStatView = (TextView) controlView.findViewById(R.id.encoder_stat_call); roomIdView = (TextView) controlView.findViewById(R.id.contact_name_call); hudView = (TextView) controlView.findViewById(R.id.hud_stat_call); disconnectButton = (ImageButton) controlView.findViewById(R.id.button_call_disconnect); cameraSwitchButton = (ImageButton) controlView.findViewById(R.id.button_call_switch_camera); videoScalingButton = (ImageButton) controlView.findViewById(R.id.button_call_scaling_mode); toggleDebugButton = (ImageButton) controlView.findViewById(R.id.button_toggle_debug); // Add buttons click events. disconnectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { callEvents.onCallHangUp(); } }); cameraSwitchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { callEvents.onCameraSwitch(); } }); videoScalingButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (scalingType == ScalingType.SCALE_ASPECT_FILL) { videoScalingButton.setBackgroundResource( R.drawable.ic_action_full_screen); scalingType = ScalingType.SCALE_ASPECT_FIT; } else { videoScalingButton.setBackgroundResource( R.drawable.ic_action_return_from_full_screen); scalingType = ScalingType.SCALE_ASPECT_FILL; } callEvents.onVideoScalingSwitch(scalingType); } }); scalingType = ScalingType.SCALE_ASPECT_FILL; toggleDebugButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (displayHud) { int visibility = (hudView.getVisibility() == View.VISIBLE) ? View.INVISIBLE : View.VISIBLE; hudView.setVisibility(visibility); } } }); return controlView; } @Override public void onStart() { super.onStart(); Bundle args = getArguments(); if (args != null) { String roomId = args.getString(CallActivity.EXTRA_ROOMID); roomIdView.setText(roomId); displayHud = args.getBoolean(CallActivity.EXTRA_DISPLAY_HUD, false); } int visibility = displayHud ? View.VISIBLE : View.INVISIBLE; encoderStatView.setVisibility(visibility); toggleDebugButton.setVisibility(visibility); hudView.setVisibility(View.INVISIBLE); hudView.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5); isRunning = true; } @Override public void onStop() { isRunning = false; super.onStop(); } @Override public void onAttach(Activity activity) { super.onAttach(activity); callEvents = (OnCallEvents) activity; } private Map getReportMap(StatsReport report) { Map reportMap = new HashMap(); for (StatsReport.Value value : report.values) { reportMap.put(value.name, value.value); } return reportMap; } public void updateEncoderStatistics(final StatsReport[] reports) { if (!isRunning || !displayHud) { return; } String fps = null; String targetBitrate = null; String actualBitrate = null; StringBuilder bweBuilder = new StringBuilder(); for (StatsReport report : reports) { if (report.type.equals("ssrc") && report.id.contains("ssrc") && report.id.contains("send")) { Map 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 reportMap = getReportMap(report); targetBitrate = reportMap.get("googTargetEncBitrate"); actualBitrate = reportMap.get("googActualEncBitrate"); for (StatsReport.Value value : report.values) { String name = value.name.replace("goog", "") .replace("Available", "").replace("Bandwidth", "") .replace("Bitrate", "").replace("Enc", ""); bweBuilder.append(name).append("=").append(value.value) .append(" "); } bweBuilder.append("\n"); } } StringBuilder stat = new StringBuilder(128); if (fps != null) { stat.append("Fps: ") .append(fps) .append("\n"); } if (targetBitrate != null) { stat.append("Target BR: ") .append(targetBitrate) .append("\n"); } if (actualBitrate != null) { stat.append("Actual BR: ") .append(actualBitrate) .append("\n"); } if (cpuMonitor.sampleCpuUtilization()) { stat.append("CPU%: ") .append(cpuMonitor.getCpuCurrent()) .append("/") .append(cpuMonitor.getCpuAvg3()) .append("/") .append(cpuMonitor.getCpuAvgAll()); } encoderStatView.setText(stat.toString()); hudView.setText(bweBuilder.toString() + hudView.getText()); } }