Skip to content

[camerax] Implement startVideoCapturing and onVideoRecordedEvent #4815

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

Merged
merged 40 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
dec3d69
Merge remote-tracking branch 'upstream/main' into camx_occ
camsim99 May 1, 2023
0e0333b
Merge remote-tracking branch 'upstream/main'
camsim99 May 2, 2023
bd7ac99
Merge remote-tracking branch 'upstream/main'
camsim99 May 3, 2023
5c3363b
Merge remote-tracking branch 'upstream/main'
camsim99 May 10, 2023
fed9621
Undo changes
camsim99 May 10, 2023
5aabe34
Merge remote-tracking branch 'upstream/main'
camsim99 May 12, 2023
2b9a352
Merge remote-tracking branch 'upstream/main'
camsim99 May 25, 2023
a1173da
Merge remote-tracking branch 'upstream/main'
camsim99 May 30, 2023
cbc3d6b
Merge remote-tracking branch 'upstream/main'
camsim99 May 30, 2023
cae5a4c
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 1, 2023
72283db
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 5, 2023
166a77c
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 5, 2023
399780e
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 14, 2023
8d5d0e7
Merge remote-tracking branch 'upstream/main'
camsim99 Jun 26, 2023
084d960
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 12, 2023
d2a59ac
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 17, 2023
a1422bf
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 17, 2023
bdd87a6
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 18, 2023
137a28b
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 19, 2023
bc0db5a
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 20, 2023
d04b466
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 21, 2023
a9cfe87
Merge remote-tracking branch 'upstream/main'
camsim99 Jul 24, 2023
a32def1
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 2, 2023
4785148
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 14, 2023
7a8fc69
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 15, 2023
b02e15f
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 15, 2023
c6e5868
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 17, 2023
0c0065a
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 28, 2023
9dfe259
Merge remote-tracking branch 'upstream/main'
camsim99 Aug 29, 2023
66117a3
Add impl
camsim99 Aug 29, 2023
d909e4b
Bump platform interfae requirement, versions, and update readme
camsim99 Aug 30, 2023
f99fc77
Add test
camsim99 Aug 30, 2023
20b7d33
Change video recording tests to video capture tests
camsim99 Aug 30, 2023
917d582
Merge branch 'main' into camx_vidcap
camsim99 Aug 30, 2023
f29b951
Fix test name
camsim99 Aug 30, 2023
a653810
Merge branch 'camx_vidcap' of github.com:camsim99/packages into camx_…
camsim99 Aug 30, 2023
1b6f439
Merge remote-tracking branch 'upstream/main' into camx_vidcap
camsim99 Sep 11, 2023
7a24ee7
Fix test
camsim99 Sep 11, 2023
5d9e038
format
camsim99 Sep 11, 2023
b7c7bbf
Clarify README
camsim99 Sep 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera_android_camerax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.0+18

* Implements `startVideoCapturing`.

## 0.5.0+17

* Implements resolution configuration for all camera use cases.
Expand Down
8 changes: 5 additions & 3 deletions packages/camera/camera_android_camerax/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ Calling `setFlashMode` with mode `FlashMode.torch` currently does nothing.

`setZoomLevel` is unimplemented.

### Some video capture functionality \[[Issue #127896][127896], [Issue #126477][126477]\]
### Setting maximum duration and stream options for video capture

`startVideoCapturing` is unimplemented; use `startVideoRecording` instead.
`onVideoRecordedEvent` is also unimplemented.
Calling `startVideoCapturing` with `VideoCaptureOptions` configured with
`maxVideoDuration` and `streamOptions` is currently unsupported do to the
limitations of the CameraX library and the platform interface, respectively,
and thus, those parameters will silently be ignored.

## Contributing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ class AndroidCameraCameraX extends CameraPlatform {
]);
}

/// The camera finished recording a video.
@override
Stream<VideoRecordedEvent> onVideoRecordedEvent(int cameraId) {
return _cameraEvents(cameraId).whereType<VideoRecordedEvent>();
}

/// Gets the minimum supported exposure offset for the selected camera in EV units.
///
/// [cameraId] not used.
Expand Down Expand Up @@ -507,12 +513,23 @@ class AndroidCameraCameraX extends CameraPlatform {
/// Note that the preset resolution is used to configure the recording, but
/// 240p ([ResolutionPreset.low]) is unsupported and will fallback to
/// configure the recording as the next highest available quality.
///
/// This method is deprecated in favour of [startVideoCapturing].
@override
Future<void> startVideoRecording(int cameraId,
{Duration? maxVideoDuration}) async {
assert(cameraSelector != null);
assert(processCameraProvider != null);
return startVideoCapturing(
VideoCaptureOptions(cameraId, maxDuration: maxVideoDuration));
}

/// Starts a video recording and/or streaming session.
///
/// Please see [VideoCaptureOptions] for documentation on the
/// configuration options. Currently, maxVideoDuration and streamOptions
/// are unsupported due to the limitations of CameraX and the platform
/// interface, respectively.
@override
Future<void> startVideoCapturing(VideoCaptureOptions options) async {
if (recording != null) {
// There is currently an active recording, so do not start a new one.
return;
Expand All @@ -527,6 +544,10 @@ class AndroidCameraCameraX extends CameraPlatform {
await SystemServices.getTempFilePath(videoPrefix, '.temp');
pendingRecording = await recorder!.prepareRecording(videoOutputPath!);
recording = await pendingRecording!.start();

if (options.streamCallback != null) {
onStreamedFrameAvailable(options.cameraId).listen(options.streamCallback);
}
}

/// Stops the video recording and returns the file where it was saved.
Expand Down
4 changes: 2 additions & 2 deletions packages/camera/camera_android_camerax/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_android_camerax
description: Android implementation of the camera plugin using the CameraX library.
repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.5.0+17
version: 0.5.0+18

environment:
sdk: ">=2.19.0 <4.0.0"
Expand All @@ -19,7 +19,7 @@ flutter:

dependencies:
async: ^2.5.0
camera_platform_interface: ^2.2.0
camera_platform_interface: ^2.3.2
flutter:
sdk: flutter
integration_test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,9 +785,9 @@ void main() {

group('video recording', () {
test(
'startVideoRecording binds video capture use case and starts the recording',
'startVideoCapturing binds video capture use case and starts the recording',
() async {
//Set up mocks and constants.
// Set up mocks and constants.
final FakeAndroidCameraCameraX camera = FakeAndroidCameraCameraX();
camera.processCameraProvider = MockProcessCameraProvider();
camera.cameraSelector = MockCameraSelector();
Expand Down Expand Up @@ -815,7 +815,7 @@ void main() {
camera.cameraSelector!, <UseCase>[camera.videoCapture!]))
.thenAnswer((_) async => camera.camera!);

await camera.startVideoRecording(cameraId);
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));

verify(camera.processCameraProvider!.bindToLifecycle(
camera.cameraSelector!, <UseCase>[camera.videoCapture!]));
Expand All @@ -824,9 +824,9 @@ void main() {
});

test(
'startVideoRecording binds video capture use case and starts the recording'
'startVideoCapturing binds video capture use case and starts the recording'
' on first call, and does nothing on second call', () async {
//Set up mocks and constants.
// Set up mocks and constants.
final FakeAndroidCameraCameraX camera = FakeAndroidCameraCameraX();
camera.processCameraProvider = MockProcessCameraProvider();
camera.cameraSelector = MockCameraSelector();
Expand Down Expand Up @@ -854,14 +854,14 @@ void main() {
camera.cameraSelector!, <UseCase>[camera.videoCapture!]))
.thenAnswer((_) async => camera.camera!);

await camera.startVideoRecording(cameraId);
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));

verify(camera.processCameraProvider!.bindToLifecycle(
camera.cameraSelector!, <UseCase>[camera.videoCapture!]));
expect(camera.pendingRecording, equals(mockPendingRecording));
expect(camera.recording, mockRecording);

await camera.startVideoRecording(cameraId);
await camera.startVideoCapturing(const VideoCaptureOptions(cameraId));
// Verify that each of these calls happened only once.
verify(mockSystemServicesApi.getTempFilePath(camera.videoPrefix, '.temp'))
.called(1);
Expand All @@ -872,6 +872,55 @@ void main() {
verifyNoMoreInteractions(mockPendingRecording);
});

test(
'startVideoCapturing called with stream options starts image streaming',
() async {
// Set up mocks and constants.
final FakeAndroidCameraCameraX camera =
FakeAndroidCameraCameraX(shouldCreateDetachedObjectForTesting: true);
final MockProcessCameraProvider mockProcessCameraProvider =
MockProcessCameraProvider();
camera.processCameraProvider = mockProcessCameraProvider;
camera.cameraSelector = MockCameraSelector();
camera.recorder = camera.testRecorder;
camera.videoCapture = camera.testVideoCapture;
camera.imageAnalysis = camera.testImageAnalysis;
camera.camera = MockCamera();
final MockPendingRecording mockPendingRecording = MockPendingRecording();
final TestSystemServicesHostApi mockSystemServicesApi =
MockTestSystemServicesHostApi();
TestSystemServicesHostApi.setup(mockSystemServicesApi);

const int cameraId = 17;
const String outputPath = '/temp/MOV123.temp';
final Completer<CameraImageData> imageDataCompleter =
Completer<CameraImageData>();
final VideoCaptureOptions videoCaptureOptions = VideoCaptureOptions(
cameraId,
streamCallback: (CameraImageData imageData) =>
imageDataCompleter.complete(imageData));

// Mock method calls.
when(camera.processCameraProvider!.isBound(camera.videoCapture!))
.thenAnswer((_) async => true);
when(mockSystemServicesApi.getTempFilePath(camera.videoPrefix, '.temp'))
.thenReturn(outputPath);
when(camera.testRecorder.prepareRecording(outputPath))
.thenAnswer((_) async => mockPendingRecording);
when(mockProcessCameraProvider.bindToLifecycle(any, any))
.thenAnswer((_) => Future<Camera>.value(camera.camera));
when(camera.camera!.getCameraInfo())
.thenAnswer((_) => Future<CameraInfo>.value(MockCameraInfo()));

await camera.startVideoCapturing(videoCaptureOptions);

final CameraImageData mockCameraImageData = MockCameraImageData();
camera.cameraImageDataStreamController!.add(mockCameraImageData);

expect(imageDataCompleter.future, isNotNull);
await camera.cameraImageDataStreamController!.close();
});

test('pauseVideoRecording pauses the recording', () async {
final AndroidCameraCameraX camera = AndroidCameraCameraX();
final MockRecording recording = MockRecording();
Expand Down