From f93aa6a3dc9efd5457e205cd54fdbc37c513a179 Mon Sep 17 00:00:00 2001 From: Gaston Dombiak Date: Sat, 12 Feb 2022 08:45:21 -0800 Subject: [PATCH] Render TSD camera in ATV. No countdown implemented yet to indicate when camera will refresh again Issue #564 --- OctoPod TV/CameraService.swift | 42 ++++++++++++++++++++++++++++++++++ OctoPod/CameraUtils.swift | 11 ++++++++- OctoPod/UIUtils.swift | 6 ++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/OctoPod TV/CameraService.swift b/OctoPod TV/CameraService.swift index 8ea831cc..125a4ba8 100644 --- a/OctoPod TV/CameraService.swift +++ b/OctoPod TV/CameraService.swift @@ -35,6 +35,10 @@ class CameraService: ObservableObject { private var preemptiveAuthentication: Bool = false private var isStreamPathFromSettings: Bool = true + /// Timer to fetch new image when using The Spaghetti Detective + private var tsdTimer: Timer? + private var tsdCountdown = 0 + /// Render next camera func renderNext() { renderCamera(index: cameraIndex + 1) @@ -64,6 +68,7 @@ class CameraService: ObservableObject { func disconnectFromServer() { streamingController?.stop() + tsdTimer?.invalidate() player?.pause() detailedPlayer?.pause() playing = false @@ -180,6 +185,24 @@ class CameraService: ObservableObject { // Disable volume by default detailedPlayer?.isMuted = true } + + fileprivate func renderTSDImage(image: UIImage?, error: String?) { + if let receivedImage = image { + DispatchQueue.main.async { + // Hide error messages since an image will be rendered (so that means that it worked!) + self.errorMessage = nil + // Render image + self.image = receivedImage + self.imageRatio = receivedImage.size.height / receivedImage.size.width + } + } else if let message = error { + DispatchQueue.main.async { + self.image = nil + // Display error messages + self.errorMessage = message + } + } + } /// Stops rendering any previous URL and starts rendering the requested camera /// Needs to be called from main thread @@ -193,6 +216,7 @@ class CameraService: ObservableObject { // Stop any video since it will be replaced with a new one streamingController?.stop() + tsdTimer?.invalidate() player?.pause() detailedPlayer?.pause() @@ -211,8 +235,26 @@ class CameraService: ObservableObject { player!.play() } + } else if CameraUtils.shared.isTLS(cameraURL: url) { + tsdTimer = Timer(fire: Date(), interval: 1, repeats: true, block: { (timer: Timer) in + if self.tsdCountdown == 0 { + // We need to make call Webcam snapshot API to then fetch Image from returned URL + CameraUtils.shared.renderImage(cameraURL: url, imageOrientation: imageOrientation, username: self.username, password: self.password, preemptive: true, timeoutInterval: 5.0) { (image: UIImage?, error: String?) in + self.renderTSDImage(image: image, error: error) + } + // Reset counter to 10 seconds since image changes every 10 seconds + self.tsdCountdown = 10 + } else { + self.tsdCountdown -= 1 + } +// DispatchQueue.main.async { +// self.countdownProgressView.showProgress(percent: Float(self.tsdCountdown * 10)) +// } + }) + RunLoop.main.add(tsdTimer!, forMode: .common) } else { // Clean up any previous HLS config + tsdTimer?.invalidate() player = nil detailedPlayer = nil prepareForMJPEGRendering() diff --git a/OctoPod/CameraUtils.swift b/OctoPod/CameraUtils.swift index 8e6a2aaf..fc75ea5a 100644 --- a/OctoPod/CameraUtils.swift +++ b/OctoPod/CameraUtils.swift @@ -17,7 +17,7 @@ class CameraUtils { if isHLS(url: cameraURL.absoluteString) { // Render image from HLS camera renderHLSImage(cameraURL: cameraURL, imageOrientation: imageOrientation, username: username, password: password, completion: completion) - } else if let host = cameraURL.host, host.hasSuffix("thespaghettidetective.com") { + } else if isTLS(cameraURL: cameraURL) { // The Spaghetti Detective has its own special logic renderTLSImage(cameraURL: cameraURL, imageOrientation: imageOrientation, username: username, password: password, preemptive: preemptive, timeoutInterval: timeoutInterval, completion: completion) } else { @@ -26,10 +26,19 @@ class CameraUtils { } } + /// Returns true if camera URL is rendering HLS videos func isHLS(url: String) -> Bool { return url.hasSuffix(".m3u8") } + /// Returns true if camera URL is hosted via The Spaghetti Detective + func isTLS(cameraURL: URL) -> Bool { + if let host = cameraURL.host, host.hasSuffix("thespaghettidetective.com") { + return true + } + return false + } + func absoluteURL(hostname: String, streamUrl: String) -> String { if streamUrl.isEmpty { // Should never happen but let's be cautious diff --git a/OctoPod/UIUtils.swift b/OctoPod/UIUtils.swift index 9f71d312..9c6ac4fa 100644 --- a/OctoPod/UIUtils.swift +++ b/OctoPod/UIUtils.swift @@ -1,7 +1,9 @@ import Foundation import UIKit import AVKit -import SafariServices +#if !os(tvOS) + import SafariServices +#endif class UIUtils { @@ -29,10 +31,12 @@ class UIUtils { // Execute done block on dismiss done?() })) + #if !os(tvOS) alert.addAction(UIAlertAction(title: NSLocalizedString("Learn More", comment: ""), style: .default, handler: { (UIAlertAction) -> Void in let vc = SFSafariViewController(url: moreURL) presenter.present(vc, animated: false, completion: done) })) + #endif presenter.present(alert, animated: true) { () -> Void in // Nothing to do here }