diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index e5ff346cbf..af9fbfab18 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -289,6 +289,7 @@ public final class MediaEditor { self.updateRenderChain() } else { self.values = MediaEditorValues( + peerId: context.account.peerId, originalDimensions: subject.dimensions, cropOffset: .zero, cropSize: nil, @@ -310,7 +311,7 @@ public final class MediaEditor { toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, - audioTrackStart: nil, + audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil ) @@ -346,6 +347,8 @@ public final class MediaEditor { if let didPlayToEndTimeObserver = self.didPlayToEndTimeObserver { NotificationCenter.default.removeObserver(didPlayToEndTimeObserver) } + + self.audioDelayTimer?.invalidate() } public func replaceSource(_ image: UIImage, additionalImage: UIImage?, time: CMTime) { @@ -553,7 +556,7 @@ public final class MediaEditor { self.setAudioTrack(audioTrack) self.setAudioTrackVolume(self.values.audioTrackVolume) self.setAudioTrackTrimRange(self.values.audioTrackTrimRange, apply: true) - self.setAudioTrackStart(self.values.audioTrackStart) + self.setAudioTrackOffset(self.values.audioTrackOffset, apply: true) } if let player { @@ -618,12 +621,23 @@ public final class MediaEditor { let targetTime = CMTime(seconds: start, preferredTimescale: CMTimeScale(1000)) self.player?.seek(to: targetTime) self.additionalPlayer?.seek(to: targetTime) - self.audioPlayer?.seek(to: self.audioTime(for: targetTime)) self.onPlaybackAction(.seek(start)) self.player?.play() self.additionalPlayer?.play() - self.audioPlayer?.play() + + let audioTime = self.audioTime(for: targetTime) + if let audioDelay = self.audioDelay(for: targetTime) { + self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in + self?.audioPlayer?.seek(to: audioTime) + self?.audioPlayer?.play() + }, queue: Queue.mainQueue()) + self.audioDelayTimer?.start() + } else { + self.audioPlayer?.seek(to: audioTime) + self.audioPlayer?.play() + } + Queue.mainQueue().justDispatch { self.onPlaybackAction(.play) @@ -746,7 +760,19 @@ public final class MediaEditor { if play { self.player?.play() self.additionalPlayer?.play() - self.audioPlayer?.play() + + let audioTime = self.audioTime(for: targetPosition) + if let audioDelay = self.audioDelay(for: targetPosition) { + self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in + self?.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) + self?.audioPlayer?.play() + }, queue: Queue.mainQueue()) + self.audioDelayTimer?.start() + } else { + self.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) + self.audioPlayer?.play() + } + self.onPlaybackAction(.play) } } @@ -758,6 +784,7 @@ public final class MediaEditor { } player.pause() self.additionalPlayer?.pause() + self.audioPlayer?.pause() let targetPosition = CMTime(seconds: position, preferredTimescale: CMTimeScale(1000.0)) player.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero, completionHandler: { _ in @@ -767,14 +794,49 @@ public final class MediaEditor { }) self.additionalPlayer?.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero) - self.audioPlayer?.seek(to: self.audioTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) + + if let _ = self.audioDelay(for: targetPosition) { + + } else { + self.audioPlayer?.seek(to: self.audioTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) + } + } + + private func setupAudioPlayback() { + + } + + private var audioDelayTimer: SwiftSignalKit.Timer? + private func audioDelay(for time: CMTime) -> Double? { + var time = time + if time == .invalid { + time = .zero + } + let videoStart = self.values.videoTrimRange?.lowerBound ?? 0.0 + let audioStart = self.values.audioTrackTrimRange?.lowerBound ?? 0.0 + if audioStart - videoStart > 0.0 { + let delay = audioStart - time.seconds + if delay > 0 { + return delay + } + } + return nil } private func audioTime(for time: CMTime) -> CMTime { - let time = time.seconds - let offsettedTime = time - (self.values.videoTrimRange?.lowerBound ?? 0.0) + (self.values.audioTrackTrimRange?.lowerBound ?? 0.0) - (self.values.audioTrackStart ?? 0.0) + var time = time + if time == .invalid { + time = .zero + } + let seconds = time.seconds - return CMTime(seconds: offsettedTime, preferredTimescale: CMTimeScale(1000.0)) + let audioOffset = self.values.audioTrackOffset ?? 0.0 + let audioStart = self.values.audioTrackTrimRange?.lowerBound ?? 0.0 + if seconds < audioStart { + return CMTime(seconds: audioOffset + audioStart, preferredTimescale: CMTimeScale(1000.0)) + } else { + return CMTime(seconds: audioOffset + seconds, preferredTimescale: CMTimeScale(1000.0)) + } } public var isPlaying: Bool { @@ -810,22 +872,28 @@ public final class MediaEditor { audioPlayer.setRate(rate, time: itemTime, atHostTime: futureTime) } else { let itemTime = self.player?.currentItem?.currentTime() ?? .invalid - let audioTime: CMTime - if itemTime == .invalid { - audioTime = .invalid - } else { - audioTime = self.audioTime(for: itemTime) - } - + let audioTime = self.audioTime(for: itemTime) + self.player?.setRate(rate, time: itemTime, atHostTime: futureTime) self.additionalPlayer?.setRate(rate, time: itemTime, atHostTime: futureTime) - self.audioPlayer?.setRate(rate, time: audioTime, atHostTime: futureTime) + + if rate > 0.0, let audioDelay = self.audioDelay(for: itemTime) { + self.audioDelayTimer = SwiftSignalKit.Timer(timeout: audioDelay, repeat: false, completion: { [weak self] in + self?.audioPlayer?.setRate(rate, time: audioTime, atHostTime: futureTime) + }, queue: Queue.mainQueue()) + self.audioDelayTimer?.start() + } else { + self.audioPlayer?.setRate(rate, time: audioTime, atHostTime: futureTime) + } } if rate > 0.0 { self.onPlaybackAction(.play) } else { self.onPlaybackAction(.pause) + + self.audioDelayTimer?.invalidate() + self.audioDelayTimer = nil } } @@ -835,6 +903,9 @@ public final class MediaEditor { self.audioPlayer?.pause() self.onPlaybackAction(.pause) self.renderer.textureSource?.invalidate() + + self.audioDelayTimer?.invalidate() + self.audioDelayTimer = nil } private func updateVideoTimePosition() { @@ -867,7 +938,11 @@ public final class MediaEditor { }) self.additionalPlayer?.seek(to: targetPosition, toleranceBefore: .zero, toleranceAfter: .zero) - self.audioPlayer?.seek(to: self.audioTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) + + if let _ = self.audioDelay(for: targetPosition) { + } else { + self.audioPlayer?.seek(to: self.audioTime(for: targetPosition), toleranceBefore: .zero, toleranceAfter: .zero) + } } self.onPlaybackAction(.seek(targetPosition.seconds)) } @@ -909,11 +984,11 @@ public final class MediaEditor { public func setAudioTrack(_ audioTrack: MediaAudioTrack?) { self.updateValues(mode: .skipRendering) { values in - return values.withUpdatedAudioTrack(audioTrack).withUpdatedAudioTrackSamples(nil).withUpdatedAudioTrackTrimRange(nil).withUpdatedAudioTrackVolume(nil).withUpdatedAudioTrackStart(nil) + return values.withUpdatedAudioTrack(audioTrack).withUpdatedAudioTrackSamples(nil).withUpdatedAudioTrackTrimRange(nil).withUpdatedAudioTrackVolume(nil).withUpdatedAudioTrackOffset(nil) } if let audioTrack { - let path = fullDraftPath(engine: self.context.engine, path: audioTrack.path) + let path = fullDraftPath(peerId: self.context.account.peerId, path: audioTrack.path) let audioAsset = AVURLAsset(url: URL(fileURLWithPath: path)) let playerItem = AVPlayerItem(asset: audioAsset) let player = AVPlayer(playerItem: playerItem) @@ -930,6 +1005,9 @@ public final class MediaEditor { audioPlayer.pause() self.audioPlayer = nil + self.audioDelayTimer?.invalidate() + self.audioDelayTimer = nil + if !self.sourceIsVideo { self.playerPromise.set(.single(nil)) } @@ -942,13 +1020,25 @@ public final class MediaEditor { } if apply, let trimRange { - self.audioPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) + let offset = self.values.audioTrackOffset ?? 0.0 + self.audioPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: offset + trimRange.upperBound, preferredTimescale: CMTimeScale(1000)) } } - public func setAudioTrackStart(_ start: Double?) { + public func setAudioTrackOffset(_ offset: Double?, apply: Bool) { self.updateValues(mode: .skipRendering) { values in - return values.withUpdatedAudioTrackStart(start) + return values.withUpdatedAudioTrackOffset(offset) + } + + if apply { + let offset = offset ?? 0.0 + let duration = self.duration ?? 0.0 + let upperBound = self.values.audioTrackTrimRange?.upperBound ?? duration + + let time = self.player?.currentTime() ?? .zero + let audioTime = self.audioTime(for: time) + self.audioPlayer?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: offset + upperBound, preferredTimescale: CMTimeScale(1000)) + self.audioPlayer?.seek(to: audioTime, toleranceBefore: .zero, toleranceAfter: .zero) } } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift index 87cfead6d3..5d39ae1961 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorDraft.swift @@ -219,7 +219,7 @@ public func addStoryDraft(engine: TelegramEngine, item: MediaEditorDraft) { public func removeStoryDraft(engine: TelegramEngine, path: String, delete: Bool) { if delete { - try? FileManager.default.removeItem(atPath: fullDraftPath(engine: engine, path: path)) + try? FileManager.default.removeItem(atPath: fullDraftPath(peerId: engine.account.peerId, path: path)) } let itemId = MediaEditorDraftItemId(path.persistentHashValue) let _ = engine.orderedLists.removeItem(collectionId: ApplicationSpecificOrderedItemListCollectionId.storyDrafts, id: itemId.rawValue).start() @@ -270,10 +270,10 @@ public func updateStoryDrafts(engine: TelegramEngine) { public extension MediaEditorDraft { func fullPath(engine: TelegramEngine) -> String { - return fullDraftPath(engine: engine, path: self.path) + return fullDraftPath(peerId: engine.account.peerId, path: self.path) } } -func fullDraftPath(engine: TelegramEngine, path: String) -> String { - return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())/" + path +func fullDraftPath(peerId: EnginePeer.Id, path: String) -> String { + return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(peerId.toInt64())/" + path } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift index f0d75d8d71..5be6c92b9f 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorValues.swift @@ -101,6 +101,9 @@ public struct MediaAudioTrackSamples: Equatable { public final class MediaEditorValues: Codable, Equatable { public static func == (lhs: MediaEditorValues, rhs: MediaEditorValues) -> Bool { + if lhs.peerId != rhs.peerId { + return false + } if lhs.originalDimensions != rhs.originalDimensions { return false } @@ -161,6 +164,12 @@ public final class MediaEditorValues: Codable, Equatable { if lhs.audioTrackTrimRange != rhs.audioTrackTrimRange { return false } + if lhs.audioTrackOffset != rhs.audioTrackOffset { + return false + } + if lhs.audioTrackVolume != rhs.audioTrackVolume { + return false + } if lhs.audioTrackSamples != rhs.audioTrackSamples { return false } @@ -197,6 +206,8 @@ public final class MediaEditorValues: Codable, Equatable { } private enum CodingKeys: String, CodingKey { + case peerId + case originalWidth case originalHeight case cropOffset @@ -224,10 +235,11 @@ public final class MediaEditorValues: Codable, Equatable { case audioTrack case audioTrackTrimRange - case audioTrackStart + case audioTrackOffset case audioTrackVolume } + public let peerId: EnginePeer.Id public let originalDimensions: PixelDimensions public let cropOffset: CGPoint public let cropSize: CGSize? @@ -254,11 +266,12 @@ public final class MediaEditorValues: Codable, Equatable { public let audioTrack: MediaAudioTrack? public let audioTrackTrimRange: Range? - public let audioTrackStart: Double? + public let audioTrackOffset: Double? public let audioTrackVolume: CGFloat? public let audioTrackSamples: MediaAudioTrackSamples? init( + peerId: EnginePeer.Id, originalDimensions: PixelDimensions, cropOffset: CGPoint, cropSize: CGSize?, @@ -280,10 +293,11 @@ public final class MediaEditorValues: Codable, Equatable { toolValues: [EditorToolKey: Any], audioTrack: MediaAudioTrack?, audioTrackTrimRange: Range?, - audioTrackStart: Double?, + audioTrackOffset: Double?, audioTrackVolume: CGFloat?, audioTrackSamples: MediaAudioTrackSamples? ) { + self.peerId = peerId self.originalDimensions = originalDimensions self.cropOffset = cropOffset self.cropSize = cropSize @@ -305,7 +319,7 @@ public final class MediaEditorValues: Codable, Equatable { self.toolValues = toolValues self.audioTrack = audioTrack self.audioTrackTrimRange = audioTrackTrimRange - self.audioTrackStart = audioTrackStart + self.audioTrackOffset = audioTrackOffset self.audioTrackVolume = audioTrackVolume self.audioTrackSamples = audioTrackSamples } @@ -313,6 +327,8 @@ public final class MediaEditorValues: Codable, Equatable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) + self.peerId = EnginePeer.Id(try container.decodeIfPresent(Int64.self, forKey: .peerId) ?? 0) + let width = try container.decode(Int32.self, forKey: .originalWidth) let height = try container.decode(Int32.self, forKey: .originalHeight) self.originalDimensions = PixelDimensions(width: width, height: height) @@ -358,7 +374,7 @@ public final class MediaEditorValues: Codable, Equatable { self.audioTrack = try container.decodeIfPresent(MediaAudioTrack.self, forKey: .audioTrack) self.audioTrackTrimRange = try container.decodeIfPresent(Range.self, forKey: .audioTrackTrimRange) - self.audioTrackStart = try container.decodeIfPresent(Double.self, forKey: .audioTrackStart) + self.audioTrackOffset = try container.decodeIfPresent(Double.self, forKey: .audioTrackOffset) self.audioTrackVolume = try container.decodeIfPresent(CGFloat.self, forKey: .audioTrackVolume) self.audioTrackSamples = nil @@ -367,6 +383,8 @@ public final class MediaEditorValues: Codable, Equatable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(self.peerId, forKey: .peerId) + try container.encode(self.originalDimensions.width, forKey: .originalWidth) try container.encode(self.originalDimensions.height, forKey: .originalHeight) @@ -407,73 +425,73 @@ public final class MediaEditorValues: Codable, Equatable { try container.encodeIfPresent(self.audioTrack, forKey: .audioTrack) try container.encodeIfPresent(self.audioTrackTrimRange, forKey: .audioTrackTrimRange) - try container.encodeIfPresent(self.audioTrackStart, forKey: .audioTrackStart) + try container.encodeIfPresent(self.audioTrackOffset, forKey: .audioTrackOffset) try container.encodeIfPresent(self.audioTrackVolume, forKey: .audioTrackVolume) } public func makeCopy() -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: offset, cropSize: self.cropSize, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropSize: self.cropSize, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedGradientColors(gradientColors: [UIColor]) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedVideoIsMuted(_ videoIsMuted: Bool) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedVideoIsFullHd(_ videoIsFullHd: Bool) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedVideoIsMirrored(_ videoIsMirrored: Bool) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedAdditionalVideo(path: String, positionChanges: [VideoPositionChange]) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: path, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: path, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedAdditionalVideo(position: CGPoint, scale: CGFloat, rotation: CGFloat) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedVideoTrimRange(_ videoTrimRange: Range) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedDrawingAndEntities(drawing: UIImage?, entities: [CodableDrawingEntity]) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: drawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: drawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedToolValues(_ toolValues: [EditorToolKey: Any]) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedAudioTrack(_ audioTrack: MediaAudioTrack?) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedAudioTrackTrimRange(_ audioTrackTrimRange: Range?) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } - func withUpdatedAudioTrackStart(_ audioTrackStart: Double?) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + func withUpdatedAudioTrackOffset(_ audioTrackOffset: Double?) -> MediaEditorValues { + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedAudioTrackVolume(_ audioTrackVolume: CGFloat?) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples) } func withUpdatedAudioTrackSamples(_ audioTrackSamples: MediaAudioTrackSamples?) -> MediaEditorValues { - return MediaEditorValues(originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackStart: self.audioTrackStart, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples) + return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropSize: self.cropSize, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, additionalVideoPath: self.additionalVideoPath, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, drawing: self.drawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples) } public var resultDimensions: PixelDimensions { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift index 63c713f057..7a1d5bd4a1 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorVideoExport.swift @@ -221,13 +221,25 @@ public final class MediaEditorVideoExport { } var audioTimeRange: CMTimeRange? { - if let audioTrimRange = self.values.audioTrackTrimRange { - return CMTimeRange(start: CMTime(seconds: audioTrimRange.lowerBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), end: CMTime(seconds: audioTrimRange.upperBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC))) + let offset = self.values.audioTrackOffset ?? 0.0 + if let range = self.values.audioTrackTrimRange { + return CMTimeRange( + start: CMTime(seconds: offset + range.lowerBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC)), + end: CMTime(seconds: offset + range.upperBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + ) } else { return nil } } + var audioStartTime: CMTime { + if let range = self.values.audioTrackTrimRange { + return CMTime(seconds: range.lowerBound, preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + } else { + return .zero + } + } + var composerDimensions: CGSize { return CGSize(width: 1080.0, height: 1920.0) } @@ -372,7 +384,8 @@ public final class MediaEditorVideoExport { var inputAsset = asset if let audioData = self.configuration.values.audioTrack { let mixComposition = AVMutableComposition() - let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioData.path)) + let audioPath = fullDraftPath(peerId: self.configuration.values.peerId, path: audioData.path) + let audioAsset = AVURLAsset(url: URL(fileURLWithPath: audioPath)) guard let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid), @@ -397,7 +410,7 @@ public final class MediaEditorVideoExport { if let audioTrackRange = self.configuration.audioTimeRange { musicRange = audioTrackRange } - try? musicTrack.insertTimeRange(musicRange, of: musicAssetTrack, at: CMTime(seconds: self.configuration.values.audioTrackStart ?? 0.0, preferredTimescale: CMTimeScale(NSEC_PER_SEC))) + try? musicTrack.insertTimeRange(musicRange, of: musicAssetTrack, at: self.configuration.audioStartTime) inputAsset = mixComposition } diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 373086ff54..b68950fd33 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -916,7 +916,7 @@ final class MediaEditorScreenComponent: Component { var audioData: VideoScrubberComponent.AudioData? if let audioTrack = mediaEditor?.values.audioTrack { let trimRange = mediaEditor?.values.audioTrackTrimRange - let offset = mediaEditor?.values.audioTrackStart + let offset = mediaEditor?.values.audioTrackOffset let audioSamples = mediaEditor?.values.audioTrackSamples audioData = VideoScrubberComponent.AudioData( artist: audioTrack.artist, @@ -1318,8 +1318,26 @@ final class MediaEditorScreenComponent: Component { audioTrimUpdated: { [weak mediaEditor] start, end, _, done in if let mediaEditor { mediaEditor.setAudioTrackTrimRange(start.. String { return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())" } -private func fullDraftPath(engine: TelegramEngine, path: String) -> String { - return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(engine.account.peerId.toInt64())/" + path +private func fullDraftPath(peerId: EnginePeer.Id, path: String) -> String { + return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/storyDrafts_\(peerId.toInt64())/" + path } func hasFirstResponder(_ view: UIView) -> Bool { diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift index ab6de5a3e3..1f24abd3f9 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/VideoScrubberComponent.swift @@ -69,6 +69,7 @@ final class VideoScrubberComponent: Component { let videoTrimUpdated: (Double, Double, Bool, Bool) -> Void let positionUpdated: (Double, Bool) -> Void let audioTrimUpdated: (Double, Double, Bool, Bool) -> Void + let audioOffsetUpdated: (Double, Bool) -> Void let audioLongPressed: ((UIView) -> Void)? init( @@ -88,6 +89,7 @@ final class VideoScrubberComponent: Component { videoTrimUpdated: @escaping (Double, Double, Bool, Bool) -> Void, positionUpdated: @escaping (Double, Bool) -> Void, audioTrimUpdated: @escaping (Double, Double, Bool, Bool) -> Void, + audioOffsetUpdated: @escaping (Double, Bool) -> Void, audioLongPressed: ((UIView) -> Void)? ) { self.context = context @@ -106,6 +108,7 @@ final class VideoScrubberComponent: Component { self.videoTrimUpdated = videoTrimUpdated self.positionUpdated = positionUpdated self.audioTrimUpdated = audioTrimUpdated + self.audioOffsetUpdated = audioOffsetUpdated self.audioLongPressed = audioLongPressed } @@ -149,7 +152,7 @@ final class VideoScrubberComponent: Component { return true } - final class View: UIView, UIGestureRecognizerDelegate{ + final class View: UIView, UIGestureRecognizerDelegate, UIScrollViewDelegate { private let audioClippingView: UIView private let audioScrollView: UIScrollView private let audioContainerView: UIView @@ -187,6 +190,7 @@ final class VideoScrubberComponent: Component { override init(frame: CGRect) { self.audioScrollView = UIScrollView() + self.audioScrollView.bounces = false self.audioScrollView.decelerationRate = .fast self.audioScrollView.clipsToBounds = false self.audioScrollView.showsHorizontalScrollIndicator = false @@ -223,6 +227,8 @@ final class VideoScrubberComponent: Component { self.clipsToBounds = false + self.audioScrollView.delegate = self + self.disablesInteractiveModalDismiss = true self.disablesInteractiveKeyboardGestureRecognizer = true @@ -326,6 +332,29 @@ final class VideoScrubberComponent: Component { return self.audioContainerView.bounds.contains(location) } + private func updateAudioOffset(done: Bool) { + guard self.audioScrollView.contentSize.width > 0.0, let component = self.component, let duration = self.component?.audioData?.duration else { + return + } + let totalWidth = self.audioScrollView.contentSize.width + let offset = self.audioScrollView.contentOffset.x * duration / totalWidth + component.audioOffsetUpdated(offset, done) + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + self.updateAudioOffset(done: false) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if !decelerate { + self.updateAudioOffset(done: true) + } + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + self.updateAudioOffset(done: true) + } + @objc private func longPressed(_ gestureRecognizer: UILongPressGestureRecognizer) { guard let component = self.component, component.audioData != nil, case .began = gestureRecognizer.state else { return