"Fossies" - the Fresh Open Source Software Archive

Member "jitsi-meet-7309/ios/app/broadcast-extension/SampleUploader.swift" (31 May 2023, 5355 Bytes) of package /linux/misc/jitsi-meet-7309.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Swift source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * Copyright @ 2021-present 8x8, Inc.
    3  *
    4  * Licensed under the Apache License, Version 2.0 (the "License");
    5  * you may not use this file except in compliance with the License.
    6  * You may obtain a copy of the License at
    7  *
    8  *     http://www.apache.org/licenses/LICENSE-2.0
    9  *
   10  * Unless required by applicable law or agreed to in writing, software
   11  * distributed under the License is distributed on an "AS IS" BASIS,
   12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13  * See the License for the specific language governing permissions and
   14  * limitations under the License.
   15  */
   16 
   17 import Foundation
   18 import ReplayKit
   19 
   20 private enum Constants {
   21     static let bufferMaxLength = 10240
   22 }
   23 
   24 class SampleUploader {
   25     
   26     private static var imageContext = CIContext(options: nil)
   27     
   28     @Atomic private var isReady: Bool = false
   29     private var connection: SocketConnection
   30   
   31     private var dataToSend: Data?
   32     private var byteIndex = 0
   33   
   34     private let serialQueue: DispatchQueue
   35     
   36     init(connection: SocketConnection) {
   37         self.connection = connection
   38         self.serialQueue = DispatchQueue(label: "org.jitsi.meet.broadcast.sampleUploader")
   39       
   40         setupConnection()
   41     }
   42   
   43     @discardableResult func send(sample buffer: CMSampleBuffer) -> Bool {
   44         guard isReady == true else {
   45             return false
   46         }
   47         
   48         isReady = false
   49 
   50         dataToSend = prepare(sample: buffer)
   51         byteIndex = 0
   52 
   53         serialQueue.async { [weak self] in
   54             self?.sendDataChunk()
   55         }
   56         
   57         return true
   58     }
   59 }
   60 
   61 private extension SampleUploader {
   62     
   63     func setupConnection() {
   64         connection.didOpen = { [weak self] in
   65             self?.isReady = true
   66         }
   67         connection.streamHasSpaceAvailable = { [weak self] in
   68             self?.serialQueue.async {
   69                 self?.isReady = !(self?.sendDataChunk() ?? true)
   70             }
   71         }
   72     }
   73     
   74     @discardableResult func sendDataChunk() -> Bool {
   75         guard let dataToSend = dataToSend else {
   76             return false
   77         }
   78       
   79         var bytesLeft = dataToSend.count - byteIndex
   80         var length = bytesLeft > Constants.bufferMaxLength ? Constants.bufferMaxLength : bytesLeft
   81 
   82         length = dataToSend[byteIndex..<(byteIndex + length)].withUnsafeBytes {
   83             guard let ptr = $0.bindMemory(to: UInt8.self).baseAddress else {
   84                 return 0
   85             }
   86           
   87             return connection.writeToStream(buffer: ptr, maxLength: length)
   88         }
   89 
   90         if length > 0 {
   91             byteIndex += length
   92             bytesLeft -= length
   93 
   94             if bytesLeft == 0 {
   95                 self.dataToSend = nil
   96                 byteIndex = 0
   97             }
   98         } else {
   99             print("writeBufferToStream failure")
  100         }
  101       
  102         return true
  103     }
  104     
  105     func prepare(sample buffer: CMSampleBuffer) -> Data? {
  106         guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
  107             print("image buffer not available")
  108             return nil
  109         }
  110         
  111         CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
  112         
  113         let scaleFactor = 2.0
  114         let width = CVPixelBufferGetWidth(imageBuffer)/Int(scaleFactor)
  115         let height = CVPixelBufferGetHeight(imageBuffer)/Int(scaleFactor)
  116         let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0
  117                                     
  118         let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0/scaleFactor), y: CGFloat(1.0/scaleFactor))
  119         let bufferData = self.jpegData(from: imageBuffer, scale: scaleTransform)
  120         
  121         CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
  122         
  123         guard let messageData = bufferData else {
  124             print("corrupted image buffer")
  125             return nil
  126         }
  127               
  128         let httpResponse = CFHTTPMessageCreateResponse(nil, 200, nil, kCFHTTPVersion1_1).takeRetainedValue()
  129         CFHTTPMessageSetHeaderFieldValue(httpResponse, "Content-Length" as CFString, String(messageData.count) as CFString)
  130         CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Width" as CFString, String(width) as CFString)
  131         CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Height" as CFString, String(height) as CFString)
  132         CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Orientation" as CFString, String(orientation) as CFString)
  133         
  134         CFHTTPMessageSetBody(httpResponse, messageData as CFData)
  135         
  136         let serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse)?.takeRetainedValue() as Data?
  137       
  138         return serializedMessage
  139     }
  140     
  141     func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? {
  142         var image = CIImage(cvPixelBuffer: buffer)
  143         image = image.transformed(by: scaleTransform)
  144         
  145         guard let colorSpace = image.colorSpace else {
  146             return nil
  147         }
  148       
  149         let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0]
  150         let imageData = SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options)
  151       
  152         return imageData
  153     }
  154 }