xref: /aosp_15_r20/external/webrtc/sdk/android/api/org/webrtc/CameraEnumerationAndroid.java (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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.webrtc;
12 
13 import static java.lang.Math.abs;
14 
15 import android.graphics.ImageFormat;
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.List;
21 
22 @SuppressWarnings("deprecation")
23 public class CameraEnumerationAndroid {
24   private final static String TAG = "CameraEnumerationAndroid";
25 
26   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 
50   public static class CaptureFormat {
51     // 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 
FramerateRange(int min, int max)57       public FramerateRange(int min, int max) {
58         this.min = min;
59         this.max = max;
60       }
61 
62       @Override
toString()63       public String toString() {
64         return "[" + (min / 1000.0f) + ":" + (max / 1000.0f) + "]";
65       }
66 
67       @Override
equals(Object other)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
hashCode()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 
83     public final int width;
84     public final int height;
85     public final FramerateRange framerate;
86 
87     // 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
89     // all imageFormats.
90     public final int imageFormat = ImageFormat.NV21;
91 
CaptureFormat(int width, int height, int minFramerate, int maxFramerate)92     public CaptureFormat(int width, int height, int minFramerate, int maxFramerate) {
93       this.width = width;
94       this.height = height;
95       this.framerate = new FramerateRange(minFramerate, maxFramerate);
96     }
97 
CaptureFormat(int width, int height, FramerateRange framerate)98     public CaptureFormat(int width, int height, FramerateRange framerate) {
99       this.width = width;
100       this.height = height;
101       this.framerate = framerate;
102     }
103 
104     // Calculates the frame size of this capture format.
frameSize()105     public int frameSize() {
106       return frameSize(width, height, imageFormat);
107     }
108 
109     // Calculates the frame size of the specified image format. Currently only
110     // 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[])
frameSize(int width, int height, int imageFormat)113     public static int frameSize(int width, int height, int imageFormat) {
114       if (imageFormat != ImageFormat.NV21) {
115         throw new UnsupportedOperationException("Don't know how to calculate "
116             + "the frame size of non-NV21 image formats.");
117       }
118       return (width * height * ImageFormat.getBitsPerPixel(imageFormat)) / 8;
119     }
120 
121     @Override
toString()122     public String toString() {
123       return width + "x" + height + "@" + framerate;
124     }
125 
126     @Override
equals(Object other)127     public boolean equals(Object other) {
128       if (!(other instanceof CaptureFormat)) {
129         return false;
130       }
131       final CaptureFormat otherFormat = (CaptureFormat) other;
132       return width == otherFormat.width && height == otherFormat.height
133           && framerate.equals(otherFormat.framerate);
134     }
135 
136     @Override
hashCode()137     public int hashCode() {
138       return 1 + (width * 65497 + height) * 251 + framerate.hashCode();
139     }
140   }
141 
142   // 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.
145   private static abstract class ClosestComparator<T> implements Comparator<T> {
146     // Difference between supported and requested parameter.
diff(T supportedParameter)147     abstract int diff(T supportedParameter);
148 
149     @Override
compare(T t1, T t2)150     public int compare(T t1, T t2) {
151       return diff(t1) - diff(t2);
152     }
153   }
154 
155   // 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.
getClosestSupportedFramerateRange( List<CaptureFormat.FramerateRange> supportedFramerates, final int requestedFps)157   public static CaptureFormat.FramerateRange getClosestSupportedFramerateRange(
158       List<CaptureFormat.FramerateRange> supportedFramerates, final int requestedFps) {
159     return Collections.min(
160         supportedFramerates, new ClosestComparator<CaptureFormat.FramerateRange>() {
161           // 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) {
174             return (value < threshold) ? value * lowWeight
175                                        : threshold * lowWeight + (value - threshold) * highWeight;
176           }
177 
178           @Override
179           int diff(CaptureFormat.FramerateRange range) {
180             final int minFpsError = progressivePenalty(
181                 range.min, MIN_FPS_THRESHOLD, MIN_FPS_LOW_VALUE_WEIGHT, MIN_FPS_HIGH_VALUE_WEIGHT);
182             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;
185           }
186         });
187   }
188 
getClosestSupportedSize( List<Size> supportedSizes, final int requestedWidth, final int requestedHeight)189   public static Size getClosestSupportedSize(
190       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     });
197   }
198 
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   }
206 }
207