Compare commits
	
		
			7 Commits
		
	
	
		
			0a43d7a160
			...
			694d9cfa8c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 694d9cfa8c | ||
|  | 91767e71c8 | ||
|  | 9f2c7906e5 | ||
|  | 621bfe333c | ||
|  | 20f8fa2937 | ||
|  | b03f9ea423 | ||
|  | 98d90a6442 | 
| @@ -11,7 +11,7 @@ import AVFoundation | |||||||
| // MARK: - CameraView + AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate | // MARK: - CameraView + AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate | ||||||
|  |  | ||||||
| extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate { | extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate { | ||||||
|   func startRecording(options: NSDictionary, callback jsCallback: @escaping RCTResponseSenderBlock) { |   func startRecording(options: NSDictionary, filePath: String, callback jsCallback: @escaping RCTResponseSenderBlock) { | ||||||
|     // Type-safety |     // Type-safety | ||||||
|     let callback = Callback(jsCallback) |     let callback = Callback(jsCallback) | ||||||
|  |  | ||||||
| @@ -21,6 +21,7 @@ extension CameraView: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAud | |||||||
|       // Start Recording with success and error callbacks |       // Start Recording with success and error callbacks | ||||||
|       cameraSession.startRecording( |       cameraSession.startRecording( | ||||||
|         options: options, |         options: options, | ||||||
|  |         filePath: filePath, | ||||||
|         onVideoRecorded: { video in |         onVideoRecorded: { video in | ||||||
|           callback.resolve(video.toJSValue()) |           callback.resolve(video.toJSValue()) | ||||||
|         }, |         }, | ||||||
|   | |||||||
| @@ -342,6 +342,7 @@ public final class CameraView: UIView, CameraSessionDelegate { | |||||||
|     ReactLogger.log(level: .info, message: "Chunk ready: \(chunk)") |     ReactLogger.log(level: .info, message: "Chunk ready: \(chunk)") | ||||||
|      |      | ||||||
|     guard let onVideoChunkReady, let onInitReady else { |     guard let onVideoChunkReady, let onInitReady else { | ||||||
|  |       ReactLogger.log(level: .warning, message: "Either onInitReady or onVideoChunkReady are not valid!") | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|      |      | ||||||
|   | |||||||
| @@ -64,7 +64,8 @@ RCT_EXPORT_VIEW_PROPERTY(onCodeScanned, RCTDirectEventBlock); | |||||||
| // Camera View Functions | // Camera View Functions | ||||||
| RCT_EXTERN_METHOD(startRecording | RCT_EXTERN_METHOD(startRecording | ||||||
|                   : (nonnull NSNumber*)node options |                   : (nonnull NSNumber*)node options | ||||||
|                   : (NSDictionary*)options onRecordCallback |                   : (NSDictionary*)options filePath | ||||||
|  |                   : (NSString*)filePath onRecordCallback | ||||||
|                   : (RCTResponseSenderBlock)onRecordCallback); |                   : (RCTResponseSenderBlock)onRecordCallback); | ||||||
| RCT_EXTERN_METHOD(pauseRecording | RCT_EXTERN_METHOD(pauseRecording | ||||||
|                   : (nonnull NSNumber*)node resolve |                   : (nonnull NSNumber*)node resolve | ||||||
|   | |||||||
| @@ -43,9 +43,9 @@ final class CameraViewManager: RCTViewManager { | |||||||
|   //       This means that any errors that occur in this function have to be delegated through |   //       This means that any errors that occur in this function have to be delegated through | ||||||
|   //       the callback, but I'd prefer for them to throw for the original function instead. |   //       the callback, but I'd prefer for them to throw for the original function instead. | ||||||
|   @objc |   @objc | ||||||
|   final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) { |   final func startRecording(_ node: NSNumber, options: NSDictionary, filePath: NSString, onRecordCallback: @escaping RCTResponseSenderBlock) { | ||||||
|     let component = getCameraView(withTag: node) |     let component = getCameraView(withTag: node) | ||||||
|     component.startRecording(options: options, callback: onRecordCallback) |     component.startRecording(options: options, filePath: filePath as String, callback: onRecordCallback) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @objc |   @objc | ||||||
|   | |||||||
| @@ -176,6 +176,7 @@ enum CaptureError { | |||||||
|   case noRecordingInProgress |   case noRecordingInProgress | ||||||
|   case fileError |   case fileError | ||||||
|   case createTempFileError(message: String? = nil) |   case createTempFileError(message: String? = nil) | ||||||
|  |   case createRecordingDirectoryError(message: String? = nil) | ||||||
|   case createRecorderError(message: String? = nil) |   case createRecorderError(message: String? = nil) | ||||||
|   case videoNotEnabled |   case videoNotEnabled | ||||||
|   case photoNotEnabled |   case photoNotEnabled | ||||||
| @@ -193,6 +194,8 @@ enum CaptureError { | |||||||
|       return "file-io-error" |       return "file-io-error" | ||||||
|     case .createTempFileError: |     case .createTempFileError: | ||||||
|       return "create-temp-file-error" |       return "create-temp-file-error" | ||||||
|  |     case .createRecordingDirectoryError: | ||||||
|  |       return "create-recording-directory-error" | ||||||
|     case .createRecorderError: |     case .createRecorderError: | ||||||
|       return "create-recorder-error" |       return "create-recorder-error" | ||||||
|     case .videoNotEnabled: |     case .videoNotEnabled: | ||||||
| @@ -218,6 +221,8 @@ enum CaptureError { | |||||||
|       return "An unexpected File IO error occured!" |       return "An unexpected File IO error occured!" | ||||||
|     case let .createTempFileError(message: message): |     case let .createTempFileError(message: message): | ||||||
|       return "Failed to create a temporary file! \(message ?? "(no additional message)")" |       return "Failed to create a temporary file! \(message ?? "(no additional message)")" | ||||||
|  |     case let .createRecordingDirectoryError(message: message): | ||||||
|  |       return "Failed to create a recording directory! \(message ?? "(no additional message)")" | ||||||
|     case let .createRecorderError(message: message): |     case let .createRecorderError(message: message): | ||||||
|       return "Failed to create the AVAssetWriter (Recorder)! \(message ?? "(no additional message)")" |       return "Failed to create the AVAssetWriter (Recorder)! \(message ?? "(no additional message)")" | ||||||
|     case .videoNotEnabled: |     case .videoNotEnabled: | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ extension CameraSession { | |||||||
|    Starts a video + audio recording with a custom Asset Writer. |    Starts a video + audio recording with a custom Asset Writer. | ||||||
|    */ |    */ | ||||||
|   func startRecording(options: RecordVideoOptions, |   func startRecording(options: RecordVideoOptions, | ||||||
|  |                       filePath: String, | ||||||
|                       onVideoRecorded: @escaping (_ video: Video) -> Void, |                       onVideoRecorded: @escaping (_ video: Video) -> Void, | ||||||
|                       onError: @escaping (_ error: CameraError) -> Void) { |                       onError: @escaping (_ error: CameraError) -> Void) { | ||||||
|     // Run on Camera Queue |     // Run on Camera Queue | ||||||
| @@ -70,7 +71,7 @@ extension CameraSession { | |||||||
|         } else { |         } else { | ||||||
|           if status == .completed { |           if status == .completed { | ||||||
|             // Recording was successfully saved |             // Recording was successfully saved | ||||||
|             let video = Video(path: recordingSession.url.absoluteString, |             let video = Video(path: recordingSession.outputDiretory.absoluteString, | ||||||
|                               duration: recordingSession.duration, |                               duration: recordingSession.duration, | ||||||
|                               size: recordingSession.size ?? CGSize.zero) |                               size: recordingSession.size ?? CGSize.zero) | ||||||
|             onVideoRecorded(video) |             onVideoRecorded(video) | ||||||
| @@ -81,21 +82,20 @@ extension CameraSession { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // Create temporary file |       if !FileManager.default.fileExists(atPath: filePath) { | ||||||
|       let errorPointer = ErrorPointer(nilLiteral: ()) |         do { | ||||||
|       let fileExtension = options.fileType.descriptor ?? "mov" |           try FileManager.default.createDirectory(atPath: filePath, withIntermediateDirectories: true) | ||||||
|       guard let tempFilePath = RCTTempFilePath(fileExtension, errorPointer) else { |         } catch { | ||||||
|         let message = errorPointer?.pointee?.description |           onError(.capture(.createRecordingDirectoryError(message: error.localizedDescription))) | ||||||
|         onError(.capture(.createTempFileError(message: message))) |  | ||||||
|           return |           return | ||||||
|         } |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|       ReactLogger.log(level: .info, message: "Will record to temporary file: \(tempFilePath)") |       ReactLogger.log(level: .info, message: "Will record to temporary file: \(filePath)") | ||||||
|       let tempURL = URL(string: "file://\(tempFilePath)")! |  | ||||||
|  |  | ||||||
|       do { |       do { | ||||||
|         // Create RecordingSession for the temp file |         // Create RecordingSession for the temp file | ||||||
|         let recordingSession = try RecordingSession(url: tempURL, |         let recordingSession = try RecordingSession(outputDiretory: filePath, | ||||||
|                                                     fileType: options.fileType, |                                                     fileType: options.fileType, | ||||||
|                                                     onChunkReady: onChunkReady, |                                                     onChunkReady: onChunkReady, | ||||||
|                                                     completion: onFinish) |                                                     completion: onFinish) | ||||||
|   | |||||||
| @@ -25,10 +25,10 @@ class ChunkedRecorder: NSObject { | |||||||
|   let outputURL: URL |   let outputURL: URL | ||||||
|   let onChunkReady: ((Chunk) -> Void) |   let onChunkReady: ((Chunk) -> Void) | ||||||
|    |    | ||||||
|   private var index: UInt64 = 0 |   private var chunkIndex: UInt64 = 0 | ||||||
|    |    | ||||||
|   init(url: URL, onChunkReady: @escaping ((Chunk) -> Void)) throws { |   init(outputURL: URL, onChunkReady: @escaping ((Chunk) -> Void)) throws { | ||||||
|     outputURL = url |     self.outputURL = outputURL | ||||||
|     self.onChunkReady = onChunkReady |     self.onChunkReady = onChunkReady | ||||||
|     guard FileManager.default.fileExists(atPath: outputURL.path) else { |     guard FileManager.default.fileExists(atPath: outputURL.path) else { | ||||||
|       throw CameraError.unknown(message: "output directory does not exist at: \(outputURL.path)", cause: nil) |       throw CameraError.unknown(message: "output directory does not exist at: \(outputURL.path)", cause: nil) | ||||||
| @@ -61,13 +61,11 @@ extension ChunkedRecorder: AVAssetWriterDelegate { | |||||||
|   } |   } | ||||||
|    |    | ||||||
|   private func saveSegment(_ data: Data) { |   private func saveSegment(_ data: Data) { | ||||||
|     defer { |     let name = "\(chunkIndex).mp4" | ||||||
|       index += 1 |  | ||||||
|     } |  | ||||||
|     let name = String(format: "%06d.mp4", index) |  | ||||||
|     let url = outputURL.appendingPathComponent(name) |     let url = outputURL.appendingPathComponent(name) | ||||||
|     save(data: data, url: url) |     save(data: data, url: url) | ||||||
|     onChunkReady(url: url, type: .data(index: index)) |     onChunkReady(url: url, type: .data(index: chunkIndex)) | ||||||
|  |     chunkIndex += 1 | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   private func save(data: Data, url: URL) { |   private func save(data: Data, url: URL) { | ||||||
|   | |||||||
| @@ -49,8 +49,7 @@ class RecordingSession { | |||||||
|   /** |   /** | ||||||
|    Gets the file URL of the recorded video. |    Gets the file URL of the recorded video. | ||||||
|    */ |    */ | ||||||
|   var url: URL { |   var outputDiretory: URL { | ||||||
|     // FIXME: |  | ||||||
|     return recorder.outputURL |     return recorder.outputURL | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -72,14 +71,15 @@ class RecordingSession { | |||||||
|     return (lastWrittenTimestamp - startTimestamp).seconds |     return (lastWrittenTimestamp - startTimestamp).seconds | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   init(url: URL, |   init(outputDiretory: String, | ||||||
|        fileType: AVFileType, |        fileType: AVFileType, | ||||||
|        onChunkReady: @escaping ((ChunkedRecorder.Chunk) -> Void), |        onChunkReady: @escaping ((ChunkedRecorder.Chunk) -> Void), | ||||||
|        completion: @escaping (RecordingSession, AVAssetWriter.Status, Error?) -> Void) throws { |        completion: @escaping (RecordingSession, AVAssetWriter.Status, Error?) -> Void) throws { | ||||||
|     completionHandler = completion |     completionHandler = completion | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
|       recorder = try ChunkedRecorder(url: url.deletingLastPathComponent(), onChunkReady: onChunkReady) |       let outputURL = URL(fileURLWithPath: outputDiretory) | ||||||
|  |       recorder = try ChunkedRecorder(outputURL: outputURL, onChunkReady: onChunkReady) | ||||||
|       assetWriter = AVAssetWriter(contentType: UTType(fileType.rawValue)!) |       assetWriter = AVAssetWriter(contentType: UTType(fileType.rawValue)!) | ||||||
|       assetWriter.shouldOptimizeForNetworkUse = false |       assetWriter.shouldOptimizeForNetworkUse = false | ||||||
|       assetWriter.outputFileTypeProfile = .mpeg4AppleHLS |       assetWriter.outputFileTypeProfile = .mpeg4AppleHLS | ||||||
|   | |||||||
| @@ -80,3 +80,23 @@ func RCTTempFilePath(_ ext: String, _ error: ErrorPointer) -> String? { | |||||||
|     .appending("/").appending(UUID().uuidString) |     .appending("/").appending(UUID().uuidString) | ||||||
|     .appending(".").appending(ext) |     .appending(".").appending(ext) | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class RCTViewManager: NSObject { | ||||||
|  |    | ||||||
|  |   var methodQueue: DispatchQueue! { nil } | ||||||
|  |   class func requiresMainQueueSetup() -> Bool { false } | ||||||
|  |   func view() -> UIView! { nil } | ||||||
|  |    | ||||||
|  |   struct Bridge { | ||||||
|  |     let uiManager = UIManager() | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   struct UIManager { | ||||||
|  |     func view(forReactTag: NSNumber) -> UIView! { | ||||||
|  |       nil | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   let bridge: Bridge = Bridge() | ||||||
|  | } | ||||||
|   | |||||||
| @@ -14,10 +14,15 @@ class ViewController: UIViewController { | |||||||
|   @IBOutlet weak var recordButton: UIButton! |   @IBOutlet weak var recordButton: UIButton! | ||||||
|    |    | ||||||
|   let cameraView = CameraView() |   let cameraView = CameraView() | ||||||
|  |   let filePath: String = { | ||||||
|  |     NSTemporaryDirectory() + "TestRecorder" | ||||||
|  |   }() | ||||||
|    |    | ||||||
|   override func viewDidLoad() { |   override func viewDidLoad() { | ||||||
|     super.viewDidLoad() |     super.viewDidLoad() | ||||||
|      |      | ||||||
|  |     try? FileManager.default.removeItem(atPath: filePath) | ||||||
|  |      | ||||||
|     cameraView.translatesAutoresizingMaskIntoConstraints = false; |     cameraView.translatesAutoresizingMaskIntoConstraints = false; | ||||||
|     view.insertSubview(cameraView, at: 0) |     view.insertSubview(cameraView, at: 0) | ||||||
|     NSLayoutConstraint.activate([ |     NSLayoutConstraint.activate([ | ||||||
| @@ -33,6 +38,12 @@ class ViewController: UIViewController { | |||||||
|         self.recordButton.isHidden = false |         self.recordButton.isHidden = false | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     cameraView.onInitReady = { json in | ||||||
|  |       print("onInitReady:", json ?? "nil") | ||||||
|  |     } | ||||||
|  |     cameraView.onVideoChunkReady = { json in | ||||||
|  |       print("onVideoChunkReady:", json ?? "nil") | ||||||
|  |     } | ||||||
|      |      | ||||||
|     Task { @MainActor in |     Task { @MainActor in | ||||||
|       await requestAuthorizations() |       await requestAuthorizations() | ||||||
| @@ -92,7 +103,8 @@ class ViewController: UIViewController { | |||||||
|         options: [ |         options: [ | ||||||
|           "fileType": "mp4", |           "fileType": "mp4", | ||||||
|           "videoCodec": "h265", |           "videoCodec": "h265", | ||||||
|                 ]) { callback in |         ], | ||||||
|  |         filePath: filePath) { callback in | ||||||
|           print("callback", callback) |           print("callback", callback) | ||||||
|         } |         } | ||||||
|        |        | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import AVFoundation | |||||||
| import Foundation | import Foundation | ||||||
|  |  | ||||||
| struct RecordVideoOptions { | struct RecordVideoOptions { | ||||||
|   var fileType: AVFileType = .mov |   var fileType: AVFileType = .mp4 | ||||||
|   var flash: Torch = .off |   var flash: Torch = .off | ||||||
|   var codec: AVVideoCodecType? |   var codec: AVVideoCodecType? | ||||||
|   /** |   /** | ||||||
|   | |||||||
| @@ -7,6 +7,9 @@ | |||||||
| 	objects = { | 	objects = { | ||||||
|  |  | ||||||
| /* Begin PBXBuildFile section */ | /* Begin PBXBuildFile section */ | ||||||
|  | 		B31481772C46547B00084A26 /* CameraViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518125E0102000DB86D6 /* CameraViewManager.swift */; }; | ||||||
|  | 		B31481782C46558C00084A26 /* CameraView+TakePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517125E0102000DB86D6 /* CameraView+TakePhoto.swift */; }; | ||||||
|  | 		B31481792C46559700084A26 /* CameraView+Focus.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC52AD7F08E00169C0D /* CameraView+Focus.swift */; }; | ||||||
| 		B3AF8E862C410FB700CC198C /* ReactStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = B3AF8E852C410FB700CC198C /* ReactStubs.m */; }; | 		B3AF8E862C410FB700CC198C /* ReactStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = B3AF8E852C410FB700CC198C /* ReactStubs.m */; }; | ||||||
| 		B3AF8E882C41159300CC198C /* ChunkedRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3AF8E872C41159300CC198C /* ChunkedRecorder.swift */; }; | 		B3AF8E882C41159300CC198C /* ChunkedRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3AF8E872C41159300CC198C /* ChunkedRecorder.swift */; }; | ||||||
| 		B3AF8E892C41159300CC198C /* ChunkedRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3AF8E872C41159300CC198C /* ChunkedRecorder.swift */; }; | 		B3AF8E892C41159300CC198C /* ChunkedRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3AF8E872C41159300CC198C /* ChunkedRecorder.swift */; }; | ||||||
| @@ -691,6 +694,8 @@ | |||||||
| 				B3EF9F502C3FC31E00832EE7 /* AVCaptureVideoDataOutput+recommendedVideoSettings.swift in Sources */, | 				B3EF9F502C3FC31E00832EE7 /* AVCaptureVideoDataOutput+recommendedVideoSettings.swift in Sources */, | ||||||
| 				B3EF9F512C3FC31E00832EE7 /* AVCaptureDevice+minFocusDistance.swift in Sources */, | 				B3EF9F512C3FC31E00832EE7 /* AVCaptureDevice+minFocusDistance.swift in Sources */, | ||||||
| 				B3EF9F5B2C3FC33000832EE7 /* AVCaptureDevice.DeviceType+physicalDeviceDescriptor.swift in Sources */, | 				B3EF9F5B2C3FC33000832EE7 /* AVCaptureDevice.DeviceType+physicalDeviceDescriptor.swift in Sources */, | ||||||
|  | 				B31481792C46559700084A26 /* CameraView+Focus.swift in Sources */, | ||||||
|  | 				B31481772C46547B00084A26 /* CameraViewManager.swift in Sources */, | ||||||
| 				B3EF9F522C3FC31E00832EE7 /* AVCaptureDevice+physicalDevices.swift in Sources */, | 				B3EF9F522C3FC31E00832EE7 /* AVCaptureDevice+physicalDevices.swift in Sources */, | ||||||
| 				B3EF9F532C3FC31E00832EE7 /* AVCaptureDevice+neutralZoom.swift in Sources */, | 				B3EF9F532C3FC31E00832EE7 /* AVCaptureDevice+neutralZoom.swift in Sources */, | ||||||
| 				B3EF9F542C3FC31E00832EE7 /* AVCaptureDevice.Format+dimensions.swift in Sources */, | 				B3EF9F542C3FC31E00832EE7 /* AVCaptureDevice.Format+dimensions.swift in Sources */, | ||||||
| @@ -709,6 +714,7 @@ | |||||||
| 				B3EF9F362C3FC05600832EE7 /* ResizeMode.swift in Sources */, | 				B3EF9F362C3FC05600832EE7 /* ResizeMode.swift in Sources */, | ||||||
| 				B3EF9F312C3FBFD500832EE7 /* AVAssetWriter.Status+descriptor.swift in Sources */, | 				B3EF9F312C3FBFD500832EE7 /* AVAssetWriter.Status+descriptor.swift in Sources */, | ||||||
| 				B3EF9F292C3FBF2500832EE7 /* Torch.swift in Sources */, | 				B3EF9F292C3FBF2500832EE7 /* Torch.swift in Sources */, | ||||||
|  | 				B31481782C46558C00084A26 /* CameraView+TakePhoto.swift in Sources */, | ||||||
| 				B3EF9F2C2C3FBF4A00832EE7 /* EnumParserError.swift in Sources */, | 				B3EF9F2C2C3FBF4A00832EE7 /* EnumParserError.swift in Sources */, | ||||||
| 				B3EF9F272C3FBEF800832EE7 /* PixelFormat.swift in Sources */, | 				B3EF9F272C3FBEF800832EE7 /* PixelFormat.swift in Sources */, | ||||||
| 				B3EF9F652C3FC43C00832EE7 /* CameraSession+Audio.swift in Sources */, | 				B3EF9F652C3FC43C00832EE7 /* CameraSession+Audio.swift in Sources */, | ||||||
|   | |||||||
| @@ -26,6 +26,9 @@ interface OnErrorEvent { | |||||||
|   message: string |   message: string | ||||||
|   cause?: ErrorWithCause |   cause?: ErrorWithCause | ||||||
| } | } | ||||||
|  | interface OnInitReadyEvent { | ||||||
|  |   filepath: string | ||||||
|  | } | ||||||
| interface OnVideoChunkReadyEvent { | interface OnVideoChunkReadyEvent { | ||||||
|   filepath: string |   filepath: string | ||||||
|   index: number |   index: number | ||||||
| @@ -39,6 +42,7 @@ type NativeCameraViewProps = Omit<CameraProps, 'device' | 'onInitialized' | 'onE | |||||||
|   onCodeScanned?: (event: NativeSyntheticEvent<OnCodeScannedEvent>) => void |   onCodeScanned?: (event: NativeSyntheticEvent<OnCodeScannedEvent>) => void | ||||||
|   onStarted?: (event: NativeSyntheticEvent<void>) => void |   onStarted?: (event: NativeSyntheticEvent<void>) => void | ||||||
|   onStopped?: (event: NativeSyntheticEvent<void>) => void |   onStopped?: (event: NativeSyntheticEvent<void>) => void | ||||||
|  |   onInitReady?: (event: NativeSyntheticEvent<OnInitReadyEvent>) => void | ||||||
|   onVideoChunkReady?: (event: NativeSyntheticEvent<OnVideoChunkReadyEvent>) => void |   onVideoChunkReady?: (event: NativeSyntheticEvent<OnVideoChunkReadyEvent>) => void | ||||||
|   onViewReady: () => void |   onViewReady: () => void | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user