[iOS] Fix External Subtitle Selection (#1445)
* | Type | Internal Subtitles | Internal Audio | External Subtitles | External Audio | |---------------|-----------------|--------------------|----------------|----------------| | Transcode | ✅ | ❌ | ✅ | ❌ | | DirectPlay | ✅ | ✅ | ✅ | ✅* | * WIP - GetMasterHlsVideoPlaylistParameters * WIP * Cleanup unused code. * Cleanup comments. * Remove changes to GetMasterHlsVideoPlaylistParameters * Change to use Max instead of a magic number. * Update MediaStream.swift Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com> * Update MediaStream.swift Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com> * Update MediaStream.swift Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com> * Update MediaStream.swift Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com> * New and Improved. * Ensure we are using the right audio track. --------- Co-authored-by: Ethan Pippin <ethanpippin2343@gmail.com>
This commit is contained in:
parent
1eef7c9ff5
commit
e901317317
|
@ -125,11 +125,12 @@ extension BaseItemDto {
|
|||
let userSession = Container.shared.currentUserSession()!
|
||||
|
||||
let testStartTime = Date()
|
||||
try await userSession.client.send(Paths.getBitrateTestBytes(size: testSize))
|
||||
_ = try await userSession.client.send(Paths.getBitrateTestBytes(size: testSize))
|
||||
let testDuration = Date().timeIntervalSince(testStartTime)
|
||||
let testSizeBits = Double(testSize * 8)
|
||||
let testBitrate = testSizeBits / testDuration
|
||||
|
||||
return Int(testBitrate)
|
||||
/// Exceeding 500 mbps will produce an invalid URL
|
||||
return min(Int(testBitrate), PlaybackBitrate.max.rawValue)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,37 +216,59 @@ extension MediaStream {
|
|||
|
||||
extension [MediaStream] {
|
||||
|
||||
// TODO: explain why adjustment is necessary
|
||||
func adjustExternalSubtitleIndexes(audioStreamCount: Int) -> [MediaStream] {
|
||||
guard allSatisfy({ $0.type == .subtitle }) else { return self }
|
||||
let embeddedSubtitleCount = filter { !($0.isExternal ?? false) }.count
|
||||
/// Adjusts track indexes for a full set of media streams.
|
||||
/// For non-transcode stream types:
|
||||
/// Internal tracks (non-external) are ordered as: Video, Audio, Subtitles, then any others.
|
||||
/// Their relative order within each group is preserved and indexes start at 0.
|
||||
/// For transcode stream type:
|
||||
/// Only the first internal video track and the first internal audio track are included, in that order.
|
||||
/// In both cases, external tracks are appended in their original order with indexes continuing after internal tracks.
|
||||
func adjustedTrackIndexes(for streamType: StreamType, selectedAudioStreamIndex: Int) -> [MediaStream] {
|
||||
let internalTracks = self.filter { !($0.isExternal ?? false) }
|
||||
let externalTracks = self.filter { $0.isExternal ?? false }
|
||||
|
||||
var mediaStreams = self
|
||||
var orderedInternal: [MediaStream] = []
|
||||
|
||||
for (i, mediaStream) in mediaStreams.enumerated() {
|
||||
guard mediaStream.isExternal ?? false else { continue }
|
||||
var copy = mediaStream
|
||||
copy.index = (copy.index ?? 0) + 1 + embeddedSubtitleCount + audioStreamCount
|
||||
let subtitleInternal = internalTracks.filter { $0.type == .subtitle }
|
||||
|
||||
mediaStreams[i] = copy
|
||||
// TODO: Do we need this for other media types? I think movies/shows we only care about video, audio, and subtitles.
|
||||
let otherInternal = internalTracks.filter { $0.type != .video && $0.type != .audio && $0.type != .subtitle }
|
||||
|
||||
if streamType == .transcode {
|
||||
// Only include the first video and first audio track for transcode.
|
||||
let videoInternal = internalTracks.filter { $0.type == .video }
|
||||
let audioInternal = internalTracks.filter { $0.type == .audio }
|
||||
|
||||
if let firstVideo = videoInternal.first {
|
||||
orderedInternal.append(firstVideo)
|
||||
}
|
||||
if let selectedAudio = audioInternal.first(where: { $0.index == selectedAudioStreamIndex }) {
|
||||
orderedInternal.append(selectedAudio)
|
||||
}
|
||||
|
||||
return mediaStreams
|
||||
orderedInternal += subtitleInternal
|
||||
orderedInternal += otherInternal
|
||||
} else {
|
||||
let videoInternal = internalTracks.filter { $0.type == .video }
|
||||
let audioInternal = internalTracks.filter { $0.type == .audio }
|
||||
|
||||
orderedInternal = videoInternal + audioInternal + subtitleInternal + otherInternal
|
||||
}
|
||||
|
||||
// TODO: explain why adjustment is necessary
|
||||
func adjustAudioForExternalSubtitles(externalMediaStreamCount: Int) -> [MediaStream] {
|
||||
guard allSatisfy({ $0.type == .audio }) else { return self }
|
||||
|
||||
var mediaStreams = self
|
||||
|
||||
for (i, mediaStream) in mediaStreams.enumerated() {
|
||||
var copy = mediaStream
|
||||
copy.index = (copy.index ?? 0) - externalMediaStreamCount
|
||||
mediaStreams[i] = copy
|
||||
var newInternalTracks: [MediaStream] = []
|
||||
for (index, var track) in orderedInternal.enumerated() {
|
||||
track.index = index
|
||||
newInternalTracks.append(track)
|
||||
}
|
||||
|
||||
return mediaStreams
|
||||
var newExternalTracks: [MediaStream] = []
|
||||
let startingIndexForExternal = newInternalTracks.count
|
||||
for (offset, var track) in externalTracks.enumerated() {
|
||||
track.index = startingIndexForExternal + offset
|
||||
newExternalTracks.append(track)
|
||||
}
|
||||
|
||||
return newInternalTracks + newExternalTracks
|
||||
}
|
||||
|
||||
var has4KVideo: Bool {
|
||||
|
|
|
@ -34,7 +34,7 @@ extension SubtitleProfile {
|
|||
@ArrayBuilder<SubtitleFormat> containers: () -> [SubtitleFormat]
|
||||
) -> [SubtitleProfile] {
|
||||
containers().map {
|
||||
SubtitleProfile(container: $0.rawValue, method: method)
|
||||
SubtitleProfile(container: nil, format: $0.rawValue, method: method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ class VideoPlayerViewModel: ViewModel {
|
|||
let streamType: StreamType
|
||||
|
||||
var hlsPlaybackURL: URL {
|
||||
|
||||
let parameters = Paths.GetMasterHlsVideoPlaylistParameters(
|
||||
isStatic: true,
|
||||
tag: mediaSource.eTag,
|
||||
|
@ -90,9 +89,11 @@ class VideoPlayerViewModel: ViewModel {
|
|||
item: BaseItemDto,
|
||||
mediaSource: MediaSourceInfo,
|
||||
playSessionID: String,
|
||||
// TODO: Remove?
|
||||
videoStreams: [MediaStream],
|
||||
audioStreams: [MediaStream],
|
||||
subtitleStreams: [MediaStream],
|
||||
// <- End of Potential Remove?
|
||||
selectedAudioStreamIndex: Int,
|
||||
selectedSubtitleStreamIndex: Int,
|
||||
chapters: [ChapterInfo.FullInfo],
|
||||
|
@ -102,11 +103,17 @@ class VideoPlayerViewModel: ViewModel {
|
|||
self.mediaSource = mediaSource
|
||||
self.playSessionID = playSessionID
|
||||
self.playbackURL = playbackURL
|
||||
self.videoStreams = videoStreams
|
||||
self.audioStreams = audioStreams
|
||||
.adjustAudioForExternalSubtitles(externalMediaStreamCount: subtitleStreams.filter { $0.isExternal ?? false }.count)
|
||||
self.subtitleStreams = subtitleStreams
|
||||
.adjustExternalSubtitleIndexes(audioStreamCount: audioStreams.count)
|
||||
|
||||
guard let mediaStreams = mediaSource.mediaStreams else {
|
||||
fatalError("Media source does not have any streams")
|
||||
}
|
||||
|
||||
let adjustedStreams = mediaStreams.adjustedTrackIndexes(for: streamType, selectedAudioStreamIndex: selectedAudioStreamIndex)
|
||||
|
||||
self.videoStreams = adjustedStreams.filter { $0.type == .video }
|
||||
self.audioStreams = adjustedStreams.filter { $0.type == .audio }
|
||||
self.subtitleStreams = adjustedStreams.filter { $0.type == .subtitle }
|
||||
|
||||
self.selectedAudioStreamIndex = selectedAudioStreamIndex
|
||||
self.selectedSubtitleStreamIndex = selectedSubtitleStreamIndex
|
||||
self.chapters = chapters
|
||||
|
|
Loading…
Reference in New Issue