Swift Library for Open Spherical Camera API

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


2 Likes