Camera and editor fixes

This commit is contained in:
Ilya Laktyushin
2023-06-25 20:35:06 +02:00
parent 6ba424f13d
commit e4a1cb33f0
9 changed files with 334 additions and 283 deletions

View File

@@ -66,14 +66,6 @@ final class CameraDeviceContext {
self.output.configureVideoStabilization()
}
func switchOutputWith(_ otherContext: CameraDeviceContext) {
// guard let session = self.session else {
// return
// }
// self.output.reconfigure(for: session, device: self.device, input: self.input, otherPreviewView: otherContext.previewView, otherOutput: otherContext.output)
// otherContext.output.reconfigure(for: session, device: otherContext.device, input: otherContext.input, otherPreviewView: self.previewView, otherOutput: self.output)
}
func invalidate() {
guard let session = self.session else {
return
@@ -142,7 +134,7 @@ private final class CameraContext {
private var lastSnapshotTimestamp: Double = CACurrentMediaTime()
private var lastAdditionalSnapshotTimestamp: Double = CACurrentMediaTime()
private func savePreviewSnapshot(pixelBuffer: CVPixelBuffer, mirror: Bool, additional: Bool) {
private func savePreviewSnapshot(pixelBuffer: CVPixelBuffer, mirror: Bool) {
Queue.concurrentDefaultQueue().async {
var ciImage = CIImage(cvImageBuffer: pixelBuffer)
let size = ciImage.extent.size
@@ -154,10 +146,10 @@ private final class CameraContext {
ciImage = ciImage.clampedToExtent().applyingGaussianBlur(sigma: 40.0).cropped(to: CGRect(origin: .zero, size: size))
if let cgImage = self.cameraImageContext.createCGImage(ciImage, from: ciImage.extent) {
let uiImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right)
if additional {
CameraSimplePreviewView.saveAdditionalLastStateImage(uiImage)
if mirror {
CameraSimplePreviewView.saveLastFrontImage(uiImage)
} else {
CameraSimplePreviewView.saveLastStateImage(uiImage)
CameraSimplePreviewView.saveLastBackImage(uiImage)
}
}
}
@@ -171,6 +163,8 @@ private final class CameraContext {
self.simplePreviewView = previewView
self.secondaryPreviewView = secondaryPreviewView
self.dualPosition = configuration.position
self.mainDeviceContext = CameraDeviceContext(session: session, exclusive: true, additional: false)
self.configure {
self.mainDeviceContext.configure(position: configuration.position, previewView: self.simplePreviewView, audio: configuration.audio, photo: configuration.photo, metadata: configuration.metadata)
@@ -188,7 +182,7 @@ private final class CameraContext {
if #available(iOS 13.0, *) {
mirror = connection.inputPorts.first?.sourceDevicePosition == .front
}
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: false)
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror)
self.lastSnapshotTimestamp = timestamp
}
}
@@ -256,17 +250,19 @@ private final class CameraContext {
return self._positionPromise.get()
}
private var tmpPosition: Camera.Position = .back
private var dualPosition: Camera.Position = .back
func togglePosition() {
if self.isDualCamEnabled {
// let targetPosition: Camera.Position
// if case .back = self.tmpPosition {
// targetPosition = .front
// } else {
// targetPosition = .back
// }
// self.tmpPosition = targetPosition
// self._positionPromise.set(targetPosition)
let targetPosition: Camera.Position
if case .back = self.dualPosition {
targetPosition = .front
} else {
targetPosition = .back
}
self.dualPosition = targetPosition
self._positionPromise.set(targetPosition)
self.mainDeviceContext.output.markPositionChange(position: targetPosition)
} else {
self.configure {
self.mainDeviceContext.invalidate()
@@ -277,6 +273,7 @@ private final class CameraContext {
} else {
targetPosition = .back
}
self.dualPosition = targetPosition
self._positionPromise.set(targetPosition)
self.modeChange = .position
@@ -294,6 +291,7 @@ private final class CameraContext {
self.mainDeviceContext.invalidate()
self._positionPromise.set(position)
self.dualPosition = position
self.modeChange = .position
self.mainDeviceContext.configure(position: position, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata)
@@ -333,7 +331,7 @@ private final class CameraContext {
if #available(iOS 13.0, *) {
mirror = connection.inputPorts.first?.sourceDevicePosition == .front
}
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: false)
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror)
self.lastSnapshotTimestamp = timestamp
}
}
@@ -347,7 +345,7 @@ private final class CameraContext {
if #available(iOS 13.0, *) {
mirror = connection.inputPorts.first?.sourceDevicePosition == .front
}
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: true)
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror)
self.lastAdditionalSnapshotTimestamp = timestamp
}
}
@@ -358,7 +356,7 @@ private final class CameraContext {
self.additionalDeviceContext = nil
self.mainDeviceContext = CameraDeviceContext(session: self.session, exclusive: true, additional: false)
self.mainDeviceContext.configure(position: .back, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata)
self.mainDeviceContext.configure(position: self.dualPosition, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata)
}
self.mainDeviceContext.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in
guard let self else {
@@ -372,7 +370,7 @@ private final class CameraContext {
if #available(iOS 13.0, *) {
mirror = connection.inputPorts.first?.sourceDevicePosition == .front
}
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: false)
self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror)
self.lastSnapshotTimestamp = timestamp
}
}
@@ -448,12 +446,17 @@ private final class CameraContext {
func takePhoto() -> Signal<PhotoCaptureResult, NoError> {
let orientation = self.videoOrientation ?? .portrait
if let additionalDeviceContext = self.additionalDeviceContext {
let dualPosition = self.dualPosition
return combineLatest(
self.mainDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode),
additionalDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode)
) |> map { main, additional in
if case let .finished(mainImage, _, _) = main, case let .finished(additionalImage, _, _) = additional {
if dualPosition == .front {
return .finished(additionalImage, mainImage, CACurrentMediaTime())
} else {
return .finished(mainImage, additionalImage, CACurrentMediaTime())
}
} else {
return .began
}
@@ -482,8 +485,8 @@ private final class CameraContext {
self.mainDeviceContext.output.stopRecording(),
additionalDeviceContext.output.stopRecording()
) |> mapToSignal { main, additional in
if case let .finished(mainResult, _, _) = main, case let .finished(additionalResult, _, _) = additional {
return .single(.finished(mainResult, additionalResult, CACurrentMediaTime()))
if case let .finished(mainResult, _, duration, positionChangeTimestamps, _) = main, case let .finished(additionalResult, _, _, _, _) = additional {
return .single(.finished(mainResult, additionalResult, duration, positionChangeTimestamps, CACurrentMediaTime()))
} else {
return .complete()
}

View File

@@ -7,7 +7,7 @@ import Vision
import VideoToolbox
public enum VideoCaptureResult: Equatable {
case finished((String, UIImage), (String, UIImage)?, Double)
case finished((String, UIImage), (String, UIImage)?, Double, [(Bool, Double)], Double)
case failed
public static func == (lhs: VideoCaptureResult, rhs: VideoCaptureResult) -> Bool {
@@ -18,8 +18,11 @@ public enum VideoCaptureResult: Equatable {
} else {
return false
}
case let .finished(_, _, lhsTime):
if case let .finished(_, _, rhsTime) = rhs, lhsTime == rhsTime {
case let .finished(_, _, lhsDuration, lhsChangeTimestamps, lhsTime):
if case let .finished(_, _, rhsDuration, rhsChangeTimestamps, rhsTime) = rhs, lhsDuration == rhsDuration, lhsTime == rhsTime {
if lhsChangeTimestamps.count != rhsChangeTimestamps.count {
return false
}
return true
} else {
return false
@@ -85,6 +88,7 @@ final class CameraOutput: NSObject {
private var photoCaptureRequests: [Int64: PhotoCaptureContext] = [:]
private var videoRecorder: VideoRecorder?
weak var overrideOutput: CameraOutput?
var activeFilter: CameraFilter?
var faceLandmarks: Bool = false
@@ -333,8 +337,8 @@ final class CameraOutput: NSObject {
let outputFilePath = NSTemporaryDirectory() + outputFileName + ".mp4"
let outputFileURL = URL(fileURLWithPath: outputFilePath)
let videoRecorder = VideoRecorder(configuration: VideoRecorder.Configuration(videoSettings: videoSettings, audioSettings: audioSettings), videoTransform: CGAffineTransform(rotationAngle: .pi / 2.0), fileUrl: outputFileURL, completion: { [weak self] result in
if case let .success(transitionImage) = result {
self?.recordingCompletionPipe.putNext(.finished((outputFilePath, transitionImage!), nil, CACurrentMediaTime()))
if case let .success(transitionImage, duration, positionChangeTimestamps) = result {
self?.recordingCompletionPipe.putNext(.finished((outputFilePath, transitionImage!), nil, duration, positionChangeTimestamps.map { ($0 == .front, $1) }, CACurrentMediaTime()))
} else {
self?.recordingCompletionPipe.putNext(.failed)
}
@@ -364,6 +368,12 @@ final class CameraOutput: NSObject {
self.videoRecorder = nil
}
}
func markPositionChange(position: Camera.Position) {
if let videoRecorder = self.videoRecorder {
videoRecorder.markPositionChange(position: position)
}
}
}
extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {

View File

@@ -10,8 +10,8 @@ import Vision
import ImageBlur
public class CameraSimplePreviewView: UIView {
static func lastStateImage() -> UIImage {
let imagePath = NSTemporaryDirectory() + "cameraImage.jpg"
static func lastBackImage() -> UIImage {
let imagePath = NSTemporaryDirectory() + "backCameraImage.jpg"
if let data = try? Data(contentsOf: URL(fileURLWithPath: imagePath)), let image = UIImage(data: data) {
return image
} else {
@@ -19,15 +19,15 @@ public class CameraSimplePreviewView: UIView {
}
}
static func saveLastStateImage(_ image: UIImage) {
let imagePath = NSTemporaryDirectory() + "cameraImage.jpg"
static func saveLastBackImage(_ image: UIImage) {
let imagePath = NSTemporaryDirectory() + "backCameraImage.jpg"
if let data = image.jpegData(compressionQuality: 0.6) {
try? data.write(to: URL(fileURLWithPath: imagePath))
}
}
static func lastAdditionalStateImage() -> UIImage {
let imagePath = NSTemporaryDirectory() + "cameraImage2.jpg"
static func lastFrontImage() -> UIImage {
let imagePath = NSTemporaryDirectory() + "frontCameraImage.jpg"
if let data = try? Data(contentsOf: URL(fileURLWithPath: imagePath)), let image = UIImage(data: data) {
return image
} else {
@@ -35,30 +35,25 @@ public class CameraSimplePreviewView: UIView {
}
}
static func saveAdditionalLastStateImage(_ image: UIImage) {
let imagePath = NSTemporaryDirectory() + "cameraImage2.jpg"
static func saveLastFrontImage(_ image: UIImage) {
let imagePath = NSTemporaryDirectory() + "frontCameraImage.jpg"
if let data = image.jpegData(compressionQuality: 0.6) {
try? data.write(to: URL(fileURLWithPath: imagePath))
}
}
private let additional: Bool
private var previewingDisposable: Disposable?
private let placeholderView = UIImageView()
public init(frame: CGRect, additional: Bool) {
self.additional = additional
public init(frame: CGRect, main: Bool) {
super.init(frame: frame)
self.videoPreviewLayer.videoGravity = .resizeAspectFill
self.placeholderView.contentMode = .scaleAspectFill
self.placeholderView.image = additional ? CameraSimplePreviewView.lastAdditionalStateImage() : CameraSimplePreviewView.lastStateImage()
self.addSubview(self.placeholderView)
if !additional {
if main {
if #available(iOS 13.0, *) {
self.previewingDisposable = (self.isPreviewing
|> filter { $0 }
@@ -94,11 +89,8 @@ public class CameraSimplePreviewView: UIView {
}
}
public func resetPlaceholder() {
guard self.placeholderView.alpha == 0.0 else {
return
}
self.placeholderView.image = self.additional ? CameraSimplePreviewView.lastAdditionalStateImage() : CameraSimplePreviewView.lastStateImage()
public func resetPlaceholder(front: Bool) {
self.placeholderView.image = front ? CameraSimplePreviewView.lastFrontImage() : CameraSimplePreviewView.lastBackImage()
self.placeholderView.alpha = 1.0
}

View File

@@ -49,10 +49,12 @@ private final class VideoRecorderImpl {
private var recordingStartSampleTime: CMTime = .invalid
private var recordingStopSampleTime: CMTime = .invalid
private var positionChangeTimestamps: [(Camera.Position, CMTime)] = []
private let configuration: VideoRecorder.Configuration
private let videoTransform: CGAffineTransform
private let url: URL
fileprivate var completion: (Bool, UIImage?) -> Void = { _, _ in }
fileprivate var completion: (Bool, UIImage?, [(Camera.Position, CMTime)]?) -> Void = { _, _, _ in }
private let error = Atomic<Error?>(value: nil)
@@ -84,6 +86,17 @@ private final class VideoRecorderImpl {
}
}
public func markPositionChange(position: Camera.Position) {
self.queue.async {
guard self.recordingStartSampleTime.isValid else {
return
}
let currentTime = CMTime(seconds: CACurrentMediaTime(), preferredTimescale: CMTimeScale(NSEC_PER_SEC))
let delta = currentTime - self.recordingStartSampleTime
self.positionChangeTimestamps.append((position, delta))
}
}
public func appendVideoSampleBuffer(_ sampleBuffer: CMSampleBuffer) {
if let _ = self.hasError() {
return
@@ -291,21 +304,21 @@ private final class VideoRecorderImpl {
let completion = self.completion
if self.recordingStopSampleTime == .invalid {
DispatchQueue.main.async {
completion(false, nil)
completion(false, nil, nil)
}
return
}
if let _ = self.error.with({ $0 }) {
DispatchQueue.main.async {
completion(false, nil)
completion(false, nil, nil)
}
return
}
if !self.tryAppendingPendingAudioBuffers() {
DispatchQueue.main.async {
completion(false, nil)
completion(false, nil, nil)
}
return
}
@@ -314,21 +327,21 @@ private final class VideoRecorderImpl {
self.assetWriter.finishWriting {
if let _ = self.assetWriter.error {
DispatchQueue.main.async {
completion(false, nil)
completion(false, nil, nil)
}
} else {
DispatchQueue.main.async {
completion(true, self.transitionImage)
completion(true, self.transitionImage, self.positionChangeTimestamps)
}
}
}
} else if let _ = self.assetWriter.error {
DispatchQueue.main.async {
completion(false, nil)
completion(false, nil, nil)
}
} else {
DispatchQueue.main.async {
completion(false, nil)
completion(false, nil, nil)
}
}
}
@@ -407,7 +420,7 @@ public final class VideoRecorder {
case generic
}
case success(UIImage?)
case success(UIImage?, Double, [(Camera.Position, Double)])
case initError(Error)
case writeError(Error)
case finishError(Error)
@@ -448,10 +461,17 @@ public final class VideoRecorder {
return nil
}
self.impl = impl
impl.completion = { [weak self] result, transitionImage in
impl.completion = { [weak self] result, transitionImage, positionChangeTimestamps in
if let self {
let duration = self.duration ?? 0.0
if result {
self.completion(.success(transitionImage))
var timestamps: [(Camera.Position, Double)] = []
if let positionChangeTimestamps {
for (position, time) in positionChangeTimestamps {
timestamps.append((position, time.seconds))
}
}
self.completion(.success(transitionImage, duration, timestamps))
} else {
self.completion(.finishError(.generic))
}
@@ -467,6 +487,10 @@ public final class VideoRecorder {
self.impl.stopRecording()
}
func markPositionChange(position: Camera.Position) {
self.impl.markPositionChange(position: position)
}
func appendSampleBuffer(_ sampleBuffer: CMSampleBuffer) {
guard let formatDescriptor = CMSampleBufferGetFormatDescription(sampleBuffer) else {
return

View File

@@ -305,8 +305,8 @@ private final class CameraScreenComponent: CombinedComponent {
self.cameraState = self.cameraState.updatedRecording(.none).updatedDuration(0.0)
self.resultDisposable.set((self.camera.stopRecording()
|> deliverOnMainQueue).start(next: { [weak self] result in
if let self, case let .finished(mainResult, additionalResult, _) = result {
self.completion.invoke(.single(.video(mainResult.0, mainResult.1, additionalResult?.0, additionalResult?.1, PixelDimensions(width: 1080, height: 1920), .bottomRight)))
if let self, case let .finished(mainResult, additionalResult, duration, positionChangeTimestamps, _) = result {
self.completion.invoke(.single(.video(mainResult.0, mainResult.1, additionalResult?.0, additionalResult?.1, PixelDimensions(width: 1080, height: 1920), duration, positionChangeTimestamps, .bottomRight)))
}
}))
self.isTransitioning = true
@@ -741,8 +741,6 @@ private final class CameraScreenComponent: CombinedComponent {
}
}
private let useSimplePreviewView = true
private class BlurView: UIVisualEffectView {
private func setup() {
for subview in self.subviews {
@@ -803,7 +801,7 @@ public class CameraScreen: ViewController {
public enum Result {
case pendingImage
case image(UIImage, UIImage?, CameraScreen.PIPPosition)
case video(String, UIImage?, String?, UIImage?, PixelDimensions, CameraScreen.PIPPosition)
case video(String, UIImage?, String?, UIImage?, PixelDimensions, Double, [(Bool, Double)], CameraScreen.PIPPosition)
case asset(PHAsset)
case draft(MediaEditorDraft)
@@ -811,8 +809,8 @@ public class CameraScreen: ViewController {
switch self {
case let .image(mainImage, additionalImage, _):
return .image(mainImage, additionalImage, position)
case let .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, _):
return .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, position)
case let .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, duration, positionChangeTimestamps, _):
return .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, duration, positionChangeTimestamps, position)
default:
return self
}
@@ -860,11 +858,15 @@ public class CameraScreen: ViewController {
fileprivate let containerView: UIView
fileprivate let componentHost: ComponentView<ViewControllerComponentContainer.Environment>
private let previewContainerView: UIView
fileprivate let previewView: CameraPreviewView?
fileprivate let simplePreviewView: CameraSimplePreviewView?
fileprivate var additionalPreviewView: CameraSimplePreviewView?
private let mainPreviewContainerView: UIView
fileprivate var mainPreviewView: CameraSimplePreviewView
private let additionalPreviewContainerView: UIView
fileprivate var additionalPreviewView: CameraSimplePreviewView
fileprivate let previewBlurView: BlurView
private var previewSnapshotView: UIView?
private var mainPreviewSnapshotView: UIView?
private var additionalPreviewSnapshotView: UIView?
fileprivate let previewFrameLeftDimView: UIView
fileprivate let previewFrameRightDimView: UIView
@@ -882,46 +884,6 @@ public class CameraScreen: ViewController {
private let completion = ActionSlot<Signal<CameraScreen.Result, NoError>>()
private var effectivePreviewView: UIView {
if let simplePreviewView = self.simplePreviewView {
return simplePreviewView
} else if let previewView = self.previewView {
return previewView
} else {
fatalError()
}
}
private var currentPreviewView: UIView {
if let simplePreviewView = self.simplePreviewView {
if let additionalPreviewView = self.additionalPreviewView {
if self.isDualCamEnabled && cameraPosition == .front {
return additionalPreviewView
} else {
return simplePreviewView
}
} else {
return simplePreviewView
}
} else if let previewView = self.previewView {
return previewView
} else {
fatalError()
}
}
private var currentAdditionalPreviewView: UIView? {
if let additionalPreviewView = self.additionalPreviewView {
if self.isDualCamEnabled && cameraPosition == .front {
return self.simplePreviewView
} else {
return additionalPreviewView
}
} else {
return nil
}
}
fileprivate var previewBlurPromise = ValuePromise<Bool>(false)
private let flipAnimationAction = ActionSlot<Void>()
@@ -953,36 +915,22 @@ public class CameraScreen: ViewController {
self.previewBlurView = BlurView()
self.previewBlurView.isUserInteractionEnabled = false
if let holder = controller.holder {
self.simplePreviewView = nil
self.previewView = holder.previewView
self.camera = holder.camera
} else {
if useSimplePreviewView {
self.simplePreviewView = CameraSimplePreviewView(frame: .zero, additional: false)
self.previewView = nil
self.mainPreviewContainerView = UIView()
self.mainPreviewContainerView.clipsToBounds = true
self.mainPreviewView = CameraSimplePreviewView(frame: .zero, main: true)
self.additionalPreviewView = CameraSimplePreviewView(frame: .zero, additional: true)
self.additionalPreviewView?.clipsToBounds = true
} else {
self.previewView = CameraPreviewView(test: false)!
self.simplePreviewView = nil
}
self.additionalPreviewContainerView = UIView()
self.additionalPreviewContainerView.clipsToBounds = true
self.additionalPreviewView = CameraSimplePreviewView(frame: .zero, main: false)
var cameraFrontPosition = false
if let useCameraFrontPosition = UserDefaults.standard.object(forKey: "TelegramStoryCameraUseFrontPosition") as? NSNumber, useCameraFrontPosition.boolValue {
cameraFrontPosition = true
}
self.mainPreviewView.resetPlaceholder(front: cameraFrontPosition)
self.cameraPosition = cameraFrontPosition ? .front : .back
self.camera = Camera(configuration: Camera.Configuration(preset: .hd1920x1080, position: self.cameraPosition, audio: true, photo: true, metadata: false, preferredFps: 60.0), previewView: self.simplePreviewView, secondaryPreviewView: self.additionalPreviewView)
if !useSimplePreviewView {
#if targetEnvironment(simulator)
#else
self.camera.attachPreviewView(self.previewView!)
#endif
}
}
self.camera = Camera(configuration: Camera.Configuration(preset: .hd1920x1080, position: self.cameraPosition, audio: true, photo: true, metadata: false, preferredFps: 60.0), previewView: self.mainPreviewView, secondaryPreviewView: self.additionalPreviewView)
self.previewFrameLeftDimView = UIView()
self.previewFrameLeftDimView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.6)
@@ -1006,16 +954,16 @@ public class CameraScreen: ViewController {
self.view.addSubview(self.containerView)
self.containerView.addSubview(self.previewContainerView)
self.previewContainerView.addSubview(self.effectivePreviewView)
self.previewContainerView.addSubview(self.mainPreviewContainerView)
self.previewContainerView.addSubview(self.additionalPreviewContainerView)
self.previewContainerView.addSubview(self.previewBlurView)
self.previewContainerView.addSubview(self.previewFrameLeftDimView)
self.previewContainerView.addSubview(self.previewFrameRightDimView)
self.containerView.addSubview(self.transitionDimView)
self.view.addSubview(self.transitionCornersView)
if let additionalPreviewView = self.additionalPreviewView {
self.previewContainerView.insertSubview(additionalPreviewView, at: 1)
}
self.mainPreviewContainerView.addSubview(self.mainPreviewView)
self.additionalPreviewContainerView.addSubview(self.additionalPreviewView)
self.changingPositionDisposable = combineLatest(
queue: Queue.mainQueue(),
@@ -1024,18 +972,36 @@ public class CameraScreen: ViewController {
).start(next: { [weak self] modeChange, forceBlur in
if let self {
if modeChange != .none {
if let snapshot = self.simplePreviewView?.snapshotView(afterScreenUpdates: false) {
self.simplePreviewView?.addSubview(snapshot)
self.previewSnapshotView = snapshot
// if case .dualCamera = modeChange, self.cameraPosition == .front {
// if self.isDualCamEnabled {
// if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) {
// snapshot.frame = CGRect(origin: .zero, size: self.mainPreviewContainerView.bounds.size)
// self.mainPreviewView.addSubview(snapshot)
// self.previewSnapshotView = snapshot
// }
// } else {
// if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) {
// snapshot.frame = CGRect(origin: .zero, size: self.mainPreviewContainerView.bounds.size)
// self.additionalPreviewView.addSubview(snapshot)
// self.additionalPreviewSnapshotView = snapshot
// }
// }
// } else {
if case .dualCamera = modeChange, self.cameraPosition == .front {
} else {
if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) {
self.mainPreviewView.addSubview(snapshot)
self.mainPreviewSnapshotView = snapshot
}
}
if case .position = modeChange {
UIView.transition(with: self.previewContainerView, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: {
self.previewBlurView.effect = UIBlurEffect(style: .dark)
})
} else {
if let additionalPreviewView = self.additionalPreviewView {
self.previewContainerView.insertSubview(self.previewBlurView, belowSubview: additionalPreviewView)
}
self.previewContainerView.insertSubview(self.previewBlurView, belowSubview: self.additionalPreviewContainerView)
UIView.animate(withDuration: 0.4) {
self.previewBlurView.effect = UIBlurEffect(style: .dark)
}
@@ -1045,16 +1011,16 @@ public class CameraScreen: ViewController {
self.previewBlurView.effect = UIBlurEffect(style: .dark)
}
} else {
if self.previewBlurView.effect != nil {
UIView.animate(withDuration: 0.4, animations: {
self.previewBlurView.effect = nil
}, completion: { _ in
if let additionalPreviewView = self.additionalPreviewView {
self.previewContainerView.insertSubview(self.previewBlurView, aboveSubview: additionalPreviewView)
}
self.previewContainerView.insertSubview(self.previewBlurView, aboveSubview: self.additionalPreviewContainerView)
})
}
if let previewSnapshotView = self.previewSnapshotView {
self.previewSnapshotView = nil
if let previewSnapshotView = self.mainPreviewSnapshotView {
self.mainPreviewSnapshotView = nil
UIView.animate(withDuration: 0.25, animations: {
previewSnapshotView.alpha = 0.0
}, completion: { _ in
@@ -1072,7 +1038,8 @@ public class CameraScreen: ViewController {
}
if self.isDualCamEnabled {
self.additionalPreviewView?.removePlaceholder()
self.mainPreviewView.removePlaceholder()
self.additionalPreviewView.removePlaceholder()
}
}
}
@@ -1093,9 +1060,9 @@ public class CameraScreen: ViewController {
}
if case .pendingImage = value {
Queue.mainQueue().async {
self.simplePreviewView?.isEnabled = false
self.mainPreviewView.isEnabled = false
self.additionalPreviewView?.isEnabled = false
self.additionalPreviewView.isEnabled = false
}
} else {
Queue.mainQueue().async {
@@ -1104,8 +1071,8 @@ public class CameraScreen: ViewController {
self.previewBlurPromise.set(true)
}
}
self.simplePreviewView?.isEnabled = false
self.additionalPreviewView?.isEnabled = false
self.mainPreviewView.isEnabled = false
self.additionalPreviewView.isEnabled = false
self.camera.stopCapture()
}
}
@@ -1119,25 +1086,32 @@ public class CameraScreen: ViewController {
self.updateState.connect { [weak self] state in
if let self {
let previousPosition = self.cameraPosition
self.cameraPosition = state.position
let newPosition = state.position != .unspecified ? state.position : previousPosition
self.cameraPosition = newPosition
let dualCamWasEnabled = self.isDualCamEnabled
self.isDualCamEnabled = state.isDualCamEnabled
if self.isDualCamEnabled && previousPosition != state.position, let additionalPreviewView = self.additionalPreviewView {
if state.position == .front {
additionalPreviewView.superview?.sendSubviewToBack(additionalPreviewView)
} else {
additionalPreviewView.superview?.insertSubview(additionalPreviewView, aboveSubview: self.simplePreviewView!)
if self.isDualCamEnabled != dualCamWasEnabled && newPosition == .front {
if self.isDualCamEnabled {
if let cloneView = self.mainPreviewView.snapshotView(afterScreenUpdates: false) {
self.mainPreviewSnapshotView = cloneView
self.mainPreviewContainerView.addSubview(cloneView)
}
} else {
if let cloneView = self.additionalPreviewView.snapshotView(afterScreenUpdates: false) {
self.mainPreviewSnapshotView = cloneView
self.mainPreviewContainerView.addSubview(cloneView)
}
}
}
if self.isDualCamEnabled && previousPosition != newPosition {
CATransaction.begin()
CATransaction.setDisableActions(true)
self.requestUpdateLayout(hasAppeared: false, transition: .immediate)
CATransaction.commit()
} else {
if !dualCamWasEnabled && self.isDualCamEnabled {
}
} else if dualCamWasEnabled != self.isDualCamEnabled {
self.requestUpdateLayout(hasAppeared: false, transition: .spring(duration: 0.4))
}
}
@@ -1155,17 +1129,17 @@ public class CameraScreen: ViewController {
self.view.disablesInteractiveKeyboardGestureRecognizer = true
let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(self.handlePinch(_:)))
self.effectivePreviewView.addGestureRecognizer(pinchGestureRecognizer)
self.mainPreviewContainerView.addGestureRecognizer(pinchGestureRecognizer)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
panGestureRecognizer.maximumNumberOfTouches = 1
self.effectivePreviewView.addGestureRecognizer(panGestureRecognizer)
self.mainPreviewContainerView.addGestureRecognizer(panGestureRecognizer)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
self.effectivePreviewView.addGestureRecognizer(tapGestureRecognizer)
self.mainPreviewContainerView.addGestureRecognizer(tapGestureRecognizer)
let pipPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePipPan(_:)))
self.additionalPreviewView?.addGestureRecognizer(pipPanGestureRecognizer)
self.additionalPreviewContainerView.addGestureRecognizer(pipPanGestureRecognizer)
self.camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true)
self.camera.startCapture()
@@ -1217,11 +1191,8 @@ public class CameraScreen: ViewController {
}
@objc private func handleTap(_ gestureRecognizer: UITapGestureRecognizer) {
guard let previewView = self.simplePreviewView else {
return
}
let location = gestureRecognizer.location(in: previewView)
let point = previewView.cameraPoint(for: location)
let location = gestureRecognizer.location(in: mainPreviewView)
let point = mainPreviewView.cameraPoint(for: location)
self.camera.focus(at: point, autoFocus: false)
}
@@ -1347,8 +1318,8 @@ public class CameraScreen: ViewController {
}
func pauseCameraCapture() {
self.simplePreviewView?.isEnabled = false
self.additionalPreviewView?.isEnabled = false
self.mainPreviewView.isEnabled = false
self.additionalPreviewView.isEnabled = false
Queue.mainQueue().after(0.3) {
self.previewBlurPromise.set(true)
}
@@ -1356,21 +1327,21 @@ public class CameraScreen: ViewController {
}
func resumeCameraCapture() {
if self.simplePreviewView?.isEnabled == false {
if let snapshot = self.simplePreviewView?.snapshotView(afterScreenUpdates: false) {
self.simplePreviewView?.addSubview(snapshot)
self.previewSnapshotView = snapshot
if !self.mainPreviewView.isEnabled {
if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) {
self.mainPreviewView.addSubview(snapshot)
self.mainPreviewSnapshotView = snapshot
}
if let snapshot = self.additionalPreviewView?.snapshotView(afterScreenUpdates: false) {
self.additionalPreviewView?.addSubview(snapshot)
if let snapshot = self.additionalPreviewView.snapshotView(afterScreenUpdates: false) {
self.additionalPreviewView.addSubview(snapshot)
self.additionalPreviewSnapshotView = snapshot
}
self.simplePreviewView?.isEnabled = true
self.additionalPreviewView?.isEnabled = true
self.mainPreviewView.isEnabled = true
self.additionalPreviewView.isEnabled = true
self.camera.startCapture()
if #available(iOS 13.0, *), let isPreviewing = self.simplePreviewView?.isPreviewing {
let _ = (isPreviewing
if #available(iOS 13.0, *) {
let _ = (self.mainPreviewView.isPreviewing
|> filter {
$0
}
@@ -1467,10 +1438,10 @@ public class CameraScreen: ViewController {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if result == self.componentHost.view {
if let additionalPreviewView = self.additionalPreviewView, additionalPreviewView.bounds.contains(self.view.convert(point, to: additionalPreviewView)) {
return additionalPreviewView
if self.additionalPreviewView.bounds.contains(self.view.convert(point, to: self.additionalPreviewView)) {
return self.additionalPreviewView
} else {
return self.effectivePreviewView
return self.mainPreviewView
}
}
return result
@@ -1569,7 +1540,7 @@ public class CameraScreen: ViewController {
panelWidth: panelWidth,
flipAnimationAction: self.flipAnimationAction,
animateShutter: { [weak self] in
self?.effectivePreviewView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
self?.mainPreviewContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
},
present: { [weak self] c in
self?.controller?.present(c, in: .window(.root))
@@ -1605,16 +1576,13 @@ public class CameraScreen: ViewController {
transition.setFrame(view: self.transitionDimView, frame: CGRect(origin: .zero, size: layout.size))
transition.setFrame(view: self.previewContainerView, frame: previewFrame)
self.currentPreviewView.layer.cornerRadius = 0.0
transition.setFrame(view: self.currentPreviewView, frame: CGRect(origin: .zero, size: previewFrame.size))
transition.setFrame(view: self.mainPreviewContainerView, frame: CGRect(origin: .zero, size: previewFrame.size))
transition.setFrame(view: self.previewBlurView, frame: CGRect(origin: .zero, size: previewFrame.size))
if let additionalPreviewView = self.currentAdditionalPreviewView as? CameraSimplePreviewView {
let dualCamUpdated = self.appliedDualCam != self.isDualCamEnabled
self.appliedDualCam = self.isDualCamEnabled
additionalPreviewView.layer.cornerRadius = 80.0
var origin: CGPoint
switch self.pipPosition {
case .topLeft:
@@ -1644,19 +1612,41 @@ public class CameraScreen: ViewController {
}
let additionalPreviewFrame = CGRect(origin: origin, size: CGSize(width: 160.0, height: 160.0))
transition.setPosition(view: additionalPreviewView, position: additionalPreviewFrame.center)
transition.setBounds(view: additionalPreviewView, bounds: CGRect(origin: .zero, size: additionalPreviewFrame.size))
transition.setPosition(view: self.additionalPreviewContainerView, position: additionalPreviewFrame.center)
transition.setBounds(view: self.additionalPreviewContainerView, bounds: CGRect(origin: .zero, size: additionalPreviewFrame.size))
self.additionalPreviewContainerView.layer.cornerRadius = additionalPreviewFrame.width / 2.0
transition.setScale(view: additionalPreviewView, scale: self.isDualCamEnabled ? 1.0 : 0.1)
transition.setAlpha(view: additionalPreviewView, alpha: self.isDualCamEnabled ? 1.0 : 0.0)
transition.setScale(view: self.additionalPreviewContainerView, scale: self.isDualCamEnabled ? 1.0 : 0.1)
transition.setAlpha(view: self.additionalPreviewContainerView, alpha: self.isDualCamEnabled ? 1.0 : 0.0)
if dualCamUpdated && !self.isDualCamEnabled {
Queue.mainQueue().after(0.5) {
additionalPreviewView.resetPlaceholder()
if dualCamUpdated && self.isDualCamEnabled {
if self.cameraPosition == .back {
self.additionalPreviewView.resetPlaceholder(front: true)
} else {
self.mainPreviewView.resetPlaceholder(front: false)
}
}
let mainPreviewView: CameraSimplePreviewView
let additionalPreviewView: CameraSimplePreviewView
if self.cameraPosition == .front && self.isDualCamEnabled {
mainPreviewView = self.additionalPreviewView
additionalPreviewView = self.mainPreviewView
} else {
mainPreviewView = self.mainPreviewView
additionalPreviewView = self.additionalPreviewView
}
if mainPreviewView.superview != self.mainPreviewContainerView {
self.mainPreviewContainerView.insertSubview(mainPreviewView, at: 0)
}
if additionalPreviewView.superview != self.additionalPreviewContainerView {
self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0)
}
mainPreviewView.frame = CGRect(origin: .zero, size: previewFrame.size)
additionalPreviewView.frame = CGRect(origin: .zero, size: additionalPreviewFrame.size)
self.previewFrameLeftDimView.isHidden = !isTablet
transition.setFrame(view: self.previewFrameLeftDimView, frame: CGRect(origin: .zero, size: CGSize(width: viewfinderFrame.minX, height: viewfinderFrame.height)))
@@ -1780,6 +1770,7 @@ public class CameraScreen: ViewController {
self.node.animateInFromEditor(toGallery: self.galleryController?.displayNode.supernode != nil)
}
private var didStopCameraCapture = false
func presentGallery(fromGesture: Bool = false) {
if !fromGesture {
self.hapticFeedback.impact(.light)
@@ -1787,20 +1778,20 @@ public class CameraScreen: ViewController {
self.dismissAllTooltips()
var didStopCameraCapture = false
self.didStopCameraCapture = false
let stopCameraCapture = { [weak self] in
guard !didStopCameraCapture, let self else {
guard let self, !self.didStopCameraCapture else {
return
}
didStopCameraCapture = true
self.didStopCameraCapture = true
self.node.pauseCameraCapture()
}
let resumeCameraCapture = { [weak self] in
guard didStopCameraCapture, let self else {
guard let self, self.didStopCameraCapture else {
return
}
didStopCameraCapture = false
self.didStopCameraCapture = false
self.node.resumeCameraCapture()
}

View File

@@ -25,13 +25,13 @@ public struct MediaEditorPlayerState {
public final class MediaEditor {
public enum Subject {
case image(UIImage, PixelDimensions)
case video(String, UIImage?, PixelDimensions)
case video(String, UIImage?, PixelDimensions, Double)
case asset(PHAsset)
case draft(MediaEditorDraft)
var dimensions: PixelDimensions {
switch self {
case let .image(_, dimensions), let .video(_, _, dimensions):
case let .image(_, dimensions), let .video(_, _, dimensions, _):
return dimensions
case let .asset(asset):
return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight))
@@ -116,6 +116,12 @@ public final class MediaEditor {
}
private let playerPlaybackStatePromise = Promise<(Double, Double, Bool, Bool)>((0.0, 0.0, false, false))
public var position: Signal<Double, NoError> {
return self.playerPlaybackStatePromise.get()
|> map { _, position, _, _ -> Double in
return position
}
}
public var duration: Double? {
if let _ = self.player {
if let trimRange = self.values.videoTrimRange {
@@ -275,6 +281,9 @@ public final class MediaEditor {
if case let .asset(asset) = subject {
self.playerPlaybackState = (asset.duration, 0.0, false, false)
self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState))
} else if case let .video(_, _, _, duration) = subject {
self.playerPlaybackState = (duration, 0.0, false, true)
self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState))
}
}
@@ -350,7 +359,7 @@ public final class MediaEditor {
}
textureSource = .single((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, colors.0, colors.1))
}
case let .video(path, transitionImage, _):
case let .video(path, transitionImage, _, _):
textureSource = Signal { subscriber in
let url = URL(fileURLWithPath: path)
let asset = AVURLAsset(url: url)

View File

@@ -378,6 +378,7 @@ final class MediaEditorScreenComponent: Component {
self.endEditing(true)
}
private var animatingButtons = false
enum TransitionAnimationSource {
case camera
case gallery
@@ -401,6 +402,7 @@ final class MediaEditorScreenComponent: Component {
}
if case .camera = source {
self.animatingButtons = true
var delay: Double = 0.0
for button in buttons {
if let view = button.view {
@@ -412,10 +414,12 @@ final class MediaEditorScreenComponent: Component {
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0)
})
delay += 0.03
Queue.mainQueue().after(0.45, completion)
}
}
Queue.mainQueue().after(0.45, {
self.animatingButtons = false
completion()
})
if let view = self.saveButton.view {
view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2)
@@ -745,8 +749,10 @@ final class MediaEditorScreenComponent: Component {
}
transition.setPosition(view: drawButtonView, position: drawButtonFrame.center)
transition.setBounds(view: drawButtonView, bounds: CGRect(origin: .zero, size: drawButtonFrame.size))
if !self.animatingButtons {
transition.setAlpha(view: drawButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
}
}
let textButtonSize = self.textButton.update(
transition: transition,
@@ -772,8 +778,10 @@ final class MediaEditorScreenComponent: Component {
}
transition.setPosition(view: textButtonView, position: textButtonFrame.center)
transition.setBounds(view: textButtonView, bounds: CGRect(origin: .zero, size: textButtonFrame.size))
if !self.animatingButtons {
transition.setAlpha(view: textButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
}
}
let stickerButtonSize = self.stickerButton.update(
transition: transition,
@@ -799,8 +807,10 @@ final class MediaEditorScreenComponent: Component {
}
transition.setPosition(view: stickerButtonView, position: stickerButtonFrame.center)
transition.setBounds(view: stickerButtonView, bounds: CGRect(origin: .zero, size: stickerButtonFrame.size))
if !self.animatingButtons {
transition.setAlpha(view: stickerButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
}
}
let toolsButtonSize = self.toolsButton.update(
transition: transition,
@@ -826,8 +836,10 @@ final class MediaEditorScreenComponent: Component {
}
transition.setPosition(view: toolsButtonView, position: toolsButtonFrame.center)
transition.setBounds(view: toolsButtonView, bounds: CGRect(origin: .zero, size: toolsButtonFrame.size))
if !self.animatingButtons {
transition.setAlpha(view: toolsButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
}
}
var mediaEditor: MediaEditor?
if let controller = environment.controller() as? MediaEditorScreen {
@@ -876,8 +888,10 @@ final class MediaEditorScreenComponent: Component {
self.addSubview(scrubberView)
}
transition.setFrame(view: scrubberView, frame: scrubberFrame)
if !self.animatingButtons {
transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0)
}
}
scrubberBottomInset = scrubberSize.height + 10.0
} else {
@@ -1045,8 +1059,10 @@ final class MediaEditorScreenComponent: Component {
}
let isVisible = inputHeight > 44.0
transition.setFrame(view: inputPanelBackgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: isVisible ? availableSize.height - inputPanelBackgroundSize.height : availableSize.height), size: inputPanelBackgroundSize))
if !self.animatingButtons {
transition.setAlpha(view: inputPanelBackgroundView, alpha: isVisible ? 1.0 : 0.0, delay: isVisible ? 0.0 : 0.4)
}
}
var isEditingTextEntity = false
var sizeSliderVisible = false
@@ -1772,7 +1788,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
imageEntity.scale = 1.49
imageEntity.position = position.getPosition(storyDimensions)
self.entitiesView.add(imageEntity, announce: false)
} else if case let .video(_, _, additionalVideoPath, additionalVideoImage, _, position) = subject, let additionalVideoPath {
} else if case let .video(_, _, additionalVideoPath, additionalVideoImage, _, _, _, position) = subject, let additionalVideoPath {
let videoEntity = DrawingStickerEntity(content: .video(additionalVideoPath, additionalVideoImage))
videoEntity.referenceDrawingSize = storyDimensions
videoEntity.scale = 1.49
@@ -2133,7 +2149,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
if let view = self.componentHost.view as? MediaEditorScreenComponent.View {
view.animateIn(from: .camera, completion: completion)
}
if let subject = self.subject, case let .video(_, transitionImage, _, _, _, _) = subject, let transitionImage {
if let subject = self.subject, case let .video(_, transitionImage, _, _, _, _, _, _) = subject, let transitionImage {
self.setupTransitionImage(transitionImage)
}
case let .gallery(transitionIn):
@@ -2840,13 +2856,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
public enum Subject {
case image(UIImage, PixelDimensions, UIImage?, PIPPosition)
case video(String, UIImage?, String?, UIImage?, PixelDimensions, PIPPosition)
case video(String, UIImage?, String?, UIImage?, PixelDimensions, Double, [(Bool, Double)], PIPPosition)
case asset(PHAsset)
case draft(MediaEditorDraft, Int64?)
var dimensions: PixelDimensions {
switch self {
case let .image(_, dimensions, _, _), let .video(_, _, _, _, dimensions, _):
case let .image(_, dimensions, _, _), let .video(_, _, _, _, dimensions, _, _, _):
return dimensions
case let .asset(asset):
return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight))
@@ -2859,8 +2875,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
switch self {
case let .image(image, dimensions, _, _):
return .image(image, dimensions)
case let .video(videoPath, transitionImage, _, _, dimensions, _):
return .video(videoPath, transitionImage, dimensions)
case let .video(videoPath, transitionImage, _, _, dimensions, duration, _, _):
return .video(videoPath, transitionImage, dimensions, duration)
case let .asset(asset):
return .asset(asset)
case let .draft(draft, _):
@@ -2872,7 +2888,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
switch self {
case let .image(image, dimensions, _, _):
return .image(image, dimensions)
case let .video(videoPath, _, _, _, dimensions, _):
case let .video(videoPath, _, _, _, dimensions, _, _, _):
return .video(videoPath, dimensions)
case let .asset(asset):
return .asset(asset)
@@ -3311,7 +3327,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
switch subject {
case let .image(image, dimensions, _, _):
saveImageDraft(image, dimensions)
case let .video(path, _, _, _, dimensions, _):
case let .video(path, _, _, _, dimensions, _, _, _):
saveVideoDraft(path, dimensions, duration)
case let .asset(asset):
if asset.mediaType == .video {
@@ -3404,7 +3420,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
duration = 5.0
firstFrame = .single(image)
case let .video(path, _, _, _, _, _):
case let .video(path, _, _, _, _, _, _, _):
videoResult = .videoFile(path: path)
if let videoTrimRange = mediaEditor.values.videoTrimRange {
duration = videoTrimRange.upperBound - videoTrimRange.lowerBound
@@ -3592,7 +3608,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
let exportSubject: Signal<MediaEditorVideoExport.Subject, NoError>
switch subject {
case let .video(path, _, _, _, _, _):
case let .video(path, _, _, _, _, _, _, _):
let asset = AVURLAsset(url: NSURL(fileURLWithPath: path) as URL)
exportSubject = .single(.video(asset))
case let .image(image, _, _, _):

View File

@@ -2450,7 +2450,13 @@ public final class StoryItemSetContainerComponent: Component {
// if let source {
// subject = .single(.draft(source, Int64(id)))
// } else {
subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: item.id, media: item.media._asMedia()))
var duration: Double?
let media = item.media._asMedia()
if let file = media as? TelegramMediaFile {
duration = file.duration
}
subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: item.id, media: media))
|> mapToSignal { (value, isImage) -> Signal<MediaEditorScreen.Subject?, NoError> in
guard case let .data(data) = value, data.complete else {
return .complete()
@@ -2468,7 +2474,7 @@ public final class StoryItemSetContainerComponent: Component {
}
return .single(nil)
|> then(
.single(.video(symlinkPath, nil, nil, nil, PixelDimensions(width: 720, height: 1280), .bottomRight))
.single(.video(symlinkPath, nil, nil, nil, PixelDimensions(width: 720, height: 1280), duration ?? 0.0, [], .bottomRight))
|> delay(0.1, queue: Queue.mainQueue())
)
}

View File

@@ -305,8 +305,8 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
return nil
case let .image(image, additionalImage, pipPosition):
return .image(image, PixelDimensions(image.size), additionalImage, editorPIPPosition(pipPosition))
case let .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, pipPosition):
return .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, editorPIPPosition(pipPosition))
case let .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, duration, positionChangeTimestamps, pipPosition):
return .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, duration, positionChangeTimestamps, editorPIPPosition(pipPosition))
case let .asset(asset):
return .asset(asset)
case let .draft(draft):