|
29 | 29 | import android.os.HandlerThread;
|
30 | 30 | import android.os.Looper;
|
31 | 31 | import android.util.Log;
|
| 32 | +import android.util.Range; |
32 | 33 | import android.util.Size;
|
33 | 34 | import android.view.Display;
|
34 | 35 | import android.view.Surface;
|
35 | 36 | import androidx.annotation.NonNull;
|
36 | 37 | import androidx.annotation.Nullable;
|
37 | 38 | import androidx.annotation.VisibleForTesting;
|
| 39 | +import io.flutter.BuildConfig; |
38 | 40 | import io.flutter.embedding.engine.systemchannels.PlatformChannel;
|
39 | 41 | import io.flutter.plugin.common.EventChannel;
|
40 | 42 | import io.flutter.plugin.common.MethodChannel;
|
|
52 | 54 | import io.flutter.plugins.camera.features.flash.FlashFeature;
|
53 | 55 | import io.flutter.plugins.camera.features.flash.FlashMode;
|
54 | 56 | import io.flutter.plugins.camera.features.focuspoint.FocusPointFeature;
|
| 57 | +import io.flutter.plugins.camera.features.fpsrange.FpsRangeFeature; |
55 | 58 | import io.flutter.plugins.camera.features.resolution.ResolutionFeature;
|
56 | 59 | import io.flutter.plugins.camera.features.resolution.ResolutionPreset;
|
57 | 60 | import io.flutter.plugins.camera.features.sensororientation.DeviceOrientationManager;
|
@@ -111,8 +114,7 @@ class Camera
|
111 | 114 | private int initialCameraFacing;
|
112 | 115 |
|
113 | 116 | private final SurfaceTextureEntry flutterTexture;
|
114 |
| - private final ResolutionPreset resolutionPreset; |
115 |
| - private final boolean enableAudio; |
| 117 | + private final VideoCaptureSettings videoCaptureSettings; |
116 | 118 | private final Context applicationContext;
|
117 | 119 | final DartMessenger dartMessenger;
|
118 | 120 | private CameraProperties cameraProperties;
|
@@ -185,29 +187,80 @@ public void close() {
|
185 | 187 | }
|
186 | 188 | }
|
187 | 189 |
|
| 190 | + public static class VideoCaptureSettings { |
| 191 | + @NonNull public final ResolutionPreset resolutionPreset; |
| 192 | + public final boolean enableAudio; |
| 193 | + @Nullable public final Integer fps; |
| 194 | + @Nullable public final Integer videoBitrate; |
| 195 | + @Nullable public final Integer audioBitrate; |
| 196 | + |
| 197 | + public VideoCaptureSettings( |
| 198 | + @NonNull ResolutionPreset resolutionPreset, |
| 199 | + boolean enableAudio, |
| 200 | + @Nullable Integer fps, |
| 201 | + @Nullable Integer videoBitrate, |
| 202 | + @Nullable Integer audioBitrate) { |
| 203 | + this.resolutionPreset = resolutionPreset; |
| 204 | + this.enableAudio = enableAudio; |
| 205 | + this.fps = fps; |
| 206 | + this.videoBitrate = videoBitrate; |
| 207 | + this.audioBitrate = audioBitrate; |
| 208 | + } |
| 209 | + |
| 210 | + public VideoCaptureSettings(@NonNull ResolutionPreset resolutionPreset, boolean enableAudio) { |
| 211 | + this(resolutionPreset, enableAudio, null, null, null); |
| 212 | + } |
| 213 | + } |
| 214 | + |
188 | 215 | public Camera(
|
189 | 216 | final Activity activity,
|
190 | 217 | final SurfaceTextureEntry flutterTexture,
|
191 | 218 | final CameraFeatureFactory cameraFeatureFactory,
|
192 | 219 | final DartMessenger dartMessenger,
|
193 | 220 | final CameraProperties cameraProperties,
|
194 |
| - final ResolutionPreset resolutionPreset, |
195 |
| - final boolean enableAudio) { |
| 221 | + final VideoCaptureSettings videoCaptureSettings) { |
196 | 222 |
|
197 | 223 | if (activity == null) {
|
198 | 224 | throw new IllegalStateException("No activity available!");
|
199 | 225 | }
|
200 | 226 | this.activity = activity;
|
201 |
| - this.enableAudio = enableAudio; |
202 | 227 | this.flutterTexture = flutterTexture;
|
203 | 228 | this.dartMessenger = dartMessenger;
|
204 | 229 | this.applicationContext = activity.getApplicationContext();
|
205 | 230 | this.cameraProperties = cameraProperties;
|
206 | 231 | this.cameraFeatureFactory = cameraFeatureFactory;
|
207 |
| - this.resolutionPreset = resolutionPreset; |
| 232 | + this.videoCaptureSettings = videoCaptureSettings; |
208 | 233 | this.cameraFeatures =
|
209 | 234 | CameraFeatures.init(
|
210 |
| - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); |
| 235 | + cameraFeatureFactory, |
| 236 | + cameraProperties, |
| 237 | + activity, |
| 238 | + dartMessenger, |
| 239 | + videoCaptureSettings.resolutionPreset); |
| 240 | + |
| 241 | + Integer recordingFps = null; |
| 242 | + |
| 243 | + if (videoCaptureSettings.fps != null && videoCaptureSettings.fps.intValue() > 0) { |
| 244 | + recordingFps = videoCaptureSettings.fps; |
| 245 | + } else { |
| 246 | + |
| 247 | + if (SdkCapabilityChecker.supportsEncoderProfiles()) { |
| 248 | + EncoderProfiles encoderProfiles = getRecordingProfile(); |
| 249 | + if (encoderProfiles != null && encoderProfiles.getVideoProfiles().size() > 0) { |
| 250 | + recordingFps = encoderProfiles.getVideoProfiles().get(0).getFrameRate(); |
| 251 | + } |
| 252 | + } else { |
| 253 | + CamcorderProfile camcorderProfile = getRecordingProfileLegacy(); |
| 254 | + recordingFps = null != camcorderProfile ? camcorderProfile.videoFrameRate : null; |
| 255 | + } |
| 256 | + } |
| 257 | + |
| 258 | + if (recordingFps != null && recordingFps.intValue() > 0) { |
| 259 | + |
| 260 | + final FpsRangeFeature fpsRange = new FpsRangeFeature(cameraProperties); |
| 261 | + fpsRange.setValue(new Range<Integer>(recordingFps, recordingFps)); |
| 262 | + this.cameraFeatures.setFpsRange(fpsRange); |
| 263 | + } |
211 | 264 |
|
212 | 265 | // Create capture callback.
|
213 | 266 | captureTimeouts = new CaptureTimeoutsWrapper(3000, 3000);
|
@@ -257,14 +310,28 @@ private void prepareMediaRecorder(String outputFilePath) throws IOException {
|
257 | 310 | // TODO(camsim99): Revert changes that allow legacy code to be used when recordingProfile is null
|
258 | 311 | // once this has largely been fixed on the Android side. https://github.com/flutter/flutter/issues/119668
|
259 | 312 | if (SdkCapabilityChecker.supportsEncoderProfiles() && getRecordingProfile() != null) {
|
260 |
| - mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfile(), outputFilePath); |
| 313 | + mediaRecorderBuilder = |
| 314 | + new MediaRecorderBuilder( |
| 315 | + getRecordingProfile(), |
| 316 | + new MediaRecorderBuilder.RecordingParameters( |
| 317 | + outputFilePath, |
| 318 | + videoCaptureSettings.fps, |
| 319 | + videoCaptureSettings.videoBitrate, |
| 320 | + videoCaptureSettings.audioBitrate)); |
261 | 321 | } else {
|
262 |
| - mediaRecorderBuilder = new MediaRecorderBuilder(getRecordingProfileLegacy(), outputFilePath); |
| 322 | + mediaRecorderBuilder = |
| 323 | + new MediaRecorderBuilder( |
| 324 | + getRecordingProfileLegacy(), |
| 325 | + new MediaRecorderBuilder.RecordingParameters( |
| 326 | + outputFilePath, |
| 327 | + videoCaptureSettings.fps, |
| 328 | + videoCaptureSettings.videoBitrate, |
| 329 | + videoCaptureSettings.audioBitrate)); |
263 | 330 | }
|
264 | 331 |
|
265 | 332 | mediaRecorder =
|
266 | 333 | mediaRecorderBuilder
|
267 |
| - .setEnableAudio(enableAudio) |
| 334 | + .setEnableAudio(videoCaptureSettings.enableAudio) |
268 | 335 | .setMediaOrientation(
|
269 | 336 | lockedOrientation == null
|
270 | 337 | ? getDeviceOrientationManager().getVideoOrientation()
|
@@ -1314,7 +1381,11 @@ public void setDescriptionWhileRecording(
|
1314 | 1381 | cameraProperties = properties;
|
1315 | 1382 | cameraFeatures =
|
1316 | 1383 | CameraFeatures.init(
|
1317 |
| - cameraFeatureFactory, cameraProperties, activity, dartMessenger, resolutionPreset); |
| 1384 | + cameraFeatureFactory, |
| 1385 | + cameraProperties, |
| 1386 | + activity, |
| 1387 | + dartMessenger, |
| 1388 | + videoCaptureSettings.resolutionPreset); |
1318 | 1389 | cameraFeatures.setAutoFocus(
|
1319 | 1390 | cameraFeatureFactory.createAutoFocusFeature(cameraProperties, true));
|
1320 | 1391 | try {
|
|
0 commit comments