import Foundation import UIKit import AVFoundation import SwiftSignalKit extension AVPlayer { func fadeVolume(from: Float, to: Float, duration: Float, completion: (() -> Void)? = nil) -> SwiftSignalKit.Timer? { self.volume = from guard from != to else { return nil } let interval: Float = 0.1 let range = to - from let step = (range * interval) / duration func reachedTarget() -> Bool { guard self.volume >= 0, self.volume <= 1 else { self.volume = to return true } if to > from { return self.volume >= to } return self.volume <= to } var invalidateImpl: (() -> Void)? let timer = SwiftSignalKit.Timer(timeout: Double(interval), repeat: true, completion: { [weak self] in if let self, !reachedTarget() { self.volume += step } else { invalidateImpl?() completion?() } }, queue: Queue.mainQueue()) invalidateImpl = { [weak timer] in timer?.invalidate() } timer.start() return timer } } func textureRotatonForAVAsset(_ asset: AVAsset, mirror: Bool = false) -> TextureRotation { for track in asset.tracks { if track.mediaType == .video { let t = track.preferredTransform if t.a == -1.0 && t.d == -1.0 { return .rotate180Degrees } else if t.a == 1.0 && t.d == 1.0 { return .rotate0Degrees } else if t.b == -1.0 && t.c == 1.0 { return .rotate270Degrees } else if t.a == -1.0 && t.d == 1.0 { return .rotate270Degrees } else if t.a == 1.0 && t.d == -1.0 { return .rotate180Degrees } else { return mirror ? .rotate90DegreesMirrored : .rotate90Degrees } } } return .rotate0Degrees } func loadTexture(image: UIImage, device: MTLDevice) -> MTLTexture? { func dataForImage(_ image: UIImage) -> UnsafeMutablePointer { let imageRef = image.cgImage let width = Int(image.size.width) let height = Int(image.size.height) let colorSpace = CGColorSpaceCreateDeviceRGB() let rawData = UnsafeMutablePointer.allocate(capacity: width * height * 4) let bytePerPixel = 4 let bytesPerRow = bytePerPixel * Int(width) let bitsPerComponent = 8 let bitmapInfo = CGBitmapInfo.byteOrder32Little.rawValue + CGImageAlphaInfo.premultipliedFirst.rawValue let context = CGContext.init(data: rawData, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) context?.draw(imageRef!, in: CGRect(x: 0, y: 0, width: width, height: height)) return rawData } let width = Int(image.size.width * image.scale) let height = Int(image.size.height * image.scale) let bytePerPixel = 4 let bytesPerRow = bytePerPixel * width var texture : MTLTexture? let region = MTLRegionMake2D(0, 0, Int(width), Int(height)) let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, width: width, height: height, mipmapped: false) texture = device.makeTexture(descriptor: textureDescriptor) let data = dataForImage(image) texture?.replace(region: region, mipmapLevel: 0, withBytes: data, bytesPerRow: bytesPerRow) return texture } func pixelBufferToMTLTexture(pixelBuffer: CVPixelBuffer, textureCache: CVMetalTextureCache) -> MTLTexture? { let width = CVPixelBufferGetWidth(pixelBuffer) let height = CVPixelBufferGetHeight(pixelBuffer) let format: MTLPixelFormat = .r8Unorm var textureRef : CVMetalTexture? let status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache, pixelBuffer, nil, format, width, height, 0, &textureRef) if status == kCVReturnSuccess { return CVMetalTextureGetTexture(textureRef!) } return nil } func getTextureImage(device: MTLDevice, texture: MTLTexture, mirror: Bool = false) -> UIImage? { let colorSpace = CGColorSpaceCreateDeviceRGB() let context = CIContext(mtlDevice: device, options: [:]) guard var ciImage = CIImage(mtlTexture: texture, options: [.colorSpace: colorSpace]) else { return nil } let transform: CGAffineTransform if mirror { transform = CGAffineTransform(-1.0, 0.0, 0.0, -1.0, ciImage.extent.width, ciImage.extent.height) } else { transform = CGAffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, ciImage.extent.height) } ciImage = ciImage.transformed(by: transform) guard let cgImage = context.createCGImage(ciImage, from: CGRect(origin: .zero, size: CGSize(width: ciImage.extent.width, height: ciImage.extent.height))) else { return nil } return UIImage(cgImage: cgImage) }