blob: de2b9199b44a38dbdb2074694d4cc950132cae2f [file] [log] [blame]
magjed6813ec82015-08-28 05:22:19 -07001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
magjed6813ec82015-08-28 05:22:19 -07003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * 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.
magjed6813ec82015-08-28 05:22:19 -07009 */
10
11package org.webrtc;
12
13import static java.lang.Math.abs;
magjed6813ec82015-08-28 05:22:19 -070014
sakal79ede032016-06-14 05:33:18 -070015import android.graphics.ImageFormat;
sakal9d1315a2016-11-10 06:35:17 -080016import java.util.ArrayList;
17import java.util.Arrays;
magjed6813ec82015-08-28 05:22:19 -070018import java.util.Collections;
19import java.util.Comparator;
20import java.util.List;
21
22@SuppressWarnings("deprecation")
23public class CameraEnumerationAndroid {
24 private final static String TAG = "CameraEnumerationAndroid";
magjed6813ec82015-08-28 05:22:19 -070025
sakal9d1315a2016-11-10 06:35:17 -080026 static final ArrayList<Size> COMMON_RESOLUTIONS = new ArrayList<Size>(Arrays.asList(
27 // 0, Unknown resolution
28 new Size(160, 120), // 1, QQVGA
29 new Size(240, 160), // 2, HQVGA
30 new Size(320, 240), // 3, QVGA
31 new Size(400, 240), // 4, WQVGA
32 new Size(480, 320), // 5, HVGA
33 new Size(640, 360), // 6, nHD
34 new Size(640, 480), // 7, VGA
35 new Size(768, 480), // 8, WVGA
36 new Size(854, 480), // 9, FWVGA
37 new Size(800, 600), // 10, SVGA
38 new Size(960, 540), // 11, qHD
39 new Size(960, 640), // 12, DVGA
40 new Size(1024, 576), // 13, WSVGA
41 new Size(1024, 600), // 14, WVSGA
42 new Size(1280, 720), // 15, HD
43 new Size(1280, 1024), // 16, SXGA
44 new Size(1920, 1080), // 17, Full HD
45 new Size(1920, 1440), // 18, Full HD 4:3
46 new Size(2560, 1440), // 19, QHD
47 new Size(3840, 2160) // 20, UHD
48 ));
49
magjed6813ec82015-08-28 05:22:19 -070050 public static class CaptureFormat {
magjede38a9362016-05-30 04:00:18 -070051 // Class to represent a framerate range. The framerate varies because of lightning conditions.
52 // The values are multiplied by 1000, so 1000 represents one frame per second.
53 public static class FramerateRange {
54 public int min;
55 public int max;
56
57 public FramerateRange(int min, int max) {
58 this.min = min;
59 this.max = max;
60 }
61
62 @Override
63 public String toString() {
64 return "[" + (min / 1000.0f) + ":" + (max / 1000.0f) + "]";
65 }
66
67 @Override
68 public boolean equals(Object other) {
69 if (!(other instanceof FramerateRange)) {
70 return false;
71 }
72 final FramerateRange otherFramerate = (FramerateRange) other;
73 return min == otherFramerate.min && max == otherFramerate.max;
74 }
75
76 @Override
77 public int hashCode() {
78 // Use prime close to 2^16 to avoid collisions for normal values less than 2^16.
79 return 1 + 65537 * min + max;
80 }
81 }
82
magjed6813ec82015-08-28 05:22:19 -070083 public final int width;
84 public final int height;
magjede38a9362016-05-30 04:00:18 -070085 public final FramerateRange framerate;
sakale6c9e882016-06-17 01:02:04 -070086
Magnus Jedvert5e7834e2016-02-12 17:05:29 +010087 // TODO(hbos): If VideoCapturer.startCapture is updated to support other image formats then this
88 // needs to be updated and VideoCapturer.getSupportedFormats need to return CaptureFormats of
magjed6813ec82015-08-28 05:22:19 -070089 // all imageFormats.
perkj88518a22015-12-18 00:37:06 -080090 public final int imageFormat = ImageFormat.NV21;
magjed6813ec82015-08-28 05:22:19 -070091
magjede38a9362016-05-30 04:00:18 -070092 public CaptureFormat(int width, int height, int minFramerate, int maxFramerate) {
magjed6813ec82015-08-28 05:22:19 -070093 this.width = width;
94 this.height = height;
magjede38a9362016-05-30 04:00:18 -070095 this.framerate = new FramerateRange(minFramerate, maxFramerate);
96 }
97
98 public CaptureFormat(int width, int height, FramerateRange framerate) {
99 this.width = width;
100 this.height = height;
101 this.framerate = framerate;
magjed6813ec82015-08-28 05:22:19 -0700102 }
103
104 // Calculates the frame size of this capture format.
105 public int frameSize() {
106 return frameSize(width, height, imageFormat);
107 }
108
109 // Calculates the frame size of the specified image format. Currently only
perkj88518a22015-12-18 00:37:06 -0800110 // supporting ImageFormat.NV21.
111 // The size is width * height * number of bytes per pixel.
112 // http://developer.android.com/reference/android/hardware/Camera.html#addCallbackBuffer(byte[])
magjed6813ec82015-08-28 05:22:19 -0700113 public static int frameSize(int width, int height, int imageFormat) {
perkj88518a22015-12-18 00:37:06 -0800114 if (imageFormat != ImageFormat.NV21) {
magjed6813ec82015-08-28 05:22:19 -0700115 throw new UnsupportedOperationException("Don't know how to calculate "
perkj88518a22015-12-18 00:37:06 -0800116 + "the frame size of non-NV21 image formats.");
magjed6813ec82015-08-28 05:22:19 -0700117 }
perkj88518a22015-12-18 00:37:06 -0800118 return (width * height * ImageFormat.getBitsPerPixel(imageFormat)) / 8;
magjed6813ec82015-08-28 05:22:19 -0700119 }
120
121 @Override
122 public String toString() {
magjede38a9362016-05-30 04:00:18 -0700123 return width + "x" + height + "@" + framerate;
magjed6813ec82015-08-28 05:22:19 -0700124 }
125
sakale6c9e882016-06-17 01:02:04 -0700126 @Override
127 public boolean equals(Object other) {
128 if (!(other instanceof CaptureFormat)) {
magjed6813ec82015-08-28 05:22:19 -0700129 return false;
130 }
sakale6c9e882016-06-17 01:02:04 -0700131 final CaptureFormat otherFormat = (CaptureFormat) other;
132 return width == otherFormat.width && height == otherFormat.height
133 && framerate.equals(otherFormat.framerate);
134 }
135
136 @Override
137 public int hashCode() {
138 return 1 + (width * 65497 + height) * 251 + framerate.hashCode();
magjed6813ec82015-08-28 05:22:19 -0700139 }
140 }
141
magjede38a9362016-05-30 04:00:18 -0700142 // Helper class for finding the closest supported format for the two functions below. It creates a
143 // comparator based on the difference to some requested parameters, where the element with the
144 // minimum difference is the element that is closest to the requested parameters.
magjed6813ec82015-08-28 05:22:19 -0700145 private static abstract class ClosestComparator<T> implements Comparator<T> {
146 // Difference between supported and requested parameter.
147 abstract int diff(T supportedParameter);
148
149 @Override
150 public int compare(T t1, T t2) {
151 return diff(t1) - diff(t2);
152 }
153 }
154
magjedce17e012016-06-01 00:43:59 -0700155 // Prefer a fps range with an upper bound close to |framerate|. Also prefer a fps range with a low
156 // lower bound, to allow the framerate to fluctuate based on lightning conditions.
magjede38a9362016-05-30 04:00:18 -0700157 public static CaptureFormat.FramerateRange getClosestSupportedFramerateRange(
158 List<CaptureFormat.FramerateRange> supportedFramerates, final int requestedFps) {
sakalb6760f92016-09-29 04:12:44 -0700159 return Collections.min(
160 supportedFramerates, new ClosestComparator<CaptureFormat.FramerateRange>() {
magjedce17e012016-06-01 00:43:59 -0700161 // Progressive penalty if the upper bound is further away than |MAX_FPS_DIFF_THRESHOLD|
162 // from requested.
163 private static final int MAX_FPS_DIFF_THRESHOLD = 5000;
164 private static final int MAX_FPS_LOW_DIFF_WEIGHT = 1;
165 private static final int MAX_FPS_HIGH_DIFF_WEIGHT = 3;
166
167 // Progressive penalty if the lower bound is bigger than |MIN_FPS_THRESHOLD|.
168 private static final int MIN_FPS_THRESHOLD = 8000;
169 private static final int MIN_FPS_LOW_VALUE_WEIGHT = 1;
170 private static final int MIN_FPS_HIGH_VALUE_WEIGHT = 4;
171
172 // Use one weight for small |value| less than |threshold|, and another weight above.
173 private int progressivePenalty(int value, int threshold, int lowWeight, int highWeight) {
sakalb6760f92016-09-29 04:12:44 -0700174 return (value < threshold) ? value * lowWeight
175 : threshold * lowWeight + (value - threshold) * highWeight;
magjedce17e012016-06-01 00:43:59 -0700176 }
magjede38a9362016-05-30 04:00:18 -0700177
178 @Override
179 int diff(CaptureFormat.FramerateRange range) {
sakalb6760f92016-09-29 04:12:44 -0700180 final int minFpsError = progressivePenalty(
181 range.min, MIN_FPS_THRESHOLD, MIN_FPS_LOW_VALUE_WEIGHT, MIN_FPS_HIGH_VALUE_WEIGHT);
magjedce17e012016-06-01 00:43:59 -0700182 final int maxFpsError = progressivePenalty(Math.abs(requestedFps * 1000 - range.max),
183 MAX_FPS_DIFF_THRESHOLD, MAX_FPS_LOW_DIFF_WEIGHT, MAX_FPS_HIGH_DIFF_WEIGHT);
184 return minFpsError + maxFpsError;
magjed6813ec82015-08-28 05:22:19 -0700185 }
sakalb6760f92016-09-29 04:12:44 -0700186 });
magjed6813ec82015-08-28 05:22:19 -0700187 }
188
sakale6c9e882016-06-17 01:02:04 -0700189 public static Size getClosestSupportedSize(
sakalb6760f92016-09-29 04:12:44 -0700190 List<Size> supportedSizes, final int requestedWidth, final int requestedHeight) {
191 return Collections.min(supportedSizes, new ClosestComparator<Size>() {
192 @Override
193 int diff(Size size) {
194 return abs(requestedWidth - size.width) + abs(requestedHeight - size.height);
195 }
196 });
magjed6813ec82015-08-28 05:22:19 -0700197 }
sakal9d1315a2016-11-10 06:35:17 -0800198
199 // Helper method for camera classes.
200 static void reportCameraResolution(Histogram histogram, Size resolution) {
201 int index = COMMON_RESOLUTIONS.indexOf(resolution);
202 // 0 is reserved for unknown resolution, so add 1.
203 // indexOf returns -1 for unknown resolutions so it becomes 0 automatically.
204 histogram.addSample(index + 1);
205 }
magjed6813ec82015-08-28 05:22:19 -0700206}