Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2971c85
Pause/resume video recording for Android
huulbaek Mar 21, 2019
3e87cac
Specify type
bparrishMines Aug 1, 2019
d99f9d0
Merge branch 'master' of github.com:flutter/plugins into huulbaek/pau…
bparrishMines Aug 1, 2019
474604d
Merge branch 'master' of github.com:flutter/plugins into huulbaek/pau…
bparrishMines Aug 5, 2019
423adcd
Add pausing and resuming to example app
bparrishMines Aug 6, 2019
0214093
iOS side of pausing/resuming
bparrishMines Aug 6, 2019
7216d4a
Merge branch 'master' of github.com:flutter/plugins into huulbaek/pau…
bparrishMines Aug 7, 2019
113cd55
More documentation
bparrishMines Aug 7, 2019
5664547
Version bump
bparrishMines Aug 7, 2019
9210c26
Merge branch 'master' of github.com:flutter/plugins into huulbaek/pau…
bparrishMines Aug 12, 2019
3f47c60
Add video pausing and resuming
bparrishMines Aug 14, 2019
5baee78
get pausing and recording to work for no audio
bparrishMines Aug 15, 2019
da26df1
It works
bparrishMines Aug 16, 2019
b56d1fb
Merge branch 'pauseresume' of github.com:huulbaek/plugins into huulba…
bparrishMines Aug 16, 2019
542ee3d
Formatting
bparrishMines Aug 16, 2019
5023773
Add test for pausing and resuming
bparrishMines Aug 19, 2019
6dbbf8d
Merge branch 'master' into pauseresume
bparrishMines Aug 19, 2019
ca47f38
Call success outside try catch block
bparrishMines Aug 19, 2019
c758b26
formatting
bparrishMines Aug 19, 2019
70e1b13
Merge branch 'pauseresume' of github.com:huulbaek/plugins into huulba…
bparrishMines Aug 19, 2019
8ff306f
Disable audio in test and call result on iOS
bparrishMines Aug 19, 2019
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/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.5.3

* Add feature to pause and resume video recording.

## 0.5.2+2

* Fix memory leak related to not unregistering stream handler in FlutterEventChannel when disposing camera.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,34 @@ public void stopVideoRecording(@NonNull final Result result) {
}
}

public void pauseVideoRecording(@NonNull final Result result) {
if (!recordingVideo) {
result.success(null);
return;
}

try {
mediaRecorder.pause();
result.success(null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: This should be outside of the try block here and below, just in the weird offhand chance calling this throws an IllegalStateException. If it did result.error would also throw in the catch block.

} catch (IllegalStateException e) {
result.error("videoRecordingFailed", e.getMessage(), null);
}
}

public void resumeVideoRecording(@NonNull final Result result) {
if (!recordingVideo) {
result.success(null);
return;
}

try {
mediaRecorder.resume();
result.success(null);
} catch (IllegalStateException e) {
result.error("videoRecordingFailed", e.getMessage(), null);
}
}

public void startPreview() throws CameraAccessException {
createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, pictureImageReader.getSurface());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
camera.stopVideoRecording(result);
break;
}
case "pauseVideoRecording":
{
camera.pauseVideoRecording(result);
break;
}
case "resumeVideoRecording":
{
camera.resumeVideoRecording(result);
break;
}
case "startImageStream":
{
try {
Expand Down
53 changes: 53 additions & 0 deletions packages/camera/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,19 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
? onVideoRecordButtonPressed
: null,
),
IconButton(
icon: controller != null && controller.value.isRecordingPaused
? Icon(Icons.play_arrow)
: Icon(Icons.pause),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? (controller != null && controller.value.isRecordingPaused
? onResumeButtonPressed
: onPauseButtonPressed)
: null,
),
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
Expand Down Expand Up @@ -308,6 +321,20 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
});
}

void onPauseButtonPressed() {
pauseVideoRecording().then((_) {
if (mounted) setState(() {});
showInSnackBar('Video recording paused');
});
}

void onResumeButtonPressed() {
resumeVideoRecording().then((_) {
if (mounted) setState(() {});
showInSnackBar('Video recording resumed');
});
}

Future<String> startVideoRecording() async {
if (!controller.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
Expand Down Expand Up @@ -349,6 +376,32 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
await _startVideoPlayer();
}

Future<void> pauseVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}

try {
await controller.pauseVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}

Future<void> resumeVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}

try {
await controller.resumeVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
}

Future<void> _startVideoPlayer() async {
final VideoPlayerController vcontroller =
VideoPlayerController.file(File(videoPath));
Expand Down
16 changes: 15 additions & 1 deletion packages/camera/ios/Classes/CameraPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ @interface FLTCam : NSObject <FlutterTexture,
@property(strong, nonatomic) AVCaptureVideoDataOutput *videoOutput;
@property(strong, nonatomic) AVCaptureAudioDataOutput *audioOutput;
@property(assign, nonatomic) BOOL isRecording;
@property(assign, nonatomic) BOOL isRecordingPaused;
@property(assign, nonatomic) BOOL isAudioSetup;
@property(assign, nonatomic) BOOL isStreamingImages;
@property(nonatomic) CMMotionManager *motionManager;
Expand Down Expand Up @@ -365,7 +366,7 @@ - (void)captureOutput:(AVCaptureOutput *)output
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
}
}
if (_isRecording) {
if (_isRecording && !_isRecordingPaused) {
if (_videoWriter.status == AVAssetWriterStatusFailed) {
_eventSink(@{
@"event" : @"error",
Expand Down Expand Up @@ -474,6 +475,7 @@ - (void)startVideoRecordingAtPath:(NSString *)path result:(FlutterResult)result
return;
}
_isRecording = YES;
_isRecordingPaused = NO;
result(nil);
} else {
_eventSink(@{@"event" : @"error", @"errorDescription" : @"Video is already recording!"});
Expand Down Expand Up @@ -504,6 +506,14 @@ - (void)stopVideoRecordingWithResult:(FlutterResult)result {
}
}

- (void)pauseVideoRecording {
_isRecordingPaused = YES;
}

- (void)resumeVideoRecording {
_isRecordingPaused = NO;
}

- (void)startImageStreamWithMessenger:(NSObject<FlutterBinaryMessenger> *)messenger {
if (!_isStreamingImages) {
FlutterEventChannel *eventChannel =
Expand Down Expand Up @@ -725,6 +735,10 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)re
} else if ([@"stopImageStream" isEqualToString:call.method]) {
[_camera stopImageStream];
result(nil);
} else if ([@"pauseVideoRecording" isEqualToString:call.method]) {
[_camera pauseVideoRecording];
} else if ([@"resumeVideoRecording" isEqualToString:call.method]) {
[_camera resumeVideoRecording];
} else {
NSDictionary *argsMap = call.arguments;
NSUInteger textureId = ((NSNumber *)argsMap[@"textureId"]).unsignedIntegerValue;
Expand Down
76 changes: 70 additions & 6 deletions packages/camera/lib/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,17 @@ class CameraValue {
this.isRecordingVideo,
this.isTakingPicture,
this.isStreamingImages,
});
bool isRecordingPaused,
}) : _isRecordingPaused = isRecordingPaused;

const CameraValue.uninitialized()
: this(
isInitialized: false,
isRecordingVideo: false,
isTakingPicture: false,
isStreamingImages: false);
isInitialized: false,
isRecordingVideo: false,
isTakingPicture: false,
isStreamingImages: false,
isRecordingPaused: false,
);

/// True after [CameraController.initialize] has completed successfully.
final bool isInitialized;
Expand All @@ -151,6 +154,11 @@ class CameraValue {
/// True when images from the camera are being streamed.
final bool isStreamingImages;

final bool _isRecordingPaused;

/// True when camera [isRecordingVideo] and recording is paused.
bool get isRecordingPaused => isRecordingVideo && _isRecordingPaused;

final String errorDescription;

/// The size of the preview in pixels.
Expand All @@ -172,6 +180,7 @@ class CameraValue {
bool isStreamingImages,
String errorDescription,
Size previewSize,
bool isRecordingPaused,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
Expand All @@ -180,6 +189,7 @@ class CameraValue {
isRecordingVideo: isRecordingVideo ?? this.isRecordingVideo,
isTakingPicture: isTakingPicture ?? this.isTakingPicture,
isStreamingImages: isStreamingImages ?? this.isStreamingImages,
isRecordingPaused: isRecordingPaused ?? _isRecordingPaused,
);
}

Expand Down Expand Up @@ -446,7 +456,7 @@ class CameraController extends ValueNotifier<CameraValue> {
'startVideoRecording',
<String, dynamic>{'textureId': _textureId, 'filePath': filePath},
);
value = value.copyWith(isRecordingVideo: true);
value = value.copyWith(isRecordingVideo: true, isRecordingPaused: false);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
Expand Down Expand Up @@ -477,6 +487,60 @@ class CameraController extends ValueNotifier<CameraValue> {
}
}

/// Pause video recording.
///
/// This feature is only available on iOS and Android sdk 24+.
Future<void> pauseVideoRecording() async {
if (!value.isInitialized || _isDisposed) {
throw CameraException(
'Uninitialized CameraController',
'pauseVideoRecording was called on uninitialized CameraController',
);
}
if (!value.isRecordingVideo) {
throw CameraException(
'No video is recording',
'pauseVideoRecording was called when no video is recording.',
);
}
try {
value = value.copyWith(isRecordingPaused: true);
await _channel.invokeMethod<void>(
'pauseVideoRecording',
<String, dynamic>{'textureId': _textureId},
);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
}

/// Resume video recording after pausing.
///
/// This feature is only available on iOS and Android sdk 24+.
Future<void> resumeVideoRecording() async {
if (!value.isInitialized || _isDisposed) {
throw CameraException(
'Uninitialized CameraController',
'resumeVideoRecording was called on uninitialized CameraController',
);
}
if (!value.isRecordingVideo) {
throw CameraException(
'No video is recording',
'resumeVideoRecording was called when no video is recording.',
);
}
try {
value = value.copyWith(isRecordingPaused: false);
await _channel.invokeMethod<void>(
'resumeVideoRecording',
<String, dynamic>{'textureId': _textureId},
);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
}

/// Releases the resources of this camera.
@override
Future<void> dispose() async {
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera
description: A Flutter plugin for getting information about and controlling the
camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
and streaming image buffers to dart.
version: 0.5.2+2
version: 0.5.3

authors:
- Flutter Team <flutter-dev@googlegroups.com>
Expand Down