-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[camerax] Make fixes required to swap camera_android_camerax for camera_android #6697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
71d569d
4b8a940
40b26b9
f4db415
e382581
53fb74c
87c64f5
c753225
cc3f010
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,6 +13,7 @@ import 'package:camera_platform_interface/camera_platform_interface.dart'; | |||||||||||||||||||||||||||||||||
| import 'package:flutter/painting.dart'; | ||||||||||||||||||||||||||||||||||
| import 'package:flutter_test/flutter_test.dart'; | ||||||||||||||||||||||||||||||||||
| import 'package:integration_test/integration_test.dart'; | ||||||||||||||||||||||||||||||||||
| import 'package:video_player/video_player.dart'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| void main() { | ||||||||||||||||||||||||||||||||||
| IntegrationTestWidgetsFlutterBinding.ensureInitialized(); | ||||||||||||||||||||||||||||||||||
|
|
@@ -178,4 +179,86 @@ void main() { | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| testWidgets('Video capture records valid video', (WidgetTester tester) async { | ||||||||||||||||||||||||||||||||||
| final List<CameraDescription> cameras = await availableCameras(); | ||||||||||||||||||||||||||||||||||
| if (cameras.isEmpty) { | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| final CameraController controller = CameraController(cameras[0], | ||||||||||||||||||||||||||||||||||
| mediaSettings: | ||||||||||||||||||||||||||||||||||
| const MediaSettings(resolutionPreset: ResolutionPreset.low)); | ||||||||||||||||||||||||||||||||||
| await controller.initialize(); | ||||||||||||||||||||||||||||||||||
| await controller.prepareForVideoRecording(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| await controller.startVideoRecording(); | ||||||||||||||||||||||||||||||||||
| final int recordingStart = DateTime.now().millisecondsSinceEpoch; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| sleep(const Duration(seconds: 2)); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| final XFile file = await controller.stopVideoRecording(); | ||||||||||||||||||||||||||||||||||
| final int recordingTime = | ||||||||||||||||||||||||||||||||||
| DateTime.now().millisecondsSinceEpoch - recordingStart; | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can make this test a bit better by comparing on both sides of the stopRecording call.
Suggested change
Note:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The preStopTime check didn't quite work I assume because stopping video recording takes so long but I kept the naming of postStopTime for clarity
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It shouldn't matter how long stopping recording takes. The timeline: The comparison for prestop does not depend on the length of time it takes to stop recording.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or maybe the start recording is slow I mean? The duration was 1448 ms (which is also below the 2000 ms of recording), prestop was 2002 ms.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose we could also fix this by listening for the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @reidbaker Ok so I added the fix but it didn't quite work still :/ the duration increased but guessing there still some lag time in starting the video. Still left it in though as it is a better approximation.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going to land this as-is since I've done all I can to remedy this issue. If we feel like we need this fixed, I can file a bug and escalate to CameraX.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed flutter/flutter#148138 for this. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| final File videoFile = File(file.path); | ||||||||||||||||||||||||||||||||||
| final VideoPlayerController videoController = VideoPlayerController.file( | ||||||||||||||||||||||||||||||||||
| videoFile, | ||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||
| await videoController.initialize(); | ||||||||||||||||||||||||||||||||||
| final int duration = videoController.value.duration.inMilliseconds; | ||||||||||||||||||||||||||||||||||
| await videoController.dispose(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| expect(duration, lessThan(recordingTime)); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| testWidgets('Pause and resume video recording', (WidgetTester tester) async { | ||||||||||||||||||||||||||||||||||
| final List<CameraDescription> cameras = await availableCameras(); | ||||||||||||||||||||||||||||||||||
| if (cameras.isEmpty) { | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| final CameraController controller = CameraController(cameras[0], | ||||||||||||||||||||||||||||||||||
| mediaSettings: | ||||||||||||||||||||||||||||||||||
| const MediaSettings(resolutionPreset: ResolutionPreset.low)); | ||||||||||||||||||||||||||||||||||
| await controller.initialize(); | ||||||||||||||||||||||||||||||||||
| await controller.prepareForVideoRecording(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| int startPause; | ||||||||||||||||||||||||||||||||||
| int timePaused = 0; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| await controller.startVideoRecording(); | ||||||||||||||||||||||||||||||||||
| final int recordingStart = DateTime.now().millisecondsSinceEpoch; | ||||||||||||||||||||||||||||||||||
| sleep(const Duration(milliseconds: 500)); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| await controller.pauseVideoRecording(); | ||||||||||||||||||||||||||||||||||
camsim99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
| startPause = DateTime.now().millisecondsSinceEpoch; | ||||||||||||||||||||||||||||||||||
| sleep(const Duration(milliseconds: 500)); | ||||||||||||||||||||||||||||||||||
| await controller.resumeVideoRecording(); | ||||||||||||||||||||||||||||||||||
| timePaused += DateTime.now().millisecondsSinceEpoch - startPause; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| sleep(const Duration(milliseconds: 500)); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| await controller.pauseVideoRecording(); | ||||||||||||||||||||||||||||||||||
| startPause = DateTime.now().millisecondsSinceEpoch; | ||||||||||||||||||||||||||||||||||
| sleep(const Duration(milliseconds: 500)); | ||||||||||||||||||||||||||||||||||
| await controller.resumeVideoRecording(); | ||||||||||||||||||||||||||||||||||
| timePaused += DateTime.now().millisecondsSinceEpoch - startPause; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| sleep(const Duration(milliseconds: 500)); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| final XFile file = await controller.stopVideoRecording(); | ||||||||||||||||||||||||||||||||||
| final int recordingTime = | ||||||||||||||||||||||||||||||||||
| DateTime.now().millisecondsSinceEpoch - recordingStart; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| final File videoFile = File(file.path); | ||||||||||||||||||||||||||||||||||
| final VideoPlayerController videoController = VideoPlayerController.file( | ||||||||||||||||||||||||||||||||||
| videoFile, | ||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||
| await videoController.initialize(); | ||||||||||||||||||||||||||||||||||
| final int duration = videoController.value.duration.inMilliseconds; | ||||||||||||||||||||||||||||||||||
| await videoController.dispose(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| expect(duration, lessThan(recordingTime - timePaused)); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -113,6 +113,12 @@ class AndroidCameraCameraX extends CameraPlatform { | |
| @visibleForTesting | ||
| String? videoOutputPath; | ||
|
|
||
| /// Stream queue to pick up finalized viceo recording events in | ||
| /// [stopVideoRecording]. | ||
| final StreamQueue<void> videoRecordingFinalizedStreamQueue = | ||
| StreamQueue<void>( | ||
| PendingRecording.videoRecordingFinalizedStreamController.stream); | ||
|
|
||
| /// Whether or not [preview] has been bound to the lifecycle of the camera by | ||
| /// [createCamera]. | ||
| @visibleForTesting | ||
|
|
@@ -122,7 +128,7 @@ class AndroidCameraCameraX extends CameraPlatform { | |
|
|
||
| /// The prefix used to create the filename for video recording files. | ||
| @visibleForTesting | ||
| final String videoPrefix = 'MOV'; | ||
| final String videoPrefix = 'REC'; | ||
camsim99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// The [ImageCapture] instance that can be configured to capture a still image. | ||
| @visibleForTesting | ||
|
|
@@ -777,6 +783,15 @@ class AndroidCameraCameraX extends CameraPlatform { | |
| await _unbindUseCaseFromLifecycle(preview!); | ||
| } | ||
|
|
||
| /// Sets the active camera while recording. | ||
| /// | ||
| /// Currently unsupported, so is a no-op. | ||
| @override | ||
| Future<void> setDescriptionWhileRecording(CameraDescription description) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean we have a feature we know will silently stop working when we modify the default to camerax?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does mean that :/ but the fact that it will be a breaking change can mitigate that fact; it will be noted in the README here but I can also add it to the camera/camera CHANGELOG and camera/camera README. @stuartmorgan any thoughts on this? I realized recently that this was not implemented for camera_android_camerax.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would definitely note the loss of functionality in the CHANGELOG for the This is a pretty new feature IIRC, and I would expect relatively niche, so losing it in a breaking change doesn't seem like a major issue. People will always have the possibility of using |
||
| // TODO(camsim99): Implement this feature, see https://github.com/flutter/flutter/issues/148013. | ||
| return Future<void>.value(); | ||
| } | ||
|
|
||
| /// Resume the paused preview for the selected camera. | ||
| /// | ||
| /// [cameraId] not used. | ||
|
|
@@ -955,8 +970,7 @@ class AndroidCameraCameraX extends CameraPlatform { | |
| .setTargetRotation(await proxy.getDefaultDisplayRotation()); | ||
| } | ||
|
|
||
| videoOutputPath = | ||
| await SystemServices.getTempFilePath(videoPrefix, '.temp'); | ||
| videoOutputPath = await SystemServices.getTempFilePath(videoPrefix, '.mp4'); | ||
| pendingRecording = await recorder!.prepareRecording(videoOutputPath!); | ||
| recording = await pendingRecording!.start(); | ||
|
|
||
|
|
@@ -979,21 +993,22 @@ class AndroidCameraCameraX extends CameraPlatform { | |
| 'Attempting to stop a ' | ||
| 'video recording while no recording is in progress.'); | ||
| } | ||
|
|
||
| /// Stop the active recording and wiat for the video recording to be finalized. | ||
camsim99 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| await recording!.close(); | ||
| await videoRecordingFinalizedStreamQueue.next; | ||
| recording = null; | ||
| pendingRecording = null; | ||
|
|
||
| if (videoOutputPath == null) { | ||
| // Stop the current active recording as we will be unable to complete it | ||
| // in this error case. | ||
| await recording!.close(); | ||
| recording = null; | ||
| pendingRecording = null; | ||
| // Handle any errors with finalizing video recording. | ||
| throw CameraException( | ||
| 'INVALID_PATH', | ||
| 'The platform did not return a path ' | ||
| 'while reporting success. The platform should always ' | ||
| 'return a valid path or report an error.'); | ||
| } | ||
| await recording!.close(); | ||
| recording = null; | ||
| pendingRecording = null; | ||
|
|
||
| await _unbindUseCaseFromLifecycle(videoCapture!); | ||
| return XFile(videoOutputPath!); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.