Insta360Develop / CameraSDK-iOS

iOS SDK to control Insta360 cameras.
33 stars 5 forks source link

INSCameraPreviewPlayer freezes after a few seconds with error #18

Closed Nikhiladiga closed 1 year ago

Nikhiladiga commented 1 year ago

Hi, I recently bought a new Insta360 X3 camera and signed up for the SDK. We were able to utilize the Android SDK without any issues but in iOS SDK when we want to see the camera preview, the app freezes after a few seconds. This is the code that we are currently using.

import UIKit
import INSCameraSDK

class Insta360Controller: UIViewController, INSCameraPreviewPlayerDelegate {

private var mediaSession:INSCameraMediaSession?
private var previewPlayer:INSCameraPreviewPlayer?
private var videoEncode:INSVideoEncode?

override func viewDidLoad() {
    super.viewDidLoad()

    videoEncode = INSVideoEncode.H264
    mediaSession = INSCameraMediaSession()

    //Set Navigation bar title
    title = "Camera preview"

    //Setup INSCamera socket
    INSCameraManager.socket().setup()

    //Start sending heartbeats to camera to maintain connection
    INSCameraManager.socket().commandManager.sendHeartbeats(with: nil)

    //Setup INSCamera socket for camera state monitoring
    INSCameraManager.socket().addObserver(self, forKeyPath: "cameraState", options: .new, context: nil)

    //Setup preview player view
    setupCameraPreviewView()

}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if(INSCameraManager.shared().currentCamera != nil) {
        weak var weakSelf = self
        weakSelf?.updateConfiguration()
        weakSelf?.runMediaSession()
    }

}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    INSCameraManager.socket().shutdown()
}

//Setup camera preview player view
private func setupCameraPreviewView(){
    let frame = CGRect(x:0,y:0,width: view.bounds.width,height: view.bounds.height)

    previewPlayer = INSCameraPreviewPlayer(frame: frame, renderType: INSRenderType.sphericalPanoRender)
    previewPlayer?.play(withGyroTimestampAdjust: 30.0)
    previewPlayer?.delegate = self
    view.addSubview(previewPlayer!.renderView)

    mediaSession?.plug(previewPlayer!)

    //Adjust field of view params
    let offset = INSCameraManager.shared().currentCamera?.settings?.mediaOffset
    if (offset != nil) {
        let rawValue = INSLensOffset(offset: offset!).lensType
        if rawValue == INSLensType.oneX2.rawValue ||
            rawValue == INSLensType.oneR577Wide.rawValue ||
            rawValue == INSLensType.oneR283Wide.rawValue {

            previewPlayer?.renderView.enablePanGesture = false
            previewPlayer?.renderView.enablePinchGesture = false

            previewPlayer?.renderView.render.camera?.xFov = 37
            previewPlayer?.renderView.render.camera?.distance = 700
        }
    }
}

//Run media session for camera preview and plug streaming video into preview player
private func runMediaSession() {
    guard INSCameraManager.shared().cameraState == INSCameraState.connected else { return }

    guard let mediaSession else { return }

    weak var weakSelf = self

    if mediaSession.running {
        view.isUserInteractionEnabled = false
        mediaSession.commitChanges() { error in
            if let error {
                print("###commitChanges media session with error: \(error)")
            }
            weakSelf?.view.isUserInteractionEnabled = true
            if let error {
                print("###commitChanges media failed", error.localizedDescription)
            }
        }
    } else {
        view.isUserInteractionEnabled = false
        mediaSession.startRunning() { error in
            if let error {
                print("###start running media session with error: \(error)")
            }
            weakSelf?.view.isUserInteractionEnabled = true
            if error != nil {
                weakSelf!.previewPlayer!.play(withSmoothBuffer: false)
            }
        }
    }
}

//Update media session configuration
func updateConfiguration() {
    // main stream resolution
    mediaSession?.expectedVideoResolution = INSVideoResolution1920x960x30

    // secondary stream resolution
    mediaSession?.expectedVideoResolutionSecondary = INSVideoResolution960x480x30

    // use main stream or secondary stream to preview
    mediaSession?.previewStreamType = INSPreviewStreamType.main

    // audio sample rate
    mediaSession?.expectedAudioSampleRate = INSAudioSampleRate.rate48000Hz

    // preview stream encode
    mediaSession?.videoStreamEncode = INSVideoEncode.H264

    // gyroscope correction mode
    // If you are in panoramic preview, use `INSGyroPlayModeDefault`.
    // If you are in wide angle preview, use `INSGyroPlayModeFootageMotionSmooth`.
    mediaSession?.gyroPlayMode = INSGyroPlayMode.normal

    mediaSession?.expectedVideoResolution = .init(width: Int(view.frame.width), height: Int(view.frame.height), fps: 30)
}

//Start media session as soon as camera is connected
private func startSendingHeartbeats() {
    runMediaSession()
}

//Stop media session as soon as camera is disconnected
private func stopSendingHeartbeats() {
    mediaSession?.stopRunning(completion: nil)
}

//Camera state observer
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if !(object is INSCameraManager) {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }

    if keyPath == "cameraState" {
        let state = INSCameraState(rawValue: (change?[NSKeyValueChangeKey.newKey] as? NSNumber)?.uintValue ?? 0)
        switch state {
        case .found:
            print("Found")
        case .connected:
            print("Camera connected successfully")
            startSendingHeartbeats()
        case .connectFailed:
            print("Failed to connect to camera")
            stopSendingHeartbeats()
        default:
            stopSendingHeartbeats()
        }
    }
}

//Convert (photo or video) resource uri to http url via http tunnel and Wi-Fi socket
func INSHTTPURLForResourceURI(_ uri: String) -> URL? {
    URL(string: uri)
}

//Convert local http url to (photo or video) resource uri
func INSResourceURIFromHTTPURL(_ url: URL) -> String? {
    nil
}

deinit {
    mediaSession?.stopRunning() { error in
        if let error {
            print("stop media session with err: \(error)")
        }
    }
    INSCameraManager.socket().removeObserver(self, forKeyPath: "cameraState")
}

/*
 // MARK: - Navigation

 // In a storyboard-based application, you will often want to do a little preparation before navigation
 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
 // Get the new view controller using segue.destination.
 // Pass the selected object to the new view controller.
 }
 */

}

This message gets printed in the console after a few seconds INSStitchTypeNone!

Please help.

Nikhiladiga commented 1 year ago

I wasn't sending heartbeats to the device properly every 500ms, doing that in a different utility thread has solved this issue.

camhart commented 10 months ago

@Nikhiladiga What version of the SDK(s) are you using here?