gpt4 book ai didi

AVPlayer with sideloaded subtitles breaking when ",SUBTITLES=\"subs\"" is added to the end of the "#EXT-X-STREAM-INF:" lines(在“#EXT-X-STREAM-INF:”行的末尾添加了“#EXT-X-STREAM-INF:”行的AVPlayer,其侧面加载的字幕在以下情况下中断:“,SUBTITES=\”SUBS\“”)

转载 作者:bug小助手 更新时间:2023-10-25 12:04:32 39 4
gpt4 key购买 nike



I am using a modified version of kanderson-wellbeats' delegate to modify requests as AVKit needs them. All code will be at the bottom of the post. All is well in modifying the requests, making the subtitle playlists, and adding them to the master playlist as they do show up when requesting AVMediaCharacteristic.legible, but as soon as I add the line that adds ",SUBTITLES="subs"" to the end of the "#EXT-X-STREAM-INF:" lines, which should add the subtitles to that video track, it won't play anymore. Removing that part makes it possible to select the subtitle track on the playerItem, but the subtitles do not display on the screen.

我正在使用修改后的kanderson-well beats委托版本来修改AVKit需要的请求。所有代码将在帖子的底部。修改请求、创建字幕播放列表并将它们添加到主播放列表中是很好的,因为它们在请求AVMediaCharacteristic时会显示。但是,只要我在“#ext-X-stream-INF:”行的末尾添加“,Subtiles=”subtiles=“subs”行,它就不会再播放了,这应该会将字幕添加到该视频轨道。移除该部分使得在playerItem上选择字幕轨道成为可能,但是字幕不会显示在屏幕上。


Note that I am using a custom AVPlayer with AVPlayerLayer instead of AVPlayerViewController as I required a lot more customization; maybe that is the issue?

请注意,我使用的是带有AVPlayerLayer的定制AVPlayer,而不是AVPlayerViewController,因为我需要更多的定制;也许这就是问题所在?


I know I could probably just add in an overlay with a UILabel for the subtitles, but I'd rather the solution be more native.

我知道我可能只需要为字幕添加一个带有UILabel的覆盖层,但我更希望解决方案更本地化。


InterceptingAssetResourceLoaderDelegate.swift

Note: in the subtitle playlist here, there is no comma at the end of the "#EXTINF:(rounded)" line. There seems to be no change in adding it or not, but kanderson-wellbeats said that adding it would cause issues, so it has been removed for now.

备注:这里的字幕播放列表中,“#EXTINF:(四舍五入)”行的末尾没有逗号。添加或不添加似乎没有什么变化,但Kanderson-Well Beats表示,添加它会引发问题,因此目前已将其删除。


import AVKit

class InterceptingAssetResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate {
private class SubtitleBundle {
internal init(subtitleDTO: InterceptingAssetResourceLoaderDelegate.SubtitleDTO, playlist: String? = nil) {
self.subtitleDTO = subtitleDTO
self.playlist = playlist
}

let subtitleDTO: SubtitleDTO
var playlist: String?
}

private struct SubtitleDTO {
let language: String
let title: String
let url: String
}

static let videoUrlPrefix = "INTERCEPTEDVIDEO"
static let subtitleUrlPrefix = "INTERCEPTEDSUBTITLE"
static let subtitleUrlSuffix = "m3u8"
private let session: URLSession
private let subtitleBundles: [SubtitleBundle]

init(_ subtitles: [VideoSourceEpisodeUrlSubtitle]) {
self.session = URLSession(configuration: .default)
self.subtitleBundles = subtitles.map({
SubtitleBundle(subtitleDTO: SubtitleDTO(language: $0.language, title: $0.name, url: $0.url))
})
}

func resourceLoader(
_ resourceLoader: AVAssetResourceLoader,
shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest
) -> Bool {
guard let url = loadingRequest.request.url,
let dataRequest = loadingRequest.dataRequest else { return true }

if url.absoluteString.starts(with: Self.subtitleUrlPrefix) {
guard let targetLanguage = url.host?.split(separator: ".").first,
let targetSubtitle = self.subtitleBundles.first(where: { $0.subtitleDTO.language == targetLanguage }),
let subtitleUrl = URL(string: targetSubtitle.subtitleDTO.url) else {
loadingRequest.finishLoading(with: AVError(.unknown))
return true
}

let subtitlePlaylistTask = self.session.dataTask(with: subtitleUrl) { [weak self] data, _, error in
if let error {
loadingRequest.finishLoading(with: error)
return
}
guard let data, !data.isEmpty, let dataString = String(data: data, encoding: .utf8) else {
loadingRequest.finishLoading(with: AVError(.unknown))
return
}

self?.makePlaylistAndFragments(bundle: targetSubtitle, subtitle: dataString)

guard let playlistData = targetSubtitle.playlist?.data(using: .utf8) else {
loadingRequest.finishLoading(with: AVError(.unknown))
return
}
dataRequest.respond(with: playlistData)
}

subtitlePlaylistTask.resume()
return true
}

guard let newUrl = URL(string: url.absoluteString.replacingOccurrences(of: Self.videoUrlPrefix, with: "")) else { return true }

if !(
url.absoluteString.lowercased().hasSuffix(".ism/manifest(format=m3u8-aapl)") ||
url.absoluteString.lowercased().hasSuffix(".m3u8")
) || (
dataRequest.requestedOffset == 0 && dataRequest.requestedLength == 2 && dataRequest.currentOffset == 0
) {
let newRequest = URLRequest(url: newUrl)
loadingRequest.redirect = newRequest
let fakeResponse = HTTPURLResponse(url: newUrl, statusCode: 302, httpVersion: nil, headerFields: nil)
loadingRequest.response = fakeResponse
loadingRequest.finishLoading()
return true
}

var correctedRequest = URLRequest(url: newUrl)
for header in loadingRequest.request.allHTTPHeaderFields ?? [:] {
correctedRequest.addValue(header.value, forHTTPHeaderField: header.key)
}

let masterPlaylistTask = self.session.dataTask(with: correctedRequest) { [weak self] data, _, error in
if let error {
loadingRequest.finishLoading(with: error)
return
}

guard let data,
let dataString = String(data: data, encoding: .utf8),
let withSubs = self?.addSubs(to: dataString),
let withSubsData = withSubs.data(using: .utf8) else {
loadingRequest.finishLoading(with: AVError(.unknown))
return
}
dataRequest.respond(with: withSubsData)
loadingRequest.finishLoading()
}
masterPlaylistTask.resume()
return true
}

func addSubs(to dataString: String) -> String {
guard dataString.contains("#EXT-X-STREAM-INF:") else { return dataString }
var tracks = dataString.split(separator: "\n").map({ $0.hasPrefix("#EXT-X-STREAM-INF:") ? $0 + ",SUBTITLES=\"subs\"" : $0 })
tracks.insert(contentsOf: subtitleBundles.map({
"#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",LANGUAGE=\"\($0.subtitleDTO.language)\",NAME=\"\($0.subtitleDTO.title)\","
+ "AUTOSELECT=YES,URI=\"\(Self.subtitleUrlPrefix)://\($0.subtitleDTO.language).\(Self.subtitleUrlSuffix)\""
}), at: tracks.firstIndex(where: { $0.contains("#EXT-X-STREAM-INF") }) ?? tracks.endIndex)
return tracks.joined(separator: "\n")
}

private func makePlaylistAndFragments(bundle: SubtitleBundle, subtitle: String) {
if let regex = try? NSRegularExpression(pattern: #"(\d{2}:\d{2}:\d{2}.\d{3})"#),
let timeString = regex.matches(in: subtitle, range: NSRange(location: 0, length: subtitle.count)).last.flatMap({
(subtitle as NSString).substring(with: $0.range)
}),
let hour = timeString.split(separator: ":")[safe: 0].flatMap({ Double($0) }), hour.isFinite,
let minute = timeString.split(separator: ":")[safe: 1].flatMap({ Double($0) }), minute.isFinite,
let second = timeString.split(separator: ":")[safe: 2].flatMap({ Double($0) }), second.isFinite {
let rounded = Int(ceil(hour * 3600 + minute * 60 + second))
bundle.playlist = [
"#EXTM3U",
"#EXT-X-TARGETDURATION:\(rounded)",
"#EXT-X-VERSION:3",
"#EXT-X-MEDIA-SEQUENCE:0",
"#EXT-X-PLAYLIST-TYPE:VOD",
"#EXTINF:\(rounded)",
bundle.subtitleDTO.url,
"#EXT-X-ENDLIST"
].joined(separator: "\n")
} else if let regex = try? NSRegularExpression(pattern: #"(\d{2}:\d{2}.\d{3})"#),
let timeString = regex.matches(in: subtitle, range: NSRange(location: 0, length: subtitle.count)).last.flatMap({
(subtitle as NSString).substring(with: $0.range)
}),
let minute = timeString.split(separator: ":")[safe: 0].flatMap({ Double($0) }), minute.isFinite,
let second = timeString.split(separator: ":")[safe: 1].flatMap({ Double($0) }), second.isFinite {
let rounded = Int(ceil(minute * 60 + second))
bundle.playlist = [
"#EXTM3U",
"#EXT-X-TARGETDURATION:\(rounded)",
"#EXT-X-VERSION:3",
"#EXT-X-MEDIA-SEQUENCE:0",
"#EXT-X-PLAYLIST-TYPE:VOD",
"#EXTINF:\(rounded)",
bundle.subtitleDTO.url,
"#EXT-X-ENDLIST"
].joined(separator: "\n")
}
}
}

VideoPlayerViewController.swift

VideoPlayerViewController.swift


    func setSubtitles(to subtitles: [VideoSourceEpisodeUrlSubtitle]) {
Task {
if let group = try? await self.playerLayer.player?.currentItem?.asset.loadMediaSelectionGroup(
for: AVMediaCharacteristic.legible
) {
self.captionsButton.menu = UIMenu(children: [
UIAction(
title: "None",
image: self.currentSubtitleTrack == nil ? UIImage(systemName: "checkmark") : nil
) { [weak self] _ in
self?.playerLayer.player?.currentItem?.select(nil, in: group)
self?.currentSubtitleTrack = nil
self?.setSubtitles(to: subtitles)
}
] + subtitles.map({ subtitle in
UIAction(
title: subtitle.name,
image: self.currentSubtitleTrack?.url == subtitle.url ? UIImage(systemName: "checkmark") : nil
) { [weak self] _ in
let options = AVMediaSelectionGroup.mediaSelectionOptions(from: group.options, with: Locale(
identifier: subtitle.language
))
if let option = options.first {
self?.playerLayer.player?.currentItem?.select(option, in: group)
self?.currentSubtitleTrack = subtitle
self?.setSubtitles(to: subtitles)
}
}
}))
}
}
}

VideoSourceEpisodeUrlSubtitle.swift

VideoSourceEpisodeUrlSubtitle.swift


struct VideoSourceEpisodeUrlSubtitle: Codable {
let name: String
let url: String
let language: String
}

Thanks everyone in advance for the help.

提前感谢大家的帮助。


更多回答
优秀答案推荐
更多回答

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com