From 2fb8ba03780ff5b4156db4f2349c7402dd6f3e84 Mon Sep 17 00:00:00 2001 From: Shiva Prasad Date: Wed, 16 Oct 2024 11:33:37 +1300 Subject: [PATCH] feat: added support to configure video bitrate, improved default video frame for android --- README.md | 14 +- .../videorecorder/VideoRecorder.java | 469 ------------------ .../videorecorder/VideoRecorderPlugin.java | 67 ++- example/package-lock.json | 2 +- example/src/app/home/home.page.ts | 51 +- ios/Plugin/Plugin.swift | 36 +- package-lock.json | 4 +- package.json | 2 +- src/definitions.ts | 7 + 9 files changed, 125 insertions(+), 527 deletions(-) delete mode 100644 android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorder.java diff --git a/README.md b/README.md index ae6d467..9844ce1 100644 --- a/README.md +++ b/README.md @@ -286,12 +286,14 @@ addListener(eventName: 'onVolumeInput', listenerFunc: (event: { value: number; } #### VideoRecorderOptions -| Prop | Type | -| ------------------- | --------------------------------------------------------------------- | -| **`camera`** | VideoRecorderCamera | -| **`quality`** | VideoRecorderQuality | -| **`autoShow`** | boolean | -| **`previewFrames`** | VideoRecorderPreviewFrame[] | +| Prop | Type | Description | Default | +| ------------------- | --------------------------------------------------------------------- | ------------------------------ | -------------------- | +| **`camera`** | VideoRecorderCamera | | | +| **`quality`** | VideoRecorderQuality | | | +| **`autoShow`** | boolean | | | +| **`previewFrames`** | VideoRecorderPreviewFrame[] | | | +| **`videoBitrate`** | number | The default bitrate is 4.5Mbps | 4500000 | + #### VideoRecorderPreviewFrame diff --git a/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorder.java b/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorder.java deleted file mode 100644 index 51af48c..0000000 --- a/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorder.java +++ /dev/null @@ -1,469 +0,0 @@ -package com.capacitorcommunity.videorecorder; - -import android.graphics.Color; -import android.net.Uri; -import android.util.DisplayMetrics; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.getcapacitor.FileUtils; -import com.getcapacitor.JSArray; -import com.getcapacitor.JSObject; -import com.getcapacitor.NativePlugin; -import com.getcapacitor.Plugin; -import com.getcapacitor.PluginCall; -import com.getcapacitor.PluginMethod; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; -import java.util.Timer; -import java.util.TimerTask; - -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import co.fitcom.fancycamera.CameraEventListenerUI; -import co.fitcom.fancycamera.EventType; -import co.fitcom.fancycamera.FancyCamera; -import co.fitcom.fancycamera.PhotoEvent; -import co.fitcom.fancycamera.VideoEvent; - -@NativePlugin( - requestCodes = { - VideoRecorder.REQUEST_CODE - } -) -public class VideoRecorder extends Plugin { - static final int REQUEST_CODE = 868; - private FancyCamera fancyCamera; - private PluginCall call; - private HashMap previewFrameConfigs; - private FrameConfig currentFrameConfig; - private FancyCamera.CameraPosition cameraPosition = FancyCamera.CameraPosition.FRONT; - private Timer audioFeedbackTimer; - private boolean timerStarted; - - PluginCall getCall() { - return call; - } - - @Override - protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.handleRequestPermissionsResult(requestCode, permissions, grantResults); - if (fancyCamera.hasPermission()) { - if (getCall() != null) { - getCall().success(); - } else if (savedLastCall != null) { - savedLastCall.success(); - } - startCamera(); - } else { - if (getCall() != null) { - getCall().reject(""); - } else if (savedLastCall != null) { - savedLastCall.reject(""); - } - } - } - - private void startCamera() { - if (fancyCamera == null || fancyCamera.cameraStarted()) return; - fancyCamera.start(); - } - - private void startTimer() { - if (timerStarted) { - return; - } - - if (audioFeedbackTimer != null) { - audioFeedbackTimer.cancel(); - audioFeedbackTimer = null; - } - - audioFeedbackTimer = new Timer(); - audioFeedbackTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - timerStarted = true; - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - JSObject object = new JSObject(); - double db = fancyCamera != null ? fancyCamera.getDB() : 0; - object.put("value", db); - //notifyListeners("onVolumeInput", object); - } - }); - } - }, 0, 100); - } - - private void stopTimer() { - if (audioFeedbackTimer != null) { - audioFeedbackTimer.cancel(); - audioFeedbackTimer = null; - } - timerStarted = false; - } - - @Override - public void load() { - super.load(); - } - - @PluginMethod() - public void initialize(final PluginCall call) { - JSObject defaultFrame = new JSObject(); - defaultFrame.put("id", "default"); - currentFrameConfig = new FrameConfig(defaultFrame); - previewFrameConfigs = new HashMap<>(); - - fancyCamera = new FancyCamera(this.getContext()); - fancyCamera.setDisableHEVC(true); - fancyCamera.setListener(new CameraEventListenerUI() { - public void onCameraOpenUI() { - if (getCall() != null) { - getCall().success(); - } - startTimer(); - updateCameraView(currentFrameConfig); - for (FrameConfig f : previewFrameConfigs.values()) { - updateCameraView(f); - } - } - - public void onCameraCloseUI() { - if (getCall() != null) { - getCall().success(); - } - stopTimer(); - } - - @Override - public void onPhotoEventUI(PhotoEvent event) { - - } - - @Override - public void onVideoEventUI(VideoEvent event) { - if (event.getType() == EventType.INFO && - event - .getMessage().contains(VideoEvent.EventInfo.RECORDING_FINISHED.toString())) { - if (getCall() != null) { - JSObject object = new JSObject(); - String path = FileUtils.getPortablePath(getContext(), bridge.getLocalUrl(), Uri.fromFile(event.getFile())); - object.put("videoUrl", path); - getCall().resolve(object); - } else { - if (event.getType() == co.fitcom.fancycamera.EventType.ERROR) { - getCall().reject(event.getMessage()); - } - } - - } else if (event.getType() == EventType.INFO && - event - .getMessage().contains(VideoEvent.EventInfo.RECORDING_STARTED.toString())) { - if (getCall() != null) { - getCall().success(); - } - - } - } - }); - final FrameLayout.LayoutParams cameraPreviewParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - ((CoordinatorLayout) bridge.getWebView().getParent()).addView(fancyCamera, cameraPreviewParams); - bridge.getWebView().bringToFront(); - bridge.getWebView().getParent().requestLayout(); - ((CoordinatorLayout) bridge.getWebView().getParent()).invalidate(); - } - }); - - - defaultFrame = new JSObject(); - defaultFrame.put("id", "default"); - JSArray defaultArray = new JSArray(); - defaultArray.put(defaultFrame); - JSArray array = call.getArray("previewFrames", defaultArray); - int size = array.length(); - for (int i = 0; i < size; i++) { - try { - JSONObject obj = (JSONObject) array.get(i); - FrameConfig config = new FrameConfig(JSObject.fromJSONObject(obj)); - previewFrameConfigs.put(config.id, config); - } catch (JSONException ignored) { - - } - } - - fancyCamera.setCameraPosition(1); - if (fancyCamera.hasPermission()) { - // Swapping these around since it is the other way for iOS and the plugin interface needs to stay consistent - if (call.getInt("camera") == 1) { - fancyCamera.setCameraPosition(0); - } else if (call.getInt("camera") == 0) { - fancyCamera.setCameraPosition(1); - } else { - fancyCamera.setCameraPosition(1); - } - - if (!fancyCamera.cameraStarted()) { - startCamera(); - } - } else { - fancyCamera.requestPermission(); - } - - this.call = call; - } - - @PluginMethod() - public void destroy(PluginCall call) { - makeOpaque(); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - ((ViewGroup) bridge.getWebView().getParent()).removeView(fancyCamera); - } - }); - fancyCamera.release(); - call.success(); - } - - private void makeOpaque() { - this.bridge.getWebView().setBackgroundColor(Color.WHITE); - } - - @PluginMethod() - public void showPreviewFrame(PluginCall call) { - int position = call.getInt("position"); - int quality = call.getInt("quality"); - fancyCamera.setCameraPosition(position); - fancyCamera.setQuality(quality); - bridge.getWebView().setBackgroundColor(Color.argb(0, 0, 0, 0)); - if (fancyCamera != null && !fancyCamera.cameraStarted()) { - startCamera(); - this.call = call; - } else { - call.success(); - } - } - - @PluginMethod() - public void hidePreviewFrame(PluginCall call) { - makeOpaque(); - fancyCamera.stop(); - this.call = call; - } - - @PluginMethod() - public void togglePip(PluginCall call) { - - } - - @PluginMethod() - public void startRecording(PluginCall call) { - this.call = call; - fancyCamera.startRecording(); - // call.success(); - } - - @PluginMethod() - public void stopRecording(PluginCall call) { - this.call = call; - fancyCamera.stopRecording(); - } - - @PluginMethod() - public void flipCamera(PluginCall call) { - fancyCamera.toggleCamera(); - call.success(); - } - - @PluginMethod() - public void getDuration(PluginCall call) { - JSObject object = new JSObject(); - object.put("value", fancyCamera.getDuration()); - call.resolve(object); - } - - @PluginMethod() - public void setPosition(PluginCall call) { - int position = call.getInt("position"); - fancyCamera.setCameraPosition(position); - } - - @PluginMethod() - public void setQuality(PluginCall call) { - int quality = call.getInt("quality"); - fancyCamera.setQuality(quality); - } - - @PluginMethod() - public void addPreviewFrameConfig(PluginCall call) { - if (fancyCamera != null && fancyCamera.cameraStarted()) { - String layerId = call.getString("id"); - if (layerId.isEmpty()) { - call.error("Must provide layer id"); - return; - } - - FrameConfig config = new FrameConfig(call.getData()); - - if (previewFrameConfigs.containsKey(layerId)) { - editPreviewFrameConfig(call); - return; - } else { - previewFrameConfigs.put(layerId, config); - } - call.success(); - } - } - - @PluginMethod() - public void editPreviewFrameConfig(PluginCall call) { - if (fancyCamera != null && fancyCamera.cameraStarted()) { - String layerId = call.getString("id"); - if (layerId.isEmpty()) { - call.error("Must provide layer id"); - return; - } - - FrameConfig updatedConfig = new FrameConfig(call.getData()); - previewFrameConfigs.put(layerId, updatedConfig); - - if (currentFrameConfig.id.equals(layerId)) { - currentFrameConfig = updatedConfig; - updateCameraView(currentFrameConfig); - } - - call.success(); - } - } - - - @PluginMethod() - public void switchToPreviewFrame(PluginCall call) { - if (fancyCamera != null && fancyCamera.cameraStarted()) { - String layerId = call.getString("id"); - if (layerId.isEmpty()) { - call.error("Must provide layer id"); - return; - } - FrameConfig existingConfig = previewFrameConfigs.get(layerId); - if (existingConfig != null) { - if (!existingConfig.id.equals(currentFrameConfig.id)) { - currentFrameConfig = existingConfig; - updateCameraView(currentFrameConfig); - } - - } else { - call.error("Frame config does not exist"); - return; - } - call.success(); - } - } - - private int getPixels(int value) { - return (int) (value * getContext().getResources().getDisplayMetrics().density + 0.5f); - } - - private void updateCameraView(final FrameConfig frameConfig) { - - DisplayMetrics displayMetrics = new DisplayMetrics(); - getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); - int deviceHeight = displayMetrics.heightPixels; - int deviceWidth = displayMetrics.widthPixels; - int width; - int height; - if (fancyCamera.getLayoutParams() == null) { - fancyCamera.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - } - - ViewGroup.LayoutParams oldParams = fancyCamera.getLayoutParams(); - - if (frameConfig.width == -1) { - width = deviceWidth; - } else { - width = getPixels(frameConfig.width); - } - - if (frameConfig.height == -1) { - height = deviceHeight; - } else { - height = getPixels(frameConfig.height); - } - - oldParams.width = width; - oldParams.height = height; - //fancyCamera.setY(frameConfig.y); - //fancyCamera.setX(frameConfig.x); - fancyCamera.setY(getPixels((int) frameConfig.y)); - fancyCamera.setX(getPixels((int) frameConfig.x)); - fancyCamera.setElevation(9); - bridge.getWebView().setElevation(9); - bridge.getWebView().setBackgroundColor(Color.argb(0, 0, 0, 0)); - if (frameConfig.stackPosition.equals("front")) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - fancyCamera.bringToFront(); - bridge.getWebView().getParent().requestLayout(); - ((CoordinatorLayout) bridge.getWebView().getParent()).invalidate(); - } - }); - - } else if (frameConfig.stackPosition.equals("back")) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (frameConfig.stackPosition.equals("back")) { - getBridge().getWebView().bringToFront(); - } - bridge.getWebView().getParent().requestLayout(); - ((CoordinatorLayout) bridge.getWebView().getParent()).invalidate(); - } - }); - } - - } - - - class FrameConfig { - String id; - String stackPosition; - float x; - float y; - int width; - int height; - float borderRadius; - DropShadow dropShadow; - - FrameConfig(JSObject object) { - id = object.getString("id"); - stackPosition = object.getString("stackPosition", "back"); - x = object.getInteger("x", 0).floatValue(); - y = object.getInteger("y", 0).floatValue(); - width = object.getInteger("width", -1); - height = object.getInteger("height", -1); - borderRadius = object.getInteger("borderRadius", 0); - JSObject ds = object.getJSObject("dropShadow"); - dropShadow = new DropShadow(ds != null ? ds : new JSObject()); - } - - class DropShadow { - float opacity; - float radius; - Color color; - - DropShadow(JSObject object) { - opacity = object.getInteger("opacity", 0).floatValue(); - radius = object.getInteger("radius", 0).floatValue(); - } - } - } -} diff --git a/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorderPlugin.java b/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorderPlugin.java index 4151705..a11a319 100644 --- a/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorderPlugin.java +++ b/android/src/main/java/com/capacitorcommunity/videorecorder/VideoRecorderPlugin.java @@ -32,11 +32,10 @@ @CapacitorPlugin( name = "VideoRecorder", requestCodes = { - VideoRecorder.REQUEST_CODE + 868 } ) public class VideoRecorderPlugin extends Plugin { - static final int REQUEST_CODE = 868; private FancyCamera fancyCamera; private PluginCall call; private HashMap previewFrameConfigs; @@ -44,6 +43,7 @@ public class VideoRecorderPlugin extends Plugin { private FancyCamera.CameraPosition cameraPosition = FancyCamera.CameraPosition.FRONT; private Timer audioFeedbackTimer; private boolean timerStarted; + private Integer videoBitrate = 4500000; PluginCall getCall() { return call; @@ -122,7 +122,12 @@ public void initialize(final PluginCall call) { currentFrameConfig = new FrameConfig(defaultFrame); previewFrameConfigs = new HashMap<>(); + if (call.getInt("videoBitrate") != null) { + videoBitrate = call.getInt("videoBitrate"); + } + fancyCamera = new FancyCamera(this.getContext()); + fancyCamera.setMaxVideoBitrate(videoBitrate); fancyCamera.setDisableHEVC(true); fancyCamera.setListener(new CameraEventListenerUI() { public void onCameraOpenUI() { @@ -174,7 +179,8 @@ public void onVideoEventUI(VideoEvent event) { } } }); - final FrameLayout.LayoutParams cameraPreviewParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); + final FrameLayout.LayoutParams cameraPreviewParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); + fancyCamera.setLayoutParams(cameraPreviewParams); getActivity().runOnUiThread(new Runnable() { @Override public void run() { @@ -269,9 +275,9 @@ public void togglePip(PluginCall call) { @PluginMethod() public void startRecording(PluginCall call) { - this.call = call; + fancyCamera.setAutoFocus(true); fancyCamera.startRecording(); - // call.success(); + call.success(); } @PluginMethod() @@ -381,32 +387,59 @@ private void updateCameraView(final FrameConfig frameConfig) { getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int deviceHeight = displayMetrics.heightPixels; int deviceWidth = displayMetrics.widthPixels; - int width; - int height; + boolean isLandscape = deviceWidth > deviceHeight; + if (fancyCamera.getLayoutParams() == null) { fancyCamera.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } - ViewGroup.LayoutParams oldParams = fancyCamera.getLayoutParams(); - - if (frameConfig.width == -1) { + // Calculate the aspect ratio dimensions + int width, height; + if (isLandscape) { width = deviceWidth; + height = (int) (deviceWidth * 9.0 / 16.0); } else { - width = getPixels(frameConfig.width); + width = deviceWidth; + height = (int) (deviceWidth * 4.0 / 3.0); } - if (frameConfig.height == -1) { + // If the calculated height is greater than the device height, adjust the width and height + if (height > deviceHeight) { height = deviceHeight; - } else { + width = isLandscape ? (int) (deviceHeight * 16.0 / 9.0) : (int) (deviceHeight * 3.0 / 4.0); + } + + ViewGroup.LayoutParams oldParams = fancyCamera.getLayoutParams(); + + if (frameConfig.width != -1) { + width = getPixels(frameConfig.width); + } + + if (frameConfig.height != -1) { height = getPixels(frameConfig.height); } oldParams.width = width; oldParams.height = height; - //fancyCamera.setY(frameConfig.y); - //fancyCamera.setX(frameConfig.x); - fancyCamera.setY(getPixels((int) frameConfig.y)); - fancyCamera.setX(getPixels((int) frameConfig.x)); + fancyCamera.setLayoutParams(oldParams); + + // Center the preview frame vertically if y is 0 and height and width are -1 + if (frameConfig.y == 0 && frameConfig.height == -1 && frameConfig.width == -1) { + fancyCamera.setY((deviceHeight - height) / 2); + } else { + fancyCamera.setY(getPixels((int) frameConfig.y)); + } + + // Center the preview frame horizontally if x is 0 and height and width are -1 + if (isLandscape && frameConfig.x == 0 && frameConfig.height == -1 && frameConfig.width == -1) { + fancyCamera.setX((deviceWidth - width) / 2); + } else { + fancyCamera.setX(getPixels((int) frameConfig.x)); + } + + // Set the background color to black + ((ViewGroup) fancyCamera.getParent()).setBackgroundColor(Color.BLACK); + fancyCamera.setElevation(9); bridge.getWebView().setElevation(9); bridge.getWebView().setBackgroundColor(Color.argb(0, 0, 0, 0)); diff --git a/example/package-lock.json b/example/package-lock.json index 35e7951..05a6199 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -60,7 +60,7 @@ } }, "..": { - "version": "5.0.1", + "version": "5.0.2", "license": "MIT", "devDependencies": { "@capacitor/android": "^5.0.0", diff --git a/example/src/app/home/home.page.ts b/example/src/app/home/home.page.ts index bfb11b2..9876a51 100644 --- a/example/src/app/home/home.page.ts +++ b/example/src/app/home/home.page.ts @@ -40,7 +40,7 @@ export class HomePage implements OnInit, OnDestroy { showVideos = false; durationIntervalId!: any; duration = "00:00"; - quality = VideoRecorderQuality.HIGHEST; + quality = VideoRecorderQuality.MAX_720P; videoQualityMap = [ { command: VideoRecorderQuality.HIGHEST, label: 'Highest' }, @@ -112,7 +112,8 @@ export class HomePage implements OnInit, OnDestroy { await VideoRecorder.initialize({ camera: VideoRecorderCamera.BACK, previewFrames: [config], - quality: this.quality + quality: this.quality, + videoBitrate: 4500000, }); if (this.platform.is('android')) { @@ -127,30 +128,34 @@ export class HomePage implements OnInit, OnDestroy { } async startRecording() { - await this.initialise(); - await VideoRecorder.startRecording(); - this.isRecording = true; - this.showVideos = false; - - // lock screen orientation when recording - const orientation = await ScreenOrientation.orientation(); - - if (this.platform.is('ios')) { - // On iOS the landscape-primary and landscape-secondary are flipped for some reason - if (orientation.type === 'landscape-primary') { - orientation.type = 'landscape-secondary' - } else if (orientation.type === 'landscape-secondary') { - orientation.type = 'landscape-primary' + try { + await this.initialise(); + await VideoRecorder.startRecording(); + this.isRecording = true; + this.showVideos = false; + + // lock screen orientation when recording + const orientation = await ScreenOrientation.orientation(); + + if (this.platform.is('ios')) { + // On iOS the landscape-primary and landscape-secondary are flipped for some reason + if (orientation.type === 'landscape-primary') { + orientation.type = 'landscape-secondary' + } else if (orientation.type === 'landscape-secondary') { + orientation.type = 'landscape-primary' + } } - } - await ScreenOrientation.lock({ orientation: orientation.type }); + await ScreenOrientation.lock({ orientation: orientation.type }); - this.durationIntervalId = setInterval(() => { - VideoRecorder.getDuration().then((res) => { - this.duration = this.numberToTimeString(res.value); - }); - }, 1000); + this.durationIntervalId = setInterval(() => { + VideoRecorder.getDuration().then((res) => { + this.duration = this.numberToTimeString(res.value); + }); + }, 1000); + } catch (error) { + console.error(error); + } } flipCamera() { diff --git a/ios/Plugin/Plugin.swift b/ios/Plugin/Plugin.swift index 3829354..0e6566e 100644 --- a/ios/Plugin/Plugin.swift +++ b/ios/Plugin/Plugin.swift @@ -8,8 +8,8 @@ extension UIColor { if (cString.hasPrefix("#")) { cString.remove(at: cString.startIndex) } - var rgbValue:UInt32 = 0 - Scanner(string: cString).scanHexInt32(&rgbValue) + var rgbValue:UInt64 = 0 + Scanner(string: cString).scanHexInt64(&rgbValue) self.init( red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, @@ -77,7 +77,9 @@ class CameraView: UIView { layer.frame = self.bounds } } - self.videoPreviewLayer?.connection?.videoOrientation = interfaceOrientationToVideoOrientation(UIApplication.shared.statusBarOrientation); + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + self.videoPreviewLayer?.connection?.videoOrientation = interfaceOrientationToVideoOrientation(windowScene.interfaceOrientation) + } } func addPreviewLayer(_ previewLayer:AVCaptureVideoPreviewLayer?) { @@ -152,7 +154,7 @@ public func joinPath(left: String, right: String) -> String { } public func randomFileName() -> String { - return NSUUID().uuidString + return UUID().uuidString } @objc(VideoRecorder) @@ -175,6 +177,7 @@ public class VideoRecorder: CAPPlugin, AVCaptureFileOutputRecordingDelegate { var frontCamera: AVCaptureDevice? var backCamera: AVCaptureDevice? var quality: Int = 0 + var videoBitrate: Int = 4500000 var stopRecordingCall: CAPPluginCall? @@ -217,6 +220,7 @@ public class VideoRecorder: CAPPlugin, AVCaptureFileOutputRecordingDelegate { if (self.captureSession?.isRunning != true) { self.currentCamera = call.getInt("camera", 0) self.quality = call.getInt("quality", 0) + self.videoBitrate = call.getInt("videoBitrate", 4500000) let autoShow = call.getBool("autoShow", true) for frameConfig in call.getArray("previewFrames", [ ["id": "default"] ]) { @@ -228,7 +232,9 @@ public class VideoRecorder: CAPPlugin, AVCaptureFileOutputRecordingDelegate { DispatchQueue.main.async { do { // Set webview to transparent and set the app window background to white - UIApplication.shared.delegate?.window?!.backgroundColor = UIColor.white + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + windowScene.windows.first?.backgroundColor = UIColor.white + } self.capWebView?.isOpaque = false self.capWebView?.backgroundColor = UIColor.clear @@ -580,13 +586,27 @@ public class VideoRecorder: CAPPlugin, AVCaptureFileOutputRecordingDelegate { @objc func startRecording(_ call: CAPPluginCall) { if (self.captureSession != nil) { if (!(videoOutput?.isRecording)!) { - let tempDir = NSURL.fileURL(withPath:NSTemporaryDirectory(),isDirectory:true) + let tempDir = NSURL.fileURL(withPath:NSTemporaryDirectory(), isDirectory: true) var fileName = randomFileName() fileName.append(".mp4") - let fileUrl = NSURL.fileURL(withPath: joinPath(left:tempDir.path,right: fileName)); + let fileUrl = NSURL.fileURL(withPath: joinPath(left: tempDir.path, right: fileName)) + + // Configure video output settings + let videoSettings: [String: Any] = [ + AVVideoCodecKey: AVVideoCodecType.h264, + AVVideoCompressionPropertiesKey: [ + AVVideoAverageBitRateKey: self.videoBitrate + ] + ] + + if let connection = self.videoOutput?.connection(with: .video) { + self.videoOutput?.setOutputSettings(videoSettings, for: connection) + } DispatchQueue.main.async { - self.videoOutput?.connection(with: .video)?.videoOrientation = self.cameraView.interfaceOrientationToVideoOrientation(UIApplication.shared.statusBarOrientation) + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + self.videoOutput?.connection(with: .video)?.videoOrientation = self.cameraView.interfaceOrientationToVideoOrientation(windowScene.interfaceOrientation) + } self.videoOutput?.startRecording(to: fileUrl, recordingDelegate: self) call.resolve() } diff --git a/package-lock.json b/package-lock.json index 63300b8..6c2869b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@capacitor-community/video-recorder", - "version": "6.0.0", + "version": "6.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@capacitor-community/video-recorder", - "version": "6.0.0", + "version": "6.1.0", "license": "MIT", "devDependencies": { "@capacitor/android": "^6.0.0", diff --git a/package.json b/package.json index 454df76..e55f319 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@capacitor-community/video-recorder", - "version": "6.0.0", + "version": "6.1.0", "private": false, "description": "capacitor plugin to record video", "main": "dist/plugin.cjs.js", diff --git a/src/definitions.ts b/src/definitions.ts index f714d55..bfe0cad 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -47,6 +47,13 @@ export interface VideoRecorderOptions { quality?: VideoRecorderQuality; autoShow?: boolean; previewFrames?: VideoRecorderPreviewFrame[]; + /** + * The default bitrate is 4.5Mbps + * @default 4500000 + * @type {number} + * @memberof VideoRecorderOptions + */ + videoBitrate?: number; } export enum VideoRecorderCamera {