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