diff --git a/Sources/LiveKit/Broadcast/Uploader/SampleUploader.swift b/Sources/LiveKit/Broadcast/Uploader/SampleUploader.swift index f499f6360..df1e1223b 100644 --- a/Sources/LiveKit/Broadcast/Uploader/SampleUploader.swift +++ b/Sources/LiveKit/Broadcast/Uploader/SampleUploader.swift @@ -28,6 +28,7 @@ private enum Constants { class SampleUploader { private static var imageContext = CIContext(options: nil) + private static var colorSpace = CGColorSpaceCreateDeviceRGB() @Atomic private var isReady = false private var connection: BroadcastUploadSocketConnection @@ -37,6 +38,9 @@ class SampleUploader { private let serialQueue: DispatchQueue + // Configure desired compression quality (0.0 = max compression, 1.0 = least compression) + public let compressionQuality: CGFloat = 1.0 + init(connection: BroadcastUploadSocketConnection) { self.connection = connection serialQueue = DispatchQueue(label: "io.livekit.broadcast.sampleUploader") @@ -115,14 +119,12 @@ private extension SampleUploader { CVPixelBufferLockBaseAddress(imageBuffer, .readOnly) - let scaleFactor = 1.0 - let width = CVPixelBufferGetWidth(imageBuffer) / Int(scaleFactor) - let height = CVPixelBufferGetHeight(imageBuffer) / Int(scaleFactor) + let width = CVPixelBufferGetWidth(imageBuffer) + let height = CVPixelBufferGetHeight(imageBuffer) let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0 - let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0 / scaleFactor), y: CGFloat(1.0 / scaleFactor)) - let bufferData = jpegData(from: imageBuffer, scale: scaleTransform) + let bufferData = jpegData(from: imageBuffer) CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) @@ -144,16 +146,35 @@ private extension SampleUploader { return serializedMessage } - func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? { - let image = CIImage(cvPixelBuffer: buffer).transformed(by: scaleTransform) + func jpegData(from buffer: CVPixelBuffer) -> Data? { + let image = CIImage(cvPixelBuffer: buffer) - guard let colorSpace = image.colorSpace else { - return nil - } + if #available(iOS 17.0, *) { + return Self.imageContext.jpegRepresentation( + of: image, + colorSpace: Self.colorSpace, + options: [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: compressionQuality] + ) + } else { + // Workaround for "unsupported file format 'public.heic'" + guard let cgImage = Self.imageContext.createCGImage(image, from: image.extent) else { + return nil + } - let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0] + let data = NSMutableData() + guard let imageDestination = CGImageDestinationCreateWithData(data, AVFileType.jpg as CFString, 1, nil) else { + return nil + } + + let options: [CFString: Any] = [kCGImageDestinationLossyCompressionQuality: compressionQuality] + CGImageDestinationAddImage(imageDestination, cgImage, options as CFDictionary) - return SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options) + guard CGImageDestinationFinalize(imageDestination) else { + return nil + } + + return data as Data + } } } diff --git a/Sources/LiveKit/Types/Participant+Types.swift b/Sources/LiveKit/Types/Participant+Types.swift index d949ff700..9e6b7caac 100644 --- a/Sources/LiveKit/Types/Participant+Types.swift +++ b/Sources/LiveKit/Types/Participant+Types.swift @@ -47,7 +47,7 @@ public extension Participant { @objc public let stringValue: String - init(from stringValue: String) { + public init(from stringValue: String) { self.stringValue = stringValue }