"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 }