diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index 41077b35b449..2fa09eaf79c2 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 0.13.0
+* Adds `videoOutputPath` support to `startVideoRecording`.
* Updates minimum supported SDK version to Flutter 3.38/Dart 3.10.
## 0.12.0+1
diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md
index 0a39b9a7478f..ae191e5c734b 100644
--- a/packages/camera/camera/README.md
+++ b/packages/camera/camera/README.md
@@ -6,9 +6,9 @@
A Flutter plugin for iOS, Android and Web allowing access to the device cameras.
-| | Android | iOS | Web |
-|----------------|---------|-----------|------------------------|
-| **Support** | SDK 24+ | iOS 13.0+ | [See `camera_web `][1] |
+| | Android | iOS | Web |
+| ----------- | ------- | --------- | ---------------------- |
+| **Support** | SDK 24+ | iOS 13.0+ | [See `camera_web `][1] |
## Features
@@ -92,6 +92,24 @@ Here is a list of all permission error codes that can be thrown:
- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control).
+### Custom Video Recording Path
+
+You can optionally specify a `videoOutputPath` when calling `startVideoRecording()` to save the recorded video directly to a custom absolute file path on the device.
+
+```dart
+// Always ensure the path ends with the .mp4 extension
+await controller.startVideoRecording(
+ videoOutputPath: '/path/to/your/custom_video.mp4',
+);
+```
+
+#### Platform-Specific Considerations
+
+For platform-specific considerations when saving videos, please see the READMEs on pub.dev for their respective platforms:
+- [Android](https://pub.dev/packages/camera_android)
+- [iOS](https://pub.dev/packages/camera_avfoundation)
+- [Windows](https://pub.dev/packages/camera_windows)
+
### Example
Here is a small example flutter app displaying a full screen camera preview.
diff --git a/packages/camera/camera/example/ios/Runner/Info.plist b/packages/camera/camera/example/ios/Runner/Info.plist
index 745494da50a6..42c3f394b0e0 100644
--- a/packages/camera/camera/example/ios/Runner/Info.plist
+++ b/packages/camera/camera/example/ios/Runner/Info.plist
@@ -2,6 +2,12 @@
+ LSSupportsOpeningDocumentsInPlace
+
+ UISupportsDocumentBrowser
+
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -28,6 +34,8 @@
Can I use the camera please? Only for demo purpose of the app
NSMicrophoneUsageDescription
Only for demo purpose of the app
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -46,9 +54,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml
index d94d8bef8cfa..ee8615db2247 100644
--- a/packages/camera/camera/example/pubspec.yaml
+++ b/packages/camera/camera/example/pubspec.yaml
@@ -31,3 +31,10 @@ dev_dependencies:
flutter:
uses-material-design: true
+# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
+# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
+dependency_overrides:
+ camera_android_camerax: {path: ../../../../packages/camera/camera_android_camerax}
+ camera_avfoundation: {path: ../../../../packages/camera/camera_avfoundation}
+ camera_platform_interface: {path: ../../../../packages/camera/camera_platform_interface}
+ camera_web: {path: ../../../../packages/camera/camera_web}
diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart
index 05d52152b430..4a7fc7990cc6 100644
--- a/packages/camera/camera/lib/src/camera_controller.dart
+++ b/packages/camera/camera/lib/src/camera_controller.dart
@@ -569,6 +569,7 @@ class CameraController extends ValueNotifier {
Future startVideoRecording({
onLatestImageAvailable? onAvailable,
bool enablePersistentRecording = true,
+ String? videoOutputPath,
}) async {
_throwIfNotInitialized('startVideoRecording');
if (value.isRecordingVideo) {
@@ -578,6 +579,13 @@ class CameraController extends ValueNotifier {
);
}
+ if (videoOutputPath != null) {
+ final String lowerPath = videoOutputPath.toLowerCase();
+ if (!lowerPath.endsWith('.mp4')) {
+ throw CameraException('InvalidFilePath', 'Invalid video extension. Supported: .mp4');
+ }
+ }
+
void Function(CameraImageData image)? streamCallback;
if (onAvailable != null) {
streamCallback = (CameraImageData imageData) {
@@ -591,6 +599,7 @@ class CameraController extends ValueNotifier {
_cameraId,
streamCallback: streamCallback,
enablePersistentRecording: enablePersistentRecording,
+ videoOutputPath: videoOutputPath,
),
);
value = value.copyWith(
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index f2cecaf6a7bc..eae0a04481aa 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
Dart.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.12.0+1
+version: 0.13.0
environment:
sdk: ^3.10.0
@@ -23,7 +23,7 @@ flutter:
dependencies:
camera_android_camerax: ^0.7.0
camera_avfoundation: ^0.10.0
- camera_platform_interface: ^2.12.0
+ camera_platform_interface: ^2.14.0
camera_web: ^0.3.3
flutter:
sdk: flutter
@@ -38,3 +38,10 @@ dev_dependencies:
topics:
- camera
+# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
+# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
+dependency_overrides:
+ camera_android_camerax: {path: ../../../packages/camera/camera_android_camerax}
+ camera_avfoundation: {path: ../../../packages/camera/camera_avfoundation}
+ camera_platform_interface: {path: ../../../packages/camera/camera_platform_interface}
+ camera_web: {path: ../../../packages/camera/camera_web}
diff --git a/packages/camera/camera/test/camera_image_stream_test.dart b/packages/camera/camera/test/camera_image_stream_test.dart
index e9c12afc7831..341d81a45385 100644
--- a/packages/camera/camera/test/camera_image_stream_test.dart
+++ b/packages/camera/camera/test/camera_image_stream_test.dart
@@ -235,10 +235,14 @@ class MockStreamingCameraPlatform extends MockCameraPlatform {
}
@override
- Future startVideoRecording(int cameraId, {Duration? maxVideoDuration}) {
+ Future startVideoRecording(
+ int cameraId, {
+ Duration? maxVideoDuration,
+ String? videoOutputPath,
+ }) {
streamCallLog.add('startVideoRecording');
// Ignore maxVideoDuration, as it is unimplemented and deprecated.
- return super.startVideoRecording(cameraId);
+ return super.startVideoRecording(cameraId, videoOutputPath: videoOutputPath);
}
@override
diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart
index c5dfde4f7383..405a48a3fb97 100644
--- a/packages/camera/camera/test/camera_preview_test.dart
+++ b/packages/camera/camera/test/camera_preview_test.dart
@@ -112,6 +112,7 @@ class FakeController extends ValueNotifier implements CameraControl
Future startVideoRecording({
onLatestImageAvailable? onAvailable,
bool enablePersistentRecording = true,
+ String? videoOutputPath,
}) async {}
@override
diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart
index 8366e41f6c2b..3218e4008149 100644
--- a/packages/camera/camera/test/camera_test.dart
+++ b/packages/camera/camera/test/camera_test.dart
@@ -3493,9 +3493,13 @@ class MockCameraPlatform extends Mock with MockPlatformInterfaceMixin implements
super.noSuchMethod(Invocation.method(#prepareForVideoRecording, null));
@override
- Future startVideoRecording(int cameraId, {Duration? maxVideoDuration}) {
+ Future startVideoRecording(
+ int cameraId, {
+ Duration? maxVideoDuration,
+ String? videoOutputPath,
+ }) {
// Ignore maxVideoDuration, as it is unimplemented and deprecated.
- return startVideoCapturing(VideoCaptureOptions(cameraId));
+ return startVideoCapturing(VideoCaptureOptions(cameraId, videoOutputPath: videoOutputPath));
}
@override
diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md
index 8790bfbca4e3..ba99fc82a5e8 100644
--- a/packages/camera/camera_android/CHANGELOG.md
+++ b/packages/camera/camera_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.10.11
+
+* Adds support for custom video output path in video recording.
+
## 0.10.10+18
* Bumps the androidx group across 10 directories with 1 update.
diff --git a/packages/camera/camera_android/README.md b/packages/camera/camera_android/README.md
index 36f1dd5c3c04..4a43263c5f5a 100644
--- a/packages/camera/camera_android/README.md
+++ b/packages/camera/camera_android/README.md
@@ -16,6 +16,23 @@ $ flutter pub add camera_android
when recording a video with sound enabled and trying to play it back, the duration won't be correct and
you will only see the first frame.
+## Custom Video Recording Path
+
+Although it is possible to use an absolute path like `/storage/emulated/0/Download/video.mp4`, this is a fragile practice and may fail on many devices or Android versions due to **Scoped Storage** restrictions.
+
+- **Best Practice:** Always use the [path_provider](https://pub.dev/packages/path_provider) package to fetch a safe, writable directory.
+- **Recommended Directory:** Use `getTemporaryDirectory()` or `getApplicationDocumentsDirectory()`.
+
+```dart
+import 'package:path/path.dart' as p;
+import 'package:path_provider/path_provider.dart';
+
+final directory = await getTemporaryDirectory();
+final videoPath = p.join(directory.path, 'my_video.mp4');
+
+await controller.startVideoRecording(videoOutputPath: videoPath);
+```
+
[1]: https://pub.dev/packages/camera
[2]: https://flutter.dev/to/endorsed-federated-plugin
[3]: https://pub.dev/packages/camera_android_camerax
diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java
index 1d543bd86559..39f85ad08625 100644
--- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java
+++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java
@@ -869,8 +869,9 @@ void unlockAutoFocus() {
dartMessenger.error(flutterResult, errorCode, errorMessage, null));
}
- public void startVideoRecording(@Nullable EventChannel imageStreamChannel) {
- prepareRecording();
+ public void startVideoRecording(
+ @Nullable EventChannel imageStreamChannel, @Nullable String videoOutputPath) {
+ prepareRecording(videoOutputPath);
if (imageStreamChannel != null) {
setStreamHandler(imageStreamChannel);
@@ -1296,13 +1297,19 @@ public void onError(@NonNull String errorCode, @NonNull String errorMessage) {
}
@VisibleForTesting
- void prepareRecording() {
- final File outputDir = applicationContext.getCacheDir();
- try {
- captureFile = File.createTempFile("REC", ".mp4", outputDir);
- } catch (IOException | SecurityException e) {
- throw new Messages.FlutterError("cannotCreateFile", e.getMessage(), null);
+ void prepareRecording(@Nullable String videoOutputPath) {
+ if (videoOutputPath != null) {
+ validateOutputPath(videoOutputPath);
+ captureFile = new File(videoOutputPath);
+ } else {
+ final File outputDir = applicationContext.getCacheDir();
+ try {
+ captureFile = File.createTempFile("REC", ".mp4", outputDir);
+ } catch (IOException | SecurityException e) {
+ throw new Messages.FlutterError("cannotCreateFile", e.getMessage(), null);
+ }
}
+
try {
prepareMediaRecorder(captureFile.getAbsolutePath());
} catch (IOException e) {
@@ -1317,6 +1324,23 @@ void prepareRecording() {
setFpsCameraFeatureForRecording(cameraProperties);
}
+ private void validateOutputPath(String path) {
+ File file = new File(path);
+ if (file.isDirectory()) {
+ throw new Messages.FlutterError("IOError", "The output path is a directory: " + path, null);
+ }
+ File parent = file.getParentFile();
+ if (parent != null && !parent.exists()) {
+ throw new Messages.FlutterError(
+ "IOError", "The parent directory does not exist: " + parent.getAbsolutePath(), null);
+ }
+
+ String lowerPath = path.toLowerCase(Locale.ROOT);
+ if (!lowerPath.endsWith(".mp4")) {
+ throw new Messages.FlutterError("IOError", "Invalid video extension. Supported: .mp4", null);
+ }
+ }
+
private void setStreamHandler(EventChannel imageStreamChannel) {
imageStreamChannel.setStreamHandler(
new EventChannel.StreamHandler() {
diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java
index b3b04d5309e5..95a8d210b0af 100644
--- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java
+++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraApiImpl.java
@@ -175,8 +175,8 @@ public void takePicture(@NonNull Messages.Result result) {
}
@Override
- public void startVideoRecording(@NonNull Boolean enableStream) {
- camera.startVideoRecording(enableStream ? imageStreamChannel : null);
+ public void startVideoRecording(@NonNull Boolean enableStream, @Nullable String videoOutputPath) {
+ camera.startVideoRecording(enableStream ? imageStreamChannel : null, videoOutputPath);
}
@NonNull
diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java
index e61e0c48c199..ce916c0161b2 100644
--- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java
+++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Messages.java
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Autogenerated from Pigeon (v26.1.0), do not edit directly.
+// Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package io.flutter.plugins.camera;
@@ -21,13 +21,171 @@
import java.lang.annotation.Target;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
/** Generated class from Pigeon. */
@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
public class Messages {
+ static boolean pigeonDoubleEquals(double a, double b) {
+ // Normalize -0.0 to 0.0 and handle NaN equality.
+ return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b));
+ }
+
+ static boolean pigeonFloatEquals(float a, float b) {
+ // Normalize -0.0 to 0.0 and handle NaN equality.
+ return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b));
+ }
+
+ static int pigeonDoubleHashCode(double d) {
+ // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
+ if (d == 0.0) {
+ d = 0.0;
+ }
+ long bits = Double.doubleToLongBits(d);
+ return (int) (bits ^ (bits >>> 32));
+ }
+
+ static int pigeonFloatHashCode(float f) {
+ // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
+ if (f == 0.0f) {
+ f = 0.0f;
+ }
+ return Float.floatToIntBits(f);
+ }
+
+ static boolean pigeonDeepEquals(Object a, Object b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null || b == null) {
+ return false;
+ }
+ if (a instanceof byte[] && b instanceof byte[]) {
+ return Arrays.equals((byte[]) a, (byte[]) b);
+ }
+ if (a instanceof int[] && b instanceof int[]) {
+ return Arrays.equals((int[]) a, (int[]) b);
+ }
+ if (a instanceof long[] && b instanceof long[]) {
+ return Arrays.equals((long[]) a, (long[]) b);
+ }
+ if (a instanceof double[] && b instanceof double[]) {
+ double[] da = (double[]) a;
+ double[] db = (double[]) b;
+ if (da.length != db.length) {
+ return false;
+ }
+ for (int i = 0; i < da.length; i++) {
+ if (!pigeonDoubleEquals(da[i], db[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (a instanceof List && b instanceof List) {
+ List> listA = (List>) a;
+ List> listB = (List>) b;
+ if (listA.size() != listB.size()) {
+ return false;
+ }
+ for (int i = 0; i < listA.size(); i++) {
+ if (!pigeonDeepEquals(listA.get(i), listB.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (a instanceof Map && b instanceof Map) {
+ Map, ?> mapA = (Map, ?>) a;
+ Map, ?> mapB = (Map, ?>) b;
+ if (mapA.size() != mapB.size()) {
+ return false;
+ }
+ for (Map.Entry, ?> entryA : mapA.entrySet()) {
+ Object keyA = entryA.getKey();
+ Object valueA = entryA.getValue();
+ boolean found = false;
+ for (Map.Entry, ?> entryB : mapB.entrySet()) {
+ Object keyB = entryB.getKey();
+ if (pigeonDeepEquals(keyA, keyB)) {
+ Object valueB = entryB.getValue();
+ if (pigeonDeepEquals(valueA, valueB)) {
+ found = true;
+ break;
+ } else {
+ return false;
+ }
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (a instanceof Double && b instanceof Double) {
+ return pigeonDoubleEquals((double) a, (double) b);
+ }
+ if (a instanceof Float && b instanceof Float) {
+ return pigeonFloatEquals((float) a, (float) b);
+ }
+ return a.equals(b);
+ }
+
+ static int pigeonDeepHashCode(Object value) {
+ if (value == null) {
+ return 0;
+ }
+ if (value instanceof byte[]) {
+ return Arrays.hashCode((byte[]) value);
+ }
+ if (value instanceof int[]) {
+ return Arrays.hashCode((int[]) value);
+ }
+ if (value instanceof long[]) {
+ return Arrays.hashCode((long[]) value);
+ }
+ if (value instanceof double[]) {
+ double[] da = (double[]) value;
+ int result = 1;
+ for (double d : da) {
+ result = 31 * result + pigeonDoubleHashCode(d);
+ }
+ return result;
+ }
+ if (value instanceof List) {
+ int result = 1;
+ for (Object item : (List>) value) {
+ result = 31 * result + pigeonDeepHashCode(item);
+ }
+ return result;
+ }
+ if (value instanceof Map) {
+ int result = 0;
+ for (Map.Entry, ?> entry : ((Map, ?>) value).entrySet()) {
+ result +=
+ ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue()));
+ }
+ return result;
+ }
+ if (value instanceof Object[]) {
+ int result = 1;
+ for (Object item : (Object[]) value) {
+ result = 31 * result + pigeonDeepHashCode(item);
+ }
+ return result;
+ }
+ if (value instanceof Double) {
+ return pigeonDoubleHashCode((double) value);
+ }
+ if (value instanceof Float) {
+ return pigeonFloatHashCode((float) value);
+ }
+ return value.hashCode();
+ }
/** Error class for passing custom error details to Flutter via a thrown PlatformException. */
public static class FlutterError extends RuntimeException {
@@ -224,14 +382,15 @@ public boolean equals(Object o) {
return false;
}
PlatformCameraDescription that = (PlatformCameraDescription) o;
- return name.equals(that.name)
- && lensDirection.equals(that.lensDirection)
- && sensorOrientation.equals(that.sensorOrientation);
+ return pigeonDeepEquals(name, that.name)
+ && pigeonDeepEquals(lensDirection, that.lensDirection)
+ && pigeonDeepEquals(sensorOrientation, that.sensorOrientation);
}
@Override
public int hashCode() {
- return Objects.hash(name, lensDirection, sensorOrientation);
+ Object[] fields = new Object[] {getClass(), name, lensDirection, sensorOrientation};
+ return pigeonDeepHashCode(fields);
}
public static final class Builder {
@@ -373,17 +532,25 @@ public boolean equals(Object o) {
return false;
}
PlatformCameraState that = (PlatformCameraState) o;
- return previewSize.equals(that.previewSize)
- && exposureMode.equals(that.exposureMode)
- && focusMode.equals(that.focusMode)
- && exposurePointSupported.equals(that.exposurePointSupported)
- && focusPointSupported.equals(that.focusPointSupported);
+ return pigeonDeepEquals(previewSize, that.previewSize)
+ && pigeonDeepEquals(exposureMode, that.exposureMode)
+ && pigeonDeepEquals(focusMode, that.focusMode)
+ && pigeonDeepEquals(exposurePointSupported, that.exposurePointSupported)
+ && pigeonDeepEquals(focusPointSupported, that.focusPointSupported);
}
@Override
public int hashCode() {
- return Objects.hash(
- previewSize, exposureMode, focusMode, exposurePointSupported, focusPointSupported);
+ Object[] fields =
+ new Object[] {
+ getClass(),
+ previewSize,
+ exposureMode,
+ focusMode,
+ exposurePointSupported,
+ focusPointSupported
+ };
+ return pigeonDeepHashCode(fields);
}
public static final class Builder {
@@ -510,12 +677,13 @@ public boolean equals(Object o) {
return false;
}
PlatformSize that = (PlatformSize) o;
- return width.equals(that.width) && height.equals(that.height);
+ return pigeonDeepEquals(width, that.width) && pigeonDeepEquals(height, that.height);
}
@Override
public int hashCode() {
- return Objects.hash(width, height);
+ Object[] fields = new Object[] {getClass(), width, height};
+ return pigeonDeepHashCode(fields);
}
public static final class Builder {
@@ -606,12 +774,13 @@ public boolean equals(Object o) {
return false;
}
PlatformPoint that = (PlatformPoint) o;
- return x.equals(that.x) && y.equals(that.y);
+ return pigeonDeepEquals(x, that.x) && pigeonDeepEquals(y, that.y);
}
@Override
public int hashCode() {
- return Objects.hash(x, y);
+ Object[] fields = new Object[] {getClass(), x, y};
+ return pigeonDeepHashCode(fields);
}
public static final class Builder {
@@ -732,16 +901,18 @@ public boolean equals(Object o) {
return false;
}
PlatformMediaSettings that = (PlatformMediaSettings) o;
- return resolutionPreset.equals(that.resolutionPreset)
- && Objects.equals(fps, that.fps)
- && Objects.equals(videoBitrate, that.videoBitrate)
- && Objects.equals(audioBitrate, that.audioBitrate)
- && enableAudio.equals(that.enableAudio);
+ return pigeonDeepEquals(resolutionPreset, that.resolutionPreset)
+ && pigeonDeepEquals(fps, that.fps)
+ && pigeonDeepEquals(videoBitrate, that.videoBitrate)
+ && pigeonDeepEquals(audioBitrate, that.audioBitrate)
+ && pigeonDeepEquals(enableAudio, that.enableAudio);
}
@Override
public int hashCode() {
- return Objects.hash(resolutionPreset, fps, videoBitrate, audioBitrate, enableAudio);
+ Object[] fields =
+ new Object[] {getClass(), resolutionPreset, fps, videoBitrate, audioBitrate, enableAudio};
+ return pigeonDeepHashCode(fields);
}
public static final class Builder {
@@ -993,7 +1164,7 @@ void create(
void takePicture(@NonNull Result result);
/** Starts recording a video on the camera with the given ID. */
- void startVideoRecording(@NonNull Boolean enableStream);
+ void startVideoRecording(@NonNull Boolean enableStream, @Nullable String videoOutputPath);
/**
* Ends video recording on the camera with the given ID and returns the path to the resulting
@@ -1285,8 +1456,9 @@ public void error(Throwable error) {
ArrayList