import Foundation import UIKit import SwiftSignalKit import Camera import MediaEditor public final class EntityVideoRecorder { private weak var mediaEditor: MediaEditor? private weak var entitiesView: DrawingEntitiesView? private let maxDuration: Double private let camera: Camera private let previewView: CameraSimplePreviewView private let entity: DrawingStickerEntity private var recordingDisposable = MetaDisposable() private let durationPromise = ValuePromise() private let micLevelPromise = Promise() public var duration: Signal { return self.durationPromise.get() } public var micLevel: Signal { return self.micLevelPromise.get() } public var onAutomaticStop: () -> Void = {} public init(mediaEditor: MediaEditor, entitiesView: DrawingEntitiesView) { self.mediaEditor = mediaEditor self.entitiesView = entitiesView self.maxDuration = mediaEditor.duration ?? 60.0 self.previewView = CameraSimplePreviewView(frame: .zero, main: true) self.entity = DrawingStickerEntity(content: .dualVideoReference) self.camera = Camera( configuration: Camera.Configuration( preset: .hd1920x1080, position: .front, isDualEnabled: false, audio: true, photo: false, metadata: false, preferredFps: 60.0, preferWide: true ), previewView: self.previewView, secondaryPreviewView: nil ) self.camera.startCapture() let action = { [weak self] in self?.previewView.removePlaceholder(delay: 0.15) Queue.mainQueue().after(0.1) { self?.startRecording() self?.mediaEditor?.play() } } if #available(iOS 13.0, *) { let _ = (self.previewView.isPreviewing |> filter { $0 } |> take(1) |> deliverOnMainQueue).startStandalone(next: { _ in action() }) } else { Queue.mainQueue().after(0.35) { action() } } self.micLevelPromise.set(.single(0.0)) self.mediaEditor?.stop() self.mediaEditor?.seek(0.0, andPlay: false) } deinit { self.recordingDisposable.dispose() } public func setup( referenceDrawingSize: CGSize, scale: CGFloat, position: CGPoint ) { self.entity.referenceDrawingSize = referenceDrawingSize self.entity.scale = scale self.entity.position = position self.entitiesView?.add(self.entity) if let entityView = self.entitiesView?.getView(for: self.entity.uuid) as? DrawingStickerEntityView { let maxDuration = self.maxDuration entityView.setupCameraPreviewView( self.previewView, progress: self.durationPromise.get() |> map { Float(max(0.0, min(1.0, $0 / maxDuration))) } ) self.previewView.resetPlaceholder(front: true) entityView.animateInsertion() } } var start: Double = 0.0 private func startRecording() { self.start = CACurrentMediaTime() self.recordingDisposable.set((self.camera.startRecording() |> deliverOnMainQueue).startStrict(next: { [weak self] duration in guard let self else { return } self.durationPromise.set(duration) if duration >= self.maxDuration { let onAutomaticStop = self.onAutomaticStop self.stopRecording(save: true, completion: { onAutomaticStop() }) } })) } public func stopRecording(save: Bool, completion: @escaping () -> Void = {}) { var save = save var remove = false let duration = CACurrentMediaTime() - self.start if duration < 0.2 { save = false remove = true } self.recordingDisposable.set((self.camera.stopRecording() |> deliverOnMainQueue).startStrict(next: { [weak self] result in guard let self, let mediaEditor = self.mediaEditor, let entitiesView = self.entitiesView, case let .finished(mainResult, _, duration, _, _) = result else { return } if save { mediaEditor.setAdditionalVideo(mainResult.path, positionChanges: []) mediaEditor.setAdditionalVideoTrimRange(0..