OLD | NEW |
| (Empty) |
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 | |
17 import java.util.Collections; | |
18 import java.util.Comparator; | |
19 import java.util.List; | |
20 | |
21 @SuppressWarnings("deprecation") | |
22 public class CameraEnumerationAndroid { | |
23 private final static String TAG = "CameraEnumerationAndroid"; | |
24 | |
25 public static class CaptureFormat { | |
26 // Class to represent a framerate range. The framerate varies because of lig
htning conditions. | |
27 // The values are multiplied by 1000, so 1000 represents one frame per secon
d. | |
28 public static class FramerateRange { | |
29 public int min; | |
30 public int max; | |
31 | |
32 public FramerateRange(int min, int max) { | |
33 this.min = min; | |
34 this.max = max; | |
35 } | |
36 | |
37 @Override | |
38 public String toString() { | |
39 return "[" + (min / 1000.0f) + ":" + (max / 1000.0f) + "]"; | |
40 } | |
41 | |
42 @Override | |
43 public boolean equals(Object other) { | |
44 if (!(other instanceof FramerateRange)) { | |
45 return false; | |
46 } | |
47 final FramerateRange otherFramerate = (FramerateRange) other; | |
48 return min == otherFramerate.min && max == otherFramerate.max; | |
49 } | |
50 | |
51 @Override | |
52 public int hashCode() { | |
53 // Use prime close to 2^16 to avoid collisions for normal values less th
an 2^16. | |
54 return 1 + 65537 * min + max; | |
55 } | |
56 } | |
57 | |
58 public final int width; | |
59 public final int height; | |
60 public final FramerateRange framerate; | |
61 | |
62 // TODO(hbos): If VideoCapturer.startCapture is updated to support other ima
ge formats then this | |
63 // needs to be updated and VideoCapturer.getSupportedFormats need to return
CaptureFormats of | |
64 // all imageFormats. | |
65 public final int imageFormat = ImageFormat.NV21; | |
66 | |
67 public CaptureFormat(int width, int height, int minFramerate, int maxFramera
te) { | |
68 this.width = width; | |
69 this.height = height; | |
70 this.framerate = new FramerateRange(minFramerate, maxFramerate); | |
71 } | |
72 | |
73 public CaptureFormat(int width, int height, FramerateRange framerate) { | |
74 this.width = width; | |
75 this.height = height; | |
76 this.framerate = framerate; | |
77 } | |
78 | |
79 // Calculates the frame size of this capture format. | |
80 public int frameSize() { | |
81 return frameSize(width, height, imageFormat); | |
82 } | |
83 | |
84 // Calculates the frame size of the specified image format. Currently only | |
85 // supporting ImageFormat.NV21. | |
86 // The size is width * height * number of bytes per pixel. | |
87 // http://developer.android.com/reference/android/hardware/Camera.html#addCa
llbackBuffer(byte[]) | |
88 public static int frameSize(int width, int height, int imageFormat) { | |
89 if (imageFormat != ImageFormat.NV21) { | |
90 throw new UnsupportedOperationException("Don't know how to calculate " | |
91 + "the frame size of non-NV21 image formats."); | |
92 } | |
93 return (width * height * ImageFormat.getBitsPerPixel(imageFormat)) / 8; | |
94 } | |
95 | |
96 @Override | |
97 public String toString() { | |
98 return width + "x" + height + "@" + framerate; | |
99 } | |
100 | |
101 @Override | |
102 public boolean equals(Object other) { | |
103 if (!(other instanceof CaptureFormat)) { | |
104 return false; | |
105 } | |
106 final CaptureFormat otherFormat = (CaptureFormat) other; | |
107 return width == otherFormat.width && height == otherFormat.height | |
108 && framerate.equals(otherFormat.framerate); | |
109 } | |
110 | |
111 @Override | |
112 public int hashCode() { | |
113 return 1 + (width * 65497 + height) * 251 + framerate.hashCode(); | |
114 } | |
115 } | |
116 | |
117 /** | |
118 * @deprecated | |
119 * Please use Camera1Enumerator.getDeviceNames() instead. | |
120 */ | |
121 @Deprecated | |
122 public static String[] getDeviceNames() { | |
123 return new Camera1Enumerator().getDeviceNames(); | |
124 } | |
125 | |
126 | |
127 /** | |
128 * @deprecated | |
129 * Please use Camera1Enumerator.getDeviceNames().length instead. | |
130 */ | |
131 @Deprecated | |
132 public static int getDeviceCount() { | |
133 return new Camera1Enumerator().getDeviceNames().length; | |
134 } | |
135 | |
136 /** | |
137 * @deprecated | |
138 * Please use Camera1Enumerator.getDeviceNames().get(index) instead. | |
139 */ | |
140 @Deprecated | |
141 public static String getDeviceName(int index) { | |
142 return new Camera1Enumerator().getDeviceName(index); | |
143 } | |
144 | |
145 /** | |
146 * @deprecated | |
147 * Please use Camera1Enumerator.isFrontFacing(String deviceName) instead. | |
148 */ | |
149 @Deprecated | |
150 public static String getNameOfFrontFacingDevice() { | |
151 return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_FRON
T); | |
152 } | |
153 | |
154 /** | |
155 * @deprecated | |
156 * Please use Camera1Enumerator.isBackFacing(String deviceName) instead. | |
157 */ | |
158 @Deprecated | |
159 public static String getNameOfBackFacingDevice() { | |
160 return getNameOfDevice(android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK
); | |
161 } | |
162 | |
163 // Helper class for finding the closest supported format for the two functions
below. It creates a | |
164 // comparator based on the difference to some requested parameters, where the
element with the | |
165 // minimum difference is the element that is closest to the requested paramete
rs. | |
166 private static abstract class ClosestComparator<T> implements Comparator<T> { | |
167 // Difference between supported and requested parameter. | |
168 abstract int diff(T supportedParameter); | |
169 | |
170 @Override | |
171 public int compare(T t1, T t2) { | |
172 return diff(t1) - diff(t2); | |
173 } | |
174 } | |
175 | |
176 // Prefer a fps range with an upper bound close to |framerate|. Also prefer a
fps range with a low | |
177 // lower bound, to allow the framerate to fluctuate based on lightning conditi
ons. | |
178 public static CaptureFormat.FramerateRange getClosestSupportedFramerateRange( | |
179 List<CaptureFormat.FramerateRange> supportedFramerates, final int requeste
dFps) { | |
180 return Collections.min(supportedFramerates, | |
181 new ClosestComparator<CaptureFormat.FramerateRange>() { | |
182 // Progressive penalty if the upper bound is further away than |MAX_FP
S_DIFF_THRESHOLD| | |
183 // from requested. | |
184 private static final int MAX_FPS_DIFF_THRESHOLD = 5000; | |
185 private static final int MAX_FPS_LOW_DIFF_WEIGHT = 1; | |
186 private static final int MAX_FPS_HIGH_DIFF_WEIGHT = 3; | |
187 | |
188 // Progressive penalty if the lower bound is bigger than |MIN_FPS_THRE
SHOLD|. | |
189 private static final int MIN_FPS_THRESHOLD = 8000; | |
190 private static final int MIN_FPS_LOW_VALUE_WEIGHT = 1; | |
191 private static final int MIN_FPS_HIGH_VALUE_WEIGHT = 4; | |
192 | |
193 // Use one weight for small |value| less than |threshold|, and another
weight above. | |
194 private int progressivePenalty(int value, int threshold, int lowWeight
, int highWeight) { | |
195 return (value < threshold) | |
196 ? value * lowWeight | |
197 : threshold * lowWeight + (value - threshold) * highWeight; | |
198 } | |
199 | |
200 @Override | |
201 int diff(CaptureFormat.FramerateRange range) { | |
202 final int minFpsError = progressivePenalty(range.min, | |
203 MIN_FPS_THRESHOLD, MIN_FPS_LOW_VALUE_WEIGHT, MIN_FPS_HIGH_VALUE_
WEIGHT); | |
204 final int maxFpsError = progressivePenalty(Math.abs(requestedFps * 1
000 - range.max), | |
205 MAX_FPS_DIFF_THRESHOLD, MAX_FPS_LOW_DIFF_WEIGHT, MAX_FPS_HIGH_DI
FF_WEIGHT); | |
206 return minFpsError + maxFpsError; | |
207 } | |
208 }); | |
209 } | |
210 | |
211 public static Size getClosestSupportedSize( | |
212 List<Size> supportedSizes, final int requestedWidth, | |
213 final int requestedHeight) { | |
214 return Collections.min(supportedSizes, | |
215 new ClosestComparator<Size>() { | |
216 @Override | |
217 int diff(Size size) { | |
218 return abs(requestedWidth - size.width) + abs(requestedHeight - size
.height); | |
219 } | |
220 }); | |
221 } | |
222 | |
223 private static String getNameOfDevice(int facing) { | |
224 final android.hardware.Camera.CameraInfo info = new android.hardware.Camera.
CameraInfo(); | |
225 for (int i = 0; i < android.hardware.Camera.getNumberOfCameras(); ++i) { | |
226 try { | |
227 android.hardware.Camera.getCameraInfo(i, info); | |
228 if (info.facing == facing) { | |
229 return getDeviceName(i); | |
230 } | |
231 } catch (Exception e) { | |
232 Logging.e(TAG, "getCameraInfo() failed on index " + i, e); | |
233 } | |
234 } | |
235 return null; | |
236 } | |
237 } | |
OLD | NEW |