-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[camera] Limit video length with maxVideoDuration on startVideoRecording #3403
Conversation
Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>
Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>
# 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
Outdated
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
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
Outdated
Show resolved
Hide resolved
packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java
Outdated
Show resolved
Hide resolved
@@ -381,7 +381,9 @@ class CameraController extends ValueNotifier<CameraValue> { | |||
/// | |||
/// The video is returned as a [XFile] after calling [stopVideoRecording]. | |||
/// 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 🙂 )
@@ -482,6 +485,30 @@ class CameraController extends ValueNotifier<CameraValue> { | |||
} | |||
} | |||
|
|||
/// 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
Outdated
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);
@@ -235,3 +235,33 @@ class CameraErrorEvent extends CameraEvent { | |||
@override | |||
int get hashCode => super.hashCode ^ description.hashCode; | |||
} | |||
|
|||
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; |
@@ -162,6 +162,11 @@ class MethodChannelCamera extends CameraPlatform { | |||
.whereType<DeviceOrientationChangedEvent>(); | |||
} | |||
|
|||
@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.