feat: Add pauseRecording and resumeRecording 🔥 (#911)

* feat: Add `pauseRecording` and `resumeRecording` (iOS)

* feat: Add `pauseRecording` and `resumeRecording` (Android)

* feat: Add `pauseRecording` and `resumeRecording` (JS)

* fix: Simplify Swift code for Recording
This commit is contained in:
Marc Rousavy 2022-03-22 10:44:58 +01:00 committed by GitHub
parent eb95add5ce
commit 4b9bcb37e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 121 additions and 10 deletions

View File

@ -82,6 +82,30 @@ fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Callback)
}) })
} }
@SuppressLint("RestrictedApi")
fun CameraView.pauseRecording() {
if (videoCapture == null) {
throw CameraNotReadyError()
}
if (activeVideoRecording == null) {
throw NoRecordingInProgressError()
}
activeVideoRecording!!.pause()
}
@SuppressLint("RestrictedApi")
fun CameraView.resumeRecording() {
if (videoCapture == null) {
throw CameraNotReadyError()
}
if (activeVideoRecording == null) {
throw NoRecordingInProgressError()
}
activeVideoRecording!!.resume()
}
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun CameraView.stopRecording() { fun CameraView.stopRecording() {
if (videoCapture == null) { if (videoCapture == null) {

View File

@ -115,6 +115,24 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
} }
} }
@ReactMethod
fun pauseRecording(viewTag: Int, promise: Promise) {
withPromise(promise) {
val view = findCameraView(viewTag)
view.pauseRecording()
return@withPromise null
}
}
@ReactMethod
fun resumeRecording(viewTag: Int, promise: Promise) {
withPromise(promise) {
val view = findCameraView(viewTag)
view.resumeRecording()
return@withPromise null
}
}
@ReactMethod @ReactMethod
fun stopRecording(viewTag: Int, promise: Promise) { fun stopRecording(viewTag: Int, promise: Promise) {
withPromise(promise) { withPromise(promise) {

View File

@ -161,30 +161,28 @@ extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAud
} }
} }
// TODO: Implement for JS
func pauseRecording(promise: Promise) { func pauseRecording(promise: Promise) {
cameraQueue.async { cameraQueue.async {
withPromise(promise) { withPromise(promise) {
if self.isRecording { guard self.recordingSession != nil else {
self.isRecording = false // there's no active recording!
return nil
} else {
throw CameraError.capture(.noRecordingInProgress) throw CameraError.capture(.noRecordingInProgress)
} }
self.isRecording = false
return nil
} }
} }
} }
// TODO: Implement for JS
func resumeRecording(promise: Promise) { func resumeRecording(promise: Promise) {
cameraQueue.async { cameraQueue.async {
withPromise(promise) { withPromise(promise) {
if !self.isRecording { guard self.recordingSession != nil else {
self.isRecording = true // there's no active recording!
return nil
} else {
throw CameraError.capture(.noRecordingInProgress) throw CameraError.capture(.noRecordingInProgress)
} }
self.isRecording = true
return nil
} }
} }
} }

View File

@ -54,6 +54,8 @@ RCT_EXPORT_VIEW_PROPERTY(onViewReady, RCTDirectEventBlock);
// Camera View Functions // Camera View Functions
RCT_EXTERN_METHOD(startRecording:(nonnull NSNumber *)node options:(NSDictionary *)options onRecordCallback:(RCTResponseSenderBlock)onRecordCallback); RCT_EXTERN_METHOD(startRecording:(nonnull NSNumber *)node options:(NSDictionary *)options onRecordCallback:(RCTResponseSenderBlock)onRecordCallback);
RCT_EXTERN_METHOD(pauseRecording:(nonnull NSNumber *)node resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(resumeRecording:(nonnull NSNumber *)node resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(stopRecording:(nonnull NSNumber *)node resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject); RCT_EXTERN_METHOD(stopRecording:(nonnull NSNumber *)node resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(takePhoto:(nonnull NSNumber *)node options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject); RCT_EXTERN_METHOD(takePhoto:(nonnull NSNumber *)node options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);
RCT_EXTERN_METHOD(focus:(nonnull NSNumber *)node point:(NSDictionary *)point resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject); RCT_EXTERN_METHOD(focus:(nonnull NSNumber *)node point:(NSDictionary *)point resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject);

View File

@ -49,6 +49,18 @@ final class CameraViewManager: RCTViewManager {
component.startRecording(options: options, callback: onRecordCallback) component.startRecording(options: options, callback: onRecordCallback)
} }
@objc
final func pauseRecording(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let component = getCameraView(withTag: node)
component.pauseRecording(promise: Promise(resolver: resolve, rejecter: reject))
}
@objc
final func resumeRecording(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let component = getCameraView(withTag: node)
component.resumeRecording(promise: Promise(resolver: resolve, rejecter: reject))
}
@objc @objc
final func stopRecording(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { final func stopRecording(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let component = getCameraView(withTag: node) let component = getCameraView(withTag: node)

View File

@ -190,6 +190,63 @@ export class Camera extends React.PureComponent<CameraProps> {
throw tryParseNativeCameraError(e); throw tryParseNativeCameraError(e);
} }
} }
/**
* Pauses the current video recording.
*
* @throws {@linkcode CameraCaptureError} When any kind of error occured while pausing the video recording. Use the {@linkcode CameraCaptureError.code | code} property to get the actual error
*
* @example
* ```ts
* // Start
* await camera.current.startRecording()
* await timeout(1000)
* // Pause
* await camera.current.pauseRecording()
* await timeout(500)
* // Resume
* await camera.current.resumeRecording()
* await timeout(2000)
* // Stop
* const video = await camera.current.stopRecording()
* ```
*/
public async pauseRecording(): Promise<void> {
try {
return await CameraModule.pauseRecording(this.handle);
} catch (e) {
throw tryParseNativeCameraError(e);
}
}
/**
* Resumes a currently paused video recording.
*
* @throws {@linkcode CameraCaptureError} When any kind of error occured while resuming the video recording. Use the {@linkcode CameraCaptureError.code | code} property to get the actual error
*
* @example
* ```ts
* // Start
* await camera.current.startRecording()
* await timeout(1000)
* // Pause
* await camera.current.pauseRecording()
* await timeout(500)
* // Resume
* await camera.current.resumeRecording()
* await timeout(2000)
* // Stop
* const video = await camera.current.stopRecording()
* ```
*/
public async resumeRecording(): Promise<void> {
try {
return await CameraModule.resumeRecording(this.handle);
} catch (e) {
throw tryParseNativeCameraError(e);
}
}
/** /**
* Stop the current video recording. * Stop the current video recording.
* *