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.widget.SeekBar;
14 import android.widget.TextView;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.List;
19 import org.webrtc.CameraEnumerationAndroid.CaptureFormat;
20 
21 /**
22  * Control capture format based on a seekbar listener.
23  */
24 public class CaptureQualityController implements SeekBar.OnSeekBarChangeListener {
25   private final List<CaptureFormat> formats =
26       Arrays.asList(new CaptureFormat(1280, 720, 0, 30000), new CaptureFormat(960, 540, 0, 30000),
27           new CaptureFormat(640, 480, 0, 30000), new CaptureFormat(480, 360, 0, 30000),
28           new CaptureFormat(320, 240, 0, 30000), new CaptureFormat(256, 144, 0, 30000));
29   // Prioritize framerate below this threshold and resolution above the threshold.
30   private static final int FRAMERATE_THRESHOLD = 15;
31   private TextView captureFormatText;
32   private CallFragment.OnCallEvents callEvents;
33   private int width;
34   private int height;
35   private int framerate;
36   private double targetBandwidth;
37 
CaptureQualityController( TextView captureFormatText, CallFragment.OnCallEvents callEvents)38   public CaptureQualityController(
39       TextView captureFormatText, CallFragment.OnCallEvents callEvents) {
40     this.captureFormatText = captureFormatText;
41     this.callEvents = callEvents;
42   }
43 
44   private final Comparator<CaptureFormat> compareFormats = new Comparator<CaptureFormat>() {
45     @Override
46     public int compare(CaptureFormat first, CaptureFormat second) {
47       int firstFps = calculateFramerate(targetBandwidth, first);
48       int secondFps = calculateFramerate(targetBandwidth, second);
49 
50       if ((firstFps >= FRAMERATE_THRESHOLD && secondFps >= FRAMERATE_THRESHOLD)
51           || firstFps == secondFps) {
52         // Compare resolution.
53         return first.width * first.height - second.width * second.height;
54       } else {
55         // Compare fps.
56         return firstFps - secondFps;
57       }
58     }
59   };
60 
61   @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)62   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
63     if (progress == 0) {
64       width = 0;
65       height = 0;
66       framerate = 0;
67       captureFormatText.setText(R.string.muted);
68       return;
69     }
70 
71     // Extract max bandwidth (in millipixels / second).
72     long maxCaptureBandwidth = java.lang.Long.MIN_VALUE;
73     for (CaptureFormat format : formats) {
74       maxCaptureBandwidth =
75           Math.max(maxCaptureBandwidth, (long) format.width * format.height * format.framerate.max);
76     }
77 
78     // Fraction between 0 and 1.
79     double bandwidthFraction = (double) progress / 100.0;
80     // Make a log-scale transformation, still between 0 and 1.
81     final double kExpConstant = 3.0;
82     bandwidthFraction =
83         (Math.exp(kExpConstant * bandwidthFraction) - 1) / (Math.exp(kExpConstant) - 1);
84     targetBandwidth = bandwidthFraction * maxCaptureBandwidth;
85 
86     // Choose the best format given a target bandwidth.
87     final CaptureFormat bestFormat = Collections.max(formats, compareFormats);
88     width = bestFormat.width;
89     height = bestFormat.height;
90     framerate = calculateFramerate(targetBandwidth, bestFormat);
91     captureFormatText.setText(
92         String.format(captureFormatText.getContext().getString(R.string.format_description), width,
93             height, framerate));
94   }
95 
96   @Override
onStartTrackingTouch(SeekBar seekBar)97   public void onStartTrackingTouch(SeekBar seekBar) {}
98 
99   @Override
onStopTrackingTouch(SeekBar seekBar)100   public void onStopTrackingTouch(SeekBar seekBar) {
101     callEvents.onCaptureFormatChange(width, height, framerate);
102   }
103 
104   // Return the highest frame rate possible based on bandwidth and format.
calculateFramerate(double bandwidth, CaptureFormat format)105   private int calculateFramerate(double bandwidth, CaptureFormat format) {
106     return (int) Math.round(
107         Math.min(format.framerate.max, (int) Math.round(bandwidth / (format.width * format.height)))
108         / 1000.0);
109   }
110 }
111