diff --git a/CHANGELOG.md b/CHANGELOG.md index da205bc0..28a5fe76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The connectors will now be additionally published to a [THEOplayer hosted Cocoapods spec repo](https://github.com/THEOplayer/cocoapods-specs). We will continue publishing to the main trunk until EOL. For more info, please check [the SDK changelog](https://optiview.dolby.com/docs/theoplayer/changelog/#-1100-20260416). +### Fixed + +- SideloadedSubtitle + - Fixed an issue where switching between sideloaded subtitle tracks would get stuck after the first switch due to cached subtitle responses. + ### Removed - Dropped support for iOS/tvOS 13 & 14. diff --git a/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/AVSubtitlesLoader.swift b/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/AVSubtitlesLoader.swift index 0e9d3378..a552d0d5 100644 --- a/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/AVSubtitlesLoader.swift +++ b/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/AVSubtitlesLoader.swift @@ -86,7 +86,7 @@ class AVSubtitlesLoader: NSObject { let timestamp: SSTextTrackDescription.WebVttTimestamp? = (trackDescription as? SSTextTrackDescription)?.vttTimestamp let autosync: Bool? = (trackDescription as? SSTextTrackDescription)?.automaticTimestampSyncEnabled let subtitlesMediaURL: String - if (timestamp?.localTime == nil && timestamp?.pts == nil && format == .WebVTT && autosync == nil) { + if (timestamp?.localTime == nil && timestamp?.pts == nil && format == .WebVTT && autosync != true) { subtitlesMediaURL = originalURL.absoluteString } else { subtitlesMediaURL = self.transformer.composeTranformationUrl(with: originalURL.absoluteString, format: format, timestamp: timestamp) diff --git a/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesSynchronizer.swift b/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesSynchronizer.swift index 345bdd4b..3ee66cf2 100644 --- a/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesSynchronizer.swift +++ b/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesSynchronizer.swift @@ -10,7 +10,7 @@ import AVFoundation @_spi(WebVTT) import THEOplayerSDK protocol SubtitlesSynchronizerDelegate: AnyObject { - func didUpdateTimestamp(timestamp: SSTextTrackDescription.WebVttTimestamp) + func didUpdateTimestamp(timestamp: SSTextTrackDescription.WebVttTimestamp, forContentUrl contentUrl: String) } class SubtitlesSynchronizer { @@ -79,7 +79,7 @@ class SubtitlesSynchronizer { let pts: String = .init(delta * 90000) let localTime: String = textTrackDescription.vttTimestamp.localTime ?? "00:00:00.000" - self?.delegate?.didUpdateTimestamp(timestamp: .init(pts: pts, localTime: localTime)) + self?.delegate?.didUpdateTimestamp(timestamp: .init(pts: pts, localTime: localTime), forContentUrl: textTrackDescription.src.absoluteString) welf.trackSyncMap[textTrack.label]?.status = .resolving welf.trackSyncMap[textTrack.label]?.mode = mode diff --git a/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesTransformer.swift b/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesTransformer.swift index d4997bcf..055c5093 100644 --- a/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesTransformer.swift +++ b/Code/Sideloaded-TextTracks/Sources/THEOplayerConnectorSideloadedSubtitle/SubtitlesTransformer.swift @@ -17,7 +17,7 @@ class SubtitlesTransformer { private static let PORT_RANGE: Range = 8000..<49151 private var retryAttemptsLeft: Int = 9 private var port: in_port_t - private var parameters: Parameters? + private var parametersMap: [String: Parameters] = [:] init() { self.port = .random(in: Self.PORT_RANGE) @@ -39,16 +39,24 @@ class SubtitlesTransformer { } func composeTranformationUrl(with subtitlesURL: String, format: THEOplayerSDK.TextTrackFormat, timestamp: SSTextTrackDescription.WebVttTimestamp?) -> String { - self.parameters = Parameters(contentUrl: subtitlesURL, format: format, timestamp: timestamp) - let urlComps = URLComponents(string: "http://\(self.host):\(self.port)/\(SubtitlesTransformer.TRANSFORM_ROUTE)")! + self.parametersMap[subtitlesURL] = Parameters(contentUrl: subtitlesURL, format: format, timestamp: timestamp) + var urlComps = URLComponents(string: "http://\(self.host):\(self.port)/\(SubtitlesTransformer.TRANSFORM_ROUTE)")! + let timestamp = String(Int(Date().timeIntervalSince1970 * 1000)) + urlComps.queryItems = [URLQueryItem(name: "url", value: subtitlesURL), URLQueryItem(name: "t", value: timestamp)] return urlComps.url?.absoluteString ?? subtitlesURL } private func setupServerRoutes() { let handler: (HttpRequest) -> HttpResponse = { req in // Always return with HttpResponse.ok to fail gracefully. Otherwise player will stall. - guard let contentURLString: String = self.parameters?.contentUrl, - let decodedContentUrlString: String = contentURLString.removingPercentEncoding, + guard let subtitlesURLParam: String = req.queryParams.first(where: { $0.0 == "url" })?.1, + let parameters: Parameters = self.parametersMap[subtitlesURLParam.removingPercentEncoding ?? subtitlesURLParam] else { + let errorMessage: String = "Missing subtitle content URL." + print("[AVSubtitlesLoader] ERROR: \(errorMessage)") + return HttpResponse.ok(.text(errorMessage)) + } + + guard let decodedContentUrlString: String = parameters.contentUrl.removingPercentEncoding, let contentUrl: URL = URL(string: decodedContentUrlString) else { let errorMessage: String = "Missing subtitle content URL." print("[AVSubtitlesLoader] ERROR: \(errorMessage)") @@ -70,7 +78,7 @@ class SubtitlesTransformer { var contentString: String = _contentString.replacingOccurrences(of: "\r\n", with: "\n") // SRT to VTT - if self.parameters?.format == THEOplayerSDK.TextTrackFormat.SRT { + if parameters.format == THEOplayerSDK.TextTrackFormat.SRT { do { let subtitles: Subtitles = try Subtitles.Coder.SRT().decode(contentString) contentString = try Subtitles.Coder.VTT().encode(subtitles: subtitles) @@ -81,8 +89,8 @@ class SubtitlesTransformer { } // Add/replace VTT timestamp - if let timestampPts: String = self.parameters?.timestamp?.pts, - let timestampLocalTime: String = self.parameters?.timestamp?.localTime { + if let timestampPts: String = parameters.timestamp?.pts, + let timestampLocalTime: String = parameters.timestamp?.localTime { // if timestamp exists replace it, else add if let modifiedString: String = TimestampStringUtils.overrideTimestamp(in: contentString, with: (timestampPts, timestampLocalTime)) { contentString = modifiedString @@ -118,7 +126,7 @@ class SubtitlesTransformer { } private func reset() { - self.parameters = nil + self.parametersMap.removeAll() } deinit { @@ -128,7 +136,7 @@ class SubtitlesTransformer { } extension SubtitlesTransformer: SubtitlesSynchronizerDelegate { - func didUpdateTimestamp(timestamp: SSTextTrackDescription.WebVttTimestamp) { - self.parameters?.timestamp = timestamp + func didUpdateTimestamp(timestamp: SSTextTrackDescription.WebVttTimestamp, forContentUrl contentUrl: String) { + self.parametersMap[contentUrl]?.timestamp = timestamp } }