Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[camera] Limit video length with maxVideoDuration on startVideoRecording #3403

Closed
wants to merge 25 commits into from

Conversation

danielroek
Copy link
Contributor

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

  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test exempt.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

danielroek and others added 10 commits December 23, 2020 11:49
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
@google-cla google-cla bot added the cla: yes label Jan 11, 2021
Comment on lines +11 to +12
camera_platform_interface: #^1.5.0
path: ../camera_platform_interface
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove path

Copy link
Contributor

@BeMacized BeMacized left a 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.

@@ -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
Copy link
Contributor

@BeMacized BeMacized Jan 11, 2021

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining TODO

Comment on lines 489 to 510
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);
});
}
Copy link
Contributor

@BeMacized BeMacized Jan 11, 2021

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?

Copy link
Contributor

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); 

Copy link
Contributor Author

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

Comment on lines 111 to 114
@override
Stream<CameraTimeLimitReachedEvent> onCameraTimeLimitReached(int cameraId) {
throw UnimplementedError('onCameraTimeLimitReached() is not implemented.');
}
Copy link
Contributor

@BeMacized BeMacized Jan 11, 2021

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.

Copy link
Contributor

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.

Comment on lines 489 to 510
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);
});
}
Copy link
Contributor

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;
Copy link
Contributor

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:

Suggested change
final XFile path;
final XFile file;

@@ -162,6 +162,11 @@ class MethodChannelCamera extends CameraPlatform {
.whereType<DeviceOrientationChangedEvent>();
}

@override
Stream<CameraTimeLimitReachedEvent> onCameraTimeLimitReached(int cameraId) {
Copy link
Contributor

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.

Comment on lines 111 to 114
@override
Stream<CameraTimeLimitReachedEvent> onCameraTimeLimitReached(int cameraId) {
throw UnimplementedError('onCameraTimeLimitReached() is not implemented.');
}
Copy link
Contributor

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.

Comment on lines +69 to +95
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;
}
}
```
Copy link
Contributor

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.

@danielroek danielroek closed this Feb 5, 2021
@kiaxseventh
Copy link

add a full example please

@mvanbeusekom mvanbeusekom deleted the limit_video_length branch September 21, 2021 09:31
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants