I found the problem of why my ipad swift program had a very laggy ‘live preview’.
In this swift library, the ‘live preview’ is calculated in the file /OpenSphericalCamera/LivePreviewDelegate.swift. The problem is that we load up about 50 data segments, and form an array of 80 thousand bytes, and each time we append a segment, the function parses the whole array TWO times.
long story short, LivePreviewDelegate evaluates about 1 million bytes. The solution is to check the first 2 bytes and the last 2 bytes. This approach lets you do about 100 comparisons, instead of 1 million.
below is the simplified function, removed unused variables, catch block, added comments. FYI in this code i only evaluate frames bigger than 70000 bytes, since my camera streams frames that are about 79000bytes.
class LivePreviewDelegate: NSObject, URLSessionDataDelegate {
var validStart = false
var validEnd = false
let JPEG_SOI: [UInt8] = [0xFF, 0xD8]
let JPEG_EOI: [UInt8] = [0xFF, 0xD9]
let completionHandler: ((Data?, URLResponse?, Error?) -> Void)
var dataBuffer = Data()
var frameCount = 0
var frameTime = 0
init(completionHandler: @escaping ((Data?, URLResponse?, Error?) -> Void)) {
self.completionHandler = completionHandler
}
@objc func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
DispatchQueue.global(qos: .background).sync {
self.dataBuffer.append(data)
// most frames are about 79_000 bytes, so dont evaluate small frames
if self.dataBuffer.count < 70_000{
return
}
// condition to prevent buffer overflow
if self.dataBuffer.count > 1_000_000{
print("360 live frame buffer error, ITS TOO BIG, remove all")
self.dataBuffer.removeAll()
}
// evaluate first 2 and last 2 bytes of 70+k bytes of data
if self.dataBuffer[0] == self.JPEG_SOI[0] &&
self.dataBuffer[1] == self.JPEG_SOI[1] {
validStart = true
}
else{
validStart = false
self.dataBuffer.removeAll()
}
if self.dataBuffer[self.dataBuffer.count - 2] == self.JPEG_EOI[0] &&
self.dataBuffer[self.dataBuffer.count - 1] == self.JPEG_EOI[1] {
validEnd = true
}
// if either start or end of frame are not valid, then frame is not complete yet and return so the next data segment can be appended to the buffer
if !validStart || !validEnd{
return
}
// print("360 frame start: 0x\(String(format:"%X", frameData[0])) end: 0x\(String(format:"%X", frameData[frameData.count - 1])) size: \(frameData.count)")
validStart = false
validEnd = false
self.completionHandler(self.dataBuffer, nil, nil)
// code to confirm start and end bytes
// if self.dataBuffer.count > 0{
// print("360 frame buffer start: 0x\(String(format:"%X", self.dataBuffer[0])) end: 0x\(String(format:"%X", self.dataBuffer[self.dataBuffer.count - 1]))")
// }
// else {
// print("360 frame buffer is empty \(self.dataBuffer.count)")
// }
// code to confirm frame rate is about 30 fps
// let currentTime = Int(NSDate().timeIntervalSince1970)
// if frameTime == currentTime{
// frameCount = frameCount + 1
// }
// else{
// print("360 live frame count: \(frameCount)")
// frameCount = 1
// frameTime = currentTime
// }
//
self.dataBuffer.removeAll()
}
}
@objc func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
session.invalidateAndCancel()
self.completionHandler(nil, nil, error)
}
}