feat: Support setting videoStabilizationMode (#2160)

* feat: Support setting `videoStabilizationMode`

* fix: Use `outputs`

* Format

* Set Video Stabilization Mode
This commit is contained in:
Marc Rousavy 2023-11-15 17:00:41 +01:00 committed by GitHub
parent e8ebc6ee9f
commit abf5538bb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 53 additions and 100 deletions

View File

@ -185,6 +185,14 @@ public final class CameraView: UIView, CameraSessionDelegate {
config.codeScanner = .disabled
}
// Video Stabilization
if let jsVideoStabilizationMode = videoStabilizationMode as? String {
let videoStabilizationMode = try VideoStabilizationMode(jsValue: jsVideoStabilizationMode)
config.videoStabilizationMode = videoStabilizationMode
} else {
config.videoStabilizationMode = .off
}
// Orientation
if let jsOrientation = orientation as? String {
let orientation = try Orientation(jsValue: jsOrientation)

View File

@ -22,6 +22,9 @@ class CameraConfiguration {
var video: OutputConfiguration<Video> = .disabled
var codeScanner: OutputConfiguration<CodeScanner> = .disabled
// Video Stabilization
var videoStabilizationMode: VideoStabilizationMode = .off
// Orientation
var orientation: Orientation = .portrait
@ -49,6 +52,7 @@ class CameraConfiguration {
photo = other.photo
video = other.video
codeScanner = other.codeScanner
videoStabilizationMode = other.videoStabilizationMode
orientation = other.orientation
format = other.format
fps = other.fps
@ -67,6 +71,7 @@ class CameraConfiguration {
struct Difference {
let inputChanged: Bool
let outputsChanged: Bool
let videoStabilizationChanged: Bool
let orientationChanged: Bool
let formatChanged: Bool
let sidePropsChanged: Bool
@ -80,7 +85,7 @@ class CameraConfiguration {
[`inputChanged`, `outputsChanged`, `orientationChanged`]
*/
var isSessionConfigurationDirty: Bool {
return inputChanged || outputsChanged || orientationChanged
return inputChanged || outputsChanged || videoStabilizationChanged || orientationChanged
}
/**
@ -96,6 +101,8 @@ class CameraConfiguration {
inputChanged = left?.cameraId != right.cameraId
// photo, video, codeScanner
outputsChanged = inputChanged || left?.photo != right.photo || left?.video != right.video || left?.codeScanner != right.codeScanner
// videoStabilizationMode
videoStabilizationChanged = outputsChanged || left?.videoStabilizationMode != right.videoStabilizationMode
// orientation
orientationChanged = outputsChanged || left?.orientation != right.orientation
// format (depends on cameraId)

View File

@ -154,6 +154,17 @@ extension CameraSession {
ReactLogger.log(level: .info, message: "Successfully configured all outputs!")
}
// pragma MARK: Video Stabilization
func configureVideoStabilization(configuration: CameraConfiguration) {
captureSession.outputs.forEach { output in
output.connections.forEach { connection in
if connection.isVideoStabilizationSupported {
connection.preferredVideoStabilizationMode = configuration.videoStabilizationMode.toAVCaptureVideoStabilizationMode()
}
}
}
}
// pragma MARK: Orientation
func configureOrientation(configuration: CameraConfiguration) {

View File

@ -123,7 +123,11 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
if difference.outputsChanged {
try self.configureOutputs(configuration: config)
}
// 3. Update output orientation
// 3. Update Video Stabilization
if difference.videoStabilizationChanged {
try self.configureVideoStabilization(configuration: config)
}
// 4. Update output orientation
if difference.orientationChanged {
self.configureOrientation(configuration: config)
}

View File

@ -39,6 +39,8 @@ extension AVCaptureOutput {
let cameraOrientation = orientation.rotateBy(orientation: .landscapeLeft)
let degrees = cameraOrientation.toDegrees()
// TODO: Don't rotate the video output because it adds overhead. Instead just use EXIF flags for the .mp4 file if recording.
// Does that work when we flip the camera?
if connection.isVideoRotationAngleSupported(degrees) {
connection.videoRotationAngle = degrees
}

View File

@ -1,28 +0,0 @@
//
// AVCaptureSession+setVideoStabilizationMode.swift
// VisionCamera
//
// Created by Marc Rousavy on 02.06.21.
// Copyright © 2021 Facebook. All rights reserved.
//
import AVFoundation
import Foundation
extension AVCaptureSession {
/**
Set the given video stabilization mode for all capture connections.
*/
func setVideoStabilizationMode(_ mode: String) {
if #available(iOS 13.0, *) {
guard let mode = try? AVCaptureVideoStabilizationMode(withString: mode) else {
return
}
connections.forEach { connection in
if connection.isVideoStabilizationSupported {
connection.preferredVideoStabilizationMode = mode
}
}
}
}
}

View File

@ -1,62 +0,0 @@
//
// AVCaptureVideoStabilizationMode+descriptor.swift
// mrousavy
//
// Created by Marc Rousavy on 29.12.20.
// Copyright © 2020 mrousavy. All rights reserved.
//
import AVFoundation
extension AVCaptureVideoStabilizationMode {
init(withString string: String) throws {
switch string {
case "auto":
self = .auto
return
case "cinematic":
self = .cinematic
return
case "cinematic-extended":
if #available(iOS 13.0, *) {
self = .cinematicExtended
return
} else {
self = .cinematic
return
}
case "off":
self = .off
return
case "standard":
self = .standard
return
default:
throw EnumParserError.invalidValue
}
}
var descriptor: String {
if #available(iOS 13.0, *) {
switch self {
case .cinematicExtended:
return "cinematic-extended"
default:
break
}
}
switch self {
case .auto:
return "auto"
case .cinematic:
return "cinematic"
case .off:
return "off"
case .standard:
return "standard"
default:
fatalError("AVCaptureVideoStabilizationMode has unknown state.")
}
}
}

View File

@ -41,6 +41,25 @@ enum VideoStabilizationMode: String, JSUnionValue {
}
}
func toAVCaptureVideoStabilizationMode() -> AVCaptureVideoStabilizationMode {
switch self {
case .off:
return .off
case .standard:
return .standard
case .cinematic:
return .cinematic
case .cinematicExtended:
if #available(iOS 13.0, *) {
return .cinematicExtended
} else {
return .cinematic
}
case .auto:
return .auto
}
}
var jsValue: String {
return rawValue
}

View File

@ -50,7 +50,6 @@
B887519625E0102000DB86D6 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517025E0102000DB86D6 /* Promise.swift */; };
B887519725E0102000DB86D6 /* CameraView+TakePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517125E0102000DB86D6 /* CameraView+TakePhoto.swift */; };
B887519825E0102000DB86D6 /* EnumParserError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517325E0102000DB86D6 /* EnumParserError.swift */; };
B887519925E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517425E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift */; };
B887519A25E0102000DB86D6 /* AVVideoCodecType+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517525E0102000DB86D6 /* AVVideoCodecType+descriptor.swift */; };
B887519C25E0102000DB86D6 /* AVCaptureDevice.TorchMode+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517725E0102000DB86D6 /* AVCaptureDevice.TorchMode+descriptor.swift */; };
B887519E25E0102000DB86D6 /* AVCapturePhotoOutput.QualityPrioritization+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887517925E0102000DB86D6 /* AVCapturePhotoOutput.QualityPrioritization+descriptor.swift */; };
@ -63,7 +62,6 @@
B88751A725E0102000DB86D6 /* CameraView+Zoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518225E0102000DB86D6 /* CameraView+Zoom.swift */; };
B88751A825E0102000DB86D6 /* CameraError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518325E0102000DB86D6 /* CameraError.swift */; };
B88751A925E0102000DB86D6 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B887518425E0102000DB86D6 /* CameraView.swift */; };
B88B47472667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88B47462667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift */; };
B8994E6C263F03E100069589 /* JSINSObjectConversion.mm in Sources */ = {isa = PBXBuildFile; fileRef = B8994E6B263F03E100069589 /* JSINSObjectConversion.mm */; };
B8A1AEC42AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */; };
B8A1AEC62AD7F08E00169C0D /* CameraView+Focus.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A1AEC52AD7F08E00169C0D /* CameraView+Focus.swift */; };
@ -144,7 +142,6 @@
B887517025E0102000DB86D6 /* Promise.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = "<group>"; };
B887517125E0102000DB86D6 /* CameraView+TakePhoto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CameraView+TakePhoto.swift"; sourceTree = "<group>"; };
B887517325E0102000DB86D6 /* EnumParserError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumParserError.swift; sourceTree = "<group>"; };
B887517425E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureVideoStabilizationMode+descriptor.swift"; sourceTree = "<group>"; };
B887517525E0102000DB86D6 /* AVVideoCodecType+descriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVVideoCodecType+descriptor.swift"; sourceTree = "<group>"; };
B887517725E0102000DB86D6 /* AVCaptureDevice.TorchMode+descriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureDevice.TorchMode+descriptor.swift"; sourceTree = "<group>"; };
B887517925E0102000DB86D6 /* AVCapturePhotoOutput.QualityPrioritization+descriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCapturePhotoOutput.QualityPrioritization+descriptor.swift"; sourceTree = "<group>"; };
@ -158,7 +155,6 @@
B887518325E0102000DB86D6 /* CameraError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraError.swift; sourceTree = "<group>"; };
B887518425E0102000DB86D6 /* CameraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = "<group>"; };
B88873E5263D46C7008B1D0E /* FrameProcessorPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameProcessorPlugin.h; sourceTree = "<group>"; };
B88B47462667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureSession+setVideoStabilizationMode.swift"; sourceTree = "<group>"; };
B8994E6B263F03E100069589 /* JSINSObjectConversion.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSINSObjectConversion.mm; sourceTree = "<group>"; };
B8A1AEC32AD7EDE800169C0D /* AVCaptureVideoDataOutput+pixelFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVCaptureVideoDataOutput+pixelFormat.swift"; sourceTree = "<group>"; };
B8A1AEC52AD7F08E00169C0D /* CameraView+Focus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Focus.swift"; sourceTree = "<group>"; };
@ -275,7 +271,6 @@
B887516825E0102000DB86D6 /* AVCaptureOutput+mirror.swift */,
B881D35D2ABC775E009B21C8 /* AVCaptureDevice+toDictionary.swift */,
B887516A25E0102000DB86D6 /* AVCaptureDevice.Format+toDictionary.swift */,
B88B47462667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift */,
B887516225E0102000DB86D6 /* Collection+safe.swift */,
B881D35F2ABC8E4E009B21C8 /* AVCaptureVideoDataOutput+findPixelFormat.swift */,
B8F127CF2ACF054A00B39EA3 /* CMVideoDimensions+toCGSize.swift */,
@ -300,7 +295,6 @@
children = (
B887517325E0102000DB86D6 /* EnumParserError.swift */,
B8DB3BC7263DC28C004C18D7 /* AVAssetWriter.Status+descriptor.swift */,
B887517425E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift */,
B887517525E0102000DB86D6 /* AVVideoCodecType+descriptor.swift */,
B887517725E0102000DB86D6 /* AVCaptureDevice.TorchMode+descriptor.swift */,
B887517925E0102000DB86D6 /* AVCapturePhotoOutput.QualityPrioritization+descriptor.swift */,
@ -457,7 +451,6 @@
B88685E72AD698DF00E93869 /* CameraConfiguration.swift in Sources */,
B887518725E0102000DB86D6 /* CameraViewManager.m in Sources */,
B88751A925E0102000DB86D6 /* CameraView.swift in Sources */,
B887519925E0102000DB86D6 /* AVCaptureVideoStabilizationMode+descriptor.swift in Sources */,
B80E06A0266632F000728644 /* AVAudioSession+updateCategory.swift in Sources */,
B887519425E0102000DB86D6 /* MakeReactError.swift in Sources */,
B887519525E0102000DB86D6 /* ReactLogger.swift in Sources */,
@ -504,7 +497,6 @@
B8F127D02ACF054A00B39EA3 /* CMVideoDimensions+toCGSize.swift in Sources */,
B8994E6C263F03E100069589 /* JSINSObjectConversion.mm in Sources */,
B8A1AEC62AD7F08E00169C0D /* CameraView+Focus.swift in Sources */,
B88B47472667C8E00091F538 /* AVCaptureSession+setVideoStabilizationMode.swift in Sources */,
B88103E32AD7065C0087F063 /* CameraSessionDelegate.swift in Sources */,
B887519E25E0102000DB86D6 /* AVCapturePhotoOutput.QualityPrioritization+descriptor.swift in Sources */,
);