Swiftgram/submodules/TelegramUI/Sources/MultiplexedSoftwareVideoSourceManager.swift
2020-02-20 18:08:36 +04:00

119 lines
4.4 KiB
Swift

import Foundation
import UIKit
import Postbox
import TelegramCore
import SyncCore
import SwiftSignalKit
import CoreMedia
import UniversalMediaPlayer
private final class RunningSoftwareVideoSource {
let fetchDisposable: Disposable
let fetchStatusDisposable: Disposable
var source: SoftwareVideoSource?
var beginTime: Double?
var frame: MediaTrackFrame?
init(fetchDisposable: Disposable, fetchStatusDisposable: Disposable) {
self.fetchDisposable = fetchDisposable
self.fetchStatusDisposable = fetchStatusDisposable
}
deinit {
self.fetchDisposable.dispose()
self.fetchStatusDisposable.dispose()
}
}
final class MultiplexedSoftwareVideoSourceManager {
private let queue: Queue
private let account: Account
private var videoSources: [MediaId: RunningSoftwareVideoSource] = [:]
private(set) var immediateVideoFrames: [MediaId: MediaTrackFrame] = [:]
private var updatingAt: Double?
var updateFrame: ((MediaId, MediaTrackFrame) -> Void)?
init(queue: Queue, account: Account) {
self.queue = queue
self.account = account
}
func updateVisibleItems(_ media: [TelegramMediaFile]) {
self.queue.async {
var dict: [MediaId: TelegramMediaFile] = [:]
for file in media {
dict[file.fileId] = file
}
var removeIds: [MediaId] = []
for id in self.videoSources.keys {
if dict[id] == nil {
removeIds.append(id)
}
}
for id in removeIds {
self.videoSources.removeValue(forKey: id)
}
for (id, file) in dict {
if self.videoSources[id] == nil {
self.videoSources[id] = RunningSoftwareVideoSource(fetchDisposable: (self.account.postbox.mediaBox.resourceData(file.resource) |> deliverOn(self.queue)).start(next: { [weak self] data in
if let strongSelf = self, let context = strongSelf.videoSources[id] {
if data.complete {
context.source = SoftwareVideoSource(path: data.path)
}
}
}), fetchStatusDisposable: fetchedMediaResource(mediaBox: self.account.postbox.mediaBox, reference: AnyMediaReference.standalone(media: file).resourceReference(file.resource)).start())
}
}
}
}
func update(at timestamp: Double) {
assert(Queue.mainQueue().isCurrent())
let begin = self.updatingAt == nil
self.updatingAt = timestamp
if begin {
self.queue.async {
var immediateVideoFrames: [MediaId: MediaTrackFrame] = [:]
loop: for (id, source) in self.videoSources {
if let context = source.source {
if let beginTime = source.beginTime, let currentFrame = source.frame {
let framePosition = currentFrame.position.seconds
let frameDuration = currentFrame.duration.seconds
if false && beginTime + framePosition + frameDuration > timestamp {
immediateVideoFrames[id] = currentFrame
continue loop
}
}
/*if let frame = context.readFrame(maxPts: nil) {
if source.frame == nil || CMTimeCompare(source.frame!.position, frame.position) > 0 {
source.beginTime = timestamp
}
source.frame = frame
immediateVideoFrames[id] = frame
self.updateFrame?(id, frame)
}*/
}
}
Queue.mainQueue().async {
self.immediateVideoFrames = immediateVideoFrames
if let updatingAt = self.updatingAt, !updatingAt.isEqual(to: timestamp) {
self.updatingAt = nil
self.update(at: updatingAt)
} else {
self.updatingAt = nil
}
}
}
}
}
}