Various fixes

This commit is contained in:
Ilya Laktyushin 2026-04-29 15:49:21 +02:00
parent d25010c1cb
commit 37d1e2d93e
7 changed files with 69 additions and 33 deletions

View file

@ -278,7 +278,6 @@ private final class CameraContext {
self._positionPromise.set(targetPosition)
self.modeChange = .position
let preferWide = self.initialConfiguration.preferWide || isRoundVideo
let preferLowerFramerate = self.initialConfiguration.preferLowerFramerate || isRoundVideo
@ -567,6 +566,11 @@ private final class CameraContext {
return .finished(mainImage, additionalImage, CACurrentMediaTime())
}
} else {
if case .failed = main {
return .failed
} else if case .failed = additional {
return .failed
}
return .began
}
} |> distinctUntilChanged
@ -585,6 +589,10 @@ private final class CameraContext {
mainDeviceContext.device.setTorchMode(self._flashMode)
}
let timestamp = CACurrentMediaTime() + 2.0
self.lastSnapshotTimestamp = timestamp
self.lastAdditionalSnapshotTimestamp = timestamp
let orientation = self.simplePreviewView?.videoPreviewLayer.connection?.videoOrientation ?? .portrait
if self.initialConfiguration.isRoundVideo {
return mainDeviceContext.output.startRecording(mode: .roundVideo, orientation: DeviceModel.current.isIpad ? orientation : .portrait, additionalOutput: self.additionalDeviceContext?.output)
@ -790,6 +798,11 @@ public final class Camera {
secondaryPreviewView.setSession(session.session, autoConnect: false)
}
if #available(iOS 14.5, *), configuration.isRoundVideo {
AVCaptureDevice.centerStageControlMode = .app
AVCaptureDevice.isCenterStageEnabled = false
}
self.queue.async {
let context = CameraContext(queue: self.queue, session: session, configuration: configuration, metrics: self.metrics, previewView: previewView, secondaryPreviewView: secondaryPreviewView)
self.contextRef = Unmanaged.passRetained(context)
@ -803,6 +816,10 @@ public final class Camera {
self.queue.async {
contextRef?.release()
}
if #available(iOS 14.5, *) {
AVCaptureDevice.centerStageControlMode = .user
}
}
public func startCapture() {

View file

@ -156,7 +156,7 @@ final class CameraDevice {
if device.isLowLightBoostSupported {
device.automaticallyEnablesLowLightBoostWhenAvailable = true
}
if device.isExposureModeSupported(.continuousAutoExposure) {
device.exposureMode = .continuousAutoExposure
}
@ -185,7 +185,7 @@ final class CameraDevice {
private(set) var fps: Double = defaultFPS
func setFps(_ fps: Double) {
guard let device = self.videoDevice, let targetFPS = device.actualFPS(Double(self.fps)) else {
guard let device = self.videoDevice, let targetFPS = device.actualFPS(Double(fps)) else {
return
}
self.fps = targetFPS.fps
@ -305,7 +305,8 @@ final class CameraDevice {
return
}
self.transaction(device) { device in
device.videoZoomFactor = max(device.neutralZoomFactor, min(10.0, device.neutralZoomFactor + zoomLevel))
let target = device.neutralZoomFactor + zoomLevel
device.videoZoomFactor = self.clampedZoomFactor(target, for: device)
}
}
@ -314,7 +315,8 @@ final class CameraDevice {
return
}
self.transaction(device) { device in
device.videoZoomFactor = max(1.0, min(10.0, device.videoZoomFactor * zoomDelta))
let target = device.videoZoomFactor * zoomDelta
device.videoZoomFactor = self.clampedZoomFactor(target, for: device)
}
}
@ -323,7 +325,8 @@ final class CameraDevice {
return
}
self.transaction(device) { device in
device.ramp(toVideoZoomFactor: zoomLevel, withRate: Float(rate))
let target = self.clampedZoomFactor(zoomLevel, for: device)
device.ramp(toVideoZoomFactor: target, withRate: Float(rate))
}
}
@ -332,7 +335,14 @@ final class CameraDevice {
return
}
self.transaction(device) { device in
device.videoZoomFactor = neutral ? device.neutralZoomFactor : device.minAvailableVideoZoomFactor
let target = neutral ? device.neutralZoomFactor : device.minAvailableVideoZoomFactor
device.videoZoomFactor = self.clampedZoomFactor(target, for: device)
}
}
private func clampedZoomFactor(_ value: CGFloat, for device: AVCaptureDevice) -> CGFloat {
let minimum = max(1.0, device.minAvailableVideoZoomFactor)
let maximum = max(minimum, device.maxAvailableVideoZoomFactor)
return min(maximum, max(minimum, value))
}
}

View file

@ -15,11 +15,14 @@ class CameraInput {
}
func invalidate(for session: CameraSession, switchAudio: Bool = true) {
for input in session.session.inputs {
if !switchAudio && input === self.audioInput {
continue
}
session.session.removeInput(input)
if let videoInput = self.videoInput, session.session.inputs.contains(where: { $0 === videoInput }) {
session.session.removeInput(videoInput)
}
self.videoInput = nil
if switchAudio, let audioInput = self.audioInput, session.session.inputs.contains(where: { $0 === audioInput }) {
session.session.removeInput(audioInput)
self.audioInput = nil
}
}

View file

@ -192,9 +192,9 @@ final class CameraOutput: NSObject {
}
if #available(iOS 13.0, *), session.hasMultiCam {
if let device = device.videoDevice, let ports = input.videoInput?.ports(for: AVMediaType.video, sourceDeviceType: device.deviceType, sourceDevicePosition: device.position) {
if let device = device.videoDevice, let ports = input.videoInput?.ports(for: AVMediaType.video, sourceDeviceType: device.deviceType, sourceDevicePosition: device.position), let firstPort = ports.first {
if let previewView {
let previewConnection = AVCaptureConnection(inputPort: ports.first!, videoPreviewLayer: previewView.videoPreviewLayer)
let previewConnection = AVCaptureConnection(inputPort: firstPort, videoPreviewLayer: previewView.videoPreviewLayer)
if session.session.canAddConnection(previewConnection) {
session.session.addConnection(previewConnection)
self.previewConnection = previewConnection
@ -447,7 +447,7 @@ final class CameraOutput: NSObject {
}
return Signal { subscriber in
let timer = SwiftSignalKit.Timer(timeout: 0.1, repeat: true, completion: { [weak videoRecorder] in
let timer = SwiftSignalKit.Timer(timeout: 0.09, repeat: true, completion: { [weak videoRecorder] in
let recordingData = CameraRecordingData(duration: videoRecorder?.duration ?? 0.0, filePath: outputFilePath)
subscriber.putNext(recordingData)
}, queue: Queue.mainQueue())
@ -473,11 +473,7 @@ final class CameraOutput: NSObject {
}
var transitionImage: UIImage? {
var result: UIImage?
self.videoQueue.sync {
result = self.videoRecorder?.transitionImage
}
return result
return self.videoRecorder?.transitionImage
}
private weak var masterOutput: CameraOutput?
@ -705,7 +701,7 @@ extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureA
func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if #available(iOS 13.0, *) {
Logger.shared.log("VideoRecorder", "Dropped sample buffer \(sampleBuffer.attachments)")
Logger.shared.log("Camera", "Dropped sample buffer \(sampleBuffer.attachments)")
}
}
}

View file

@ -232,7 +232,9 @@ private final class VideoRecorderImpl {
} else if self.orientation == .portraitUpsideDown {
orientation = .left
}
self.transitionImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: orientation)
Queue.mainQueue().async {
self.transitionImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: orientation)
}
} else {
self.savedTransitionImage = false
}
@ -375,21 +377,21 @@ private final class VideoRecorderImpl {
dispatchPrecondition(condition: .onQueue(self.queue))
let completion = self.completion
if self.recordingStopSampleTime == .invalid {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(false, nil, nil)
}
return
}
if let _ = self.error.with({ $0 }) {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(false, nil, nil)
}
return
}
if !self.tryAppendingPendingAudioBuffers() {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(false, nil, nil)
}
return
@ -398,21 +400,21 @@ private final class VideoRecorderImpl {
if self.assetWriter.status == .writing {
self.assetWriter.finishWriting {
if let _ = self.assetWriter.error {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(false, nil, nil)
}
} else {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(true, self.transitionImage, self.positionChangeTimestamps)
}
}
}
} else if let _ = self.assetWriter.error {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(false, nil, nil)
}
} else {
DispatchQueue.main.async {
Queue.mainQueue().async {
completion(false, nil, nil)
}
}

View file

@ -2751,6 +2751,8 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
return CGRectMake(0, 82, screenSize.width, screenSize.height - 82 - 83);
else if (widescreenWidth == 926.0f)
return CGRectMake(0, 82, screenSize.width, screenSize.height - 82 - 83);
else if (widescreenWidth == 912.0f)
return CGRectMake(0, 82, screenSize.width, screenSize.height - 82 - 83);
else if (widescreenWidth == 896.0f)
return CGRectMake(0, 77, screenSize.width, screenSize.height - 77 - 83);
else if (widescreenWidth == 874.0f)
@ -2784,6 +2786,8 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
return CGRectMake(0, 136, screenSize.width, screenSize.height - 136 - 223);
else if (widescreenWidth == 926.0f)
return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 234);
else if (widescreenWidth == 912.0f)
return CGRectMake(0, 136, screenSize.width, screenSize.height - 136 - 216);
else if (widescreenWidth == 896.0f)
return CGRectMake(0, 121, screenSize.width, screenSize.height - 121 - 223);
else if (widescreenWidth == 874.0f)

View file

@ -575,14 +575,18 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
if !component.isPreviewing {
if case .on = component.cameraState.flashMode, case .front = component.cameraState.position {
let frontFlashPosition = CGPoint(x: component.previewFrame.midX, y: component.previewFrame.midY)
let frontFlashSize = CGSize(
width: max(abs(frontFlashPosition.x), abs(context.availableSize.width - frontFlashPosition.x)) * 2.0,
height: max(abs(frontFlashPosition.y), abs(context.availableSize.height - frontFlashPosition.y)) * 2.0
)
let frontFlash = frontFlash.update(
component: Image(image: state.image(.flashImage, theme: environment.theme), tintColor: component.cameraState.flashTint.color),
availableSize: context.availableSize,
component: Image(image: state.image(.flashImage, theme: environment.theme), tintColor: component.cameraState.flashTint.color, contentMode: .scaleAspectFill),
availableSize: frontFlashSize,
transition: .easeInOut(duration: 0.2)
)
let frontFlashFrame = CGRect(origin: CGPoint(), size: context.availableSize)
context.add(frontFlash
.position(frontFlashFrame.center)
.position(frontFlashPosition)
.scale(1.5 - component.cameraState.flashTintSize * 0.5)
.appear(.default(alpha: true))
.disappear(ComponentTransition.Disappear({ view, transition, completion in