-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[camera] Limit video length with maxVideoDuration on startVideoRecording #3403
Conversation
Co-authored-by: Maurits van Beusekom <[email protected]>
Co-authored-by: Maurits van Beusekom <[email protected]>
# Conflicts: # packages/camera/camera/pubspec.yaml # packages/camera/camera_platform_interface/CHANGELOG.md # packages/camera/camera_platform_interface/lib/src/events/camera_event.dart # packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart # packages/camera/camera_platform_interface/pubspec.yaml
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Show resolved
Hide resolved
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Outdated
Show resolved
Hide resolved
| camera_platform_interface: #^1.5.0 | ||
| path: ../camera_platform_interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove path
BeMacized
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this already looks pretty good! 😄
I've left a few comments below.
Additionally:
- Unit tests are still missing for your changes.
- iOS implementations are still missing.
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Outdated
Show resolved
Hide resolved
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Show resolved
Hide resolved
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Outdated
Show resolved
Hide resolved
| /// Throws a [CameraException] if the capture fails. | ||
| Future<void> startVideoRecording() async { | ||
| /// | ||
| /// TODO: Documentation: when maxVideoDuration listen to Stream with CameraTimeLimitReachedEvent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remaining TODO (PR is in draft state, but marking these just in case 🙂 )
| } | ||
| } | ||
|
|
||
| /// TODO: Documentation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remaining TODO
| void onCameraTimeLimitReachedEvent({onCameraTimeLimitReached}) { | ||
| if (!value.isInitialized || _isDisposed) { | ||
| throw CameraException( | ||
| 'Uninitialized CameraController', | ||
| 'cameraTimeLimitReachedEventStream was called on uninitialized CameraController', | ||
| ); | ||
| } | ||
| if (!value.isRecordingVideo) { | ||
| throw CameraException( | ||
| 'No video is recording', | ||
| 'cameraTimeLimitReachedEventStream was called when no video is recording.', | ||
| ); | ||
| } | ||
|
|
||
| CameraPlatform.instance | ||
| .onCameraTimeLimitReached(_cameraId) | ||
| .first | ||
| .then((event) { | ||
| value = value.copyWith(isRecordingVideo: false); | ||
| onCameraTimeLimitReached(event.path); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One issue I see with this, is that if the user sets a maxVideoDuration, but never calls onCameraTimeLimitReachedEvent to handle the callback, value will never be updated, causing isRecordingVideo to still report true, while it has in fact already stopped.
Ideally, the camera controller should always be listening for CameraPlatform.instance.onCameraTimeLimitReached to update the CameraValue, even when the user has not provided a callback, so that the state is always correct.
Additionally, I think I would personally prefer seeing a method that returns a stream of these events (Like how the platform interface exposes events) rather than a method that allows for setting a callback, as that would imo offer a bit more flexibility. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this would be easier for users to manage if we can use this as a Stream<XFile> and simply listen for a file to arrive.
We can even extend the stopVideoRecording method to add the final video file to the same stream. This way developers simply listen to the stream to get informed that a new video file is available (no matter if the end-user pressed the stop button or if the max time limit has been reached). Of course this would mean the name of the event and the stream should be updated a to reflect this change. Something like:
Stream<VideoRecordedEvent> onVideoRecorded(int cameraId); There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might also solve possible issues with recording video's and stopping them for unusual reasons, like full storage
packages/camera/camera_platform_interface/lib/src/events/camera_event.dart
Outdated
Show resolved
Hide resolved
| @override | ||
| Stream<CameraTimeLimitReachedEvent> onCameraTimeLimitReached(int cameraId) { | ||
| throw UnimplementedError('onCameraTimeLimitReached() is not implemented.'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change to the platform interface should probably be split off to a separate PR, so that it can be published to pub.dev before the implementations are merged to master.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to rename this to onVideoRecordingTimeLimitReached, so it is clear this is directly related to the video recording feature. Of course if you accept my earlier comment about also emitting the recorded video on this stream when the user calls stopVideoRecording we should rename it to something more generic like onVideoRecorded.
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Show resolved
Hide resolved
| void onCameraTimeLimitReachedEvent({onCameraTimeLimitReached}) { | ||
| if (!value.isInitialized || _isDisposed) { | ||
| throw CameraException( | ||
| 'Uninitialized CameraController', | ||
| 'cameraTimeLimitReachedEventStream was called on uninitialized CameraController', | ||
| ); | ||
| } | ||
| if (!value.isRecordingVideo) { | ||
| throw CameraException( | ||
| 'No video is recording', | ||
| 'cameraTimeLimitReachedEventStream was called when no video is recording.', | ||
| ); | ||
| } | ||
|
|
||
| CameraPlatform.instance | ||
| .onCameraTimeLimitReached(_cameraId) | ||
| .first | ||
| .then((event) { | ||
| value = value.copyWith(isRecordingVideo: false); | ||
| onCameraTimeLimitReached(event.path); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this would be easier for users to manage if we can use this as a Stream<XFile> and simply listen for a file to arrive.
We can even extend the stopVideoRecording method to add the final video file to the same stream. This way developers simply listen to the stream to get informed that a new video file is available (no matter if the end-user pressed the stop button or if the max time limit has been reached). Of course this would mean the name of the event and the stream should be updated a to reflect this change. Something like:
Stream<VideoRecordedEvent> onVideoRecorded(int cameraId); | } | ||
|
|
||
| class CameraTimeLimitReachedEvent extends CameraEvent { | ||
| final XFile path; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Rename to file instead of path:
| final XFile path; | |
| final XFile file; |
| } | ||
|
|
||
| @override | ||
| Stream<CameraTimeLimitReachedEvent> onCameraTimeLimitReached(int cameraId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to rename this to onVideoRecordingTimeLimitReached, so it is clear this is directly related to the video recording feature. Of course if you accept my earlier comment about also emitting the recorded video on this stream when the user calls stopVideoRecording we should rename it to something more generic like onVideoRecorded.
| @override | ||
| Stream<CameraTimeLimitReachedEvent> onCameraTimeLimitReached(int cameraId) { | ||
| throw UnimplementedError('onCameraTimeLimitReached() is not implemented.'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to rename this to onVideoRecordingTimeLimitReached, so it is clear this is directly related to the video recording feature. Of course if you accept my earlier comment about also emitting the recorded video on this stream when the user calls stopVideoRecording we should rename it to something more generic like onVideoRecorded.
| As of version [0.6.5](https://github.com/flutter/plugins/blob/master/packages/camera/camera/CHANGELOG.md#065) the startVideoRecording method can be used with the maxVideoDuration. To do this the result of the recording needs to be retrieved by calling controller.onCameraTimeLimitReachedEvent which accepts a callback to retrieve the XFile result. Like so: | ||
|
|
||
| ```dart | ||
| Future<void> startVideoRecording() async { | ||
| if (!controller.value.isInitialized) { | ||
| showInSnackBar('Error: select a camera first.'); | ||
| return; | ||
| } | ||
| if (controller.value.isRecordingVideo) { | ||
| // A recording is already started, do nothing. | ||
| return; | ||
| } | ||
| try { | ||
| await controller.startVideoRecording( | ||
| maxVideoDuration: const Duration(milliseconds: 5000), | ||
| ); | ||
| controller.onCameraTimeLimitReachedEvent(onCameraTimeLimitReached: (XFile file) { | ||
| //Handle the XFile | ||
| }); | ||
| } on CameraException catch (e) { | ||
| _showCameraException(e); | ||
| return; | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really like that you didn't forget to updated the documentation in the README.md. But this should probably also be updated according the the discussion @BeMacized started on using a stream instead of registering a callback method.
# Conflicts: # packages/camera/camera/CHANGELOG.md # packages/camera/camera/pubspec.yaml
|
add a full example please |
Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.
Adds implementation for defining a maximum video duration.
List which issues are fixed by this PR. You must list at least one issue.
TODO
If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.
TODO
Pre-launch Checklist
[shared_preferences]///).If you need help, consider asking for advice on the #hackers-new channel on Discord.