You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

tvOS 18下HLS直播流章节封面图在AVPlayer中闪烁的问题求助

tvOS 18下HLS直播流章节封面图在AVPlayer中闪烁的问题求助

我最近遇到了一个棘手的问题:在tvOS 18的模拟器和真实Apple TV设备上,用AVPlayer播放带章节封面图的HLS直播流时,章节的封面图会持续闪烁。但如果完全移除封面图,播放就完全正常,没有任何视觉问题。更奇怪的是,同样的逻辑在tvOS 17上是正常工作的,到tvOS 18就出问题了。

实现细节

  • 使用AVPlayer播放HLS直播流
  • 章节标记的时间戳和标题配置都没问题
  • 封面图是通过网络加载的(没办法打包到APP里),格式为PNG
  • 就算等图片完全加载完成后再播放,闪烁问题依然存在
  • 这个问题只在Apple TV/tvOS上出现,而且仅针对直播流——用同样的图片做VOD内容的章节封面完全没问题

示例代码

我写了一个最小复现的示例,先从网络加载几张图片,然后把它们作为直播流不同章节的封面:

import SwiftUI
import AVKit

struct Chapter: Identifiable {
    let id = UUID()
    let title: String
    let seconds: Double
    var artworkData: Data?
}

class ChaptersLoader: ObservableObject {
    @Published var chapters: [Chapter] = []
    @Published var ready = false
    
    let baseChapters = [
        ("Intro", 0.0),
        ("Chapter 1", 60.0),
        ("Chapter 2", 120.0),
        ("Chapter 3", 180.0)
    ]
    
    func load() {
        var loaded: [Chapter] = []
        let group = DispatchGroup()
        
        for (title, seconds) in baseChapters {
            group.enter()
            let url = URL(string: "https://cataas.com/cat")!
            URLSession.shared.dataTask(with: url) { data, _, _ in
                var chapter = Chapter(title: title, seconds: seconds, artworkData: nil)
                if let data = data {
                    chapter.artworkData = data
                }
                loaded.append(chapter)
                group.leave()
            }.resume()
        }
        
        group.notify(queue: .main) {
            loaded.sort { $0.seconds < $1.seconds }
            self.chapters = loaded
            self.ready = true
        }
    }
}

struct AVPlayerViewControllerRepresentable: UIViewControllerRepresentable {
    let streamURL: URL
    let chapters: [Chapter]
    
    func makeUIViewController(context: Context) -> AVPlayerViewController {
        let item = AVPlayerItem(url: streamURL)
        
        let groups = chapters.map { chapter -> AVTimedMetadataGroup in
            let titleItem = AVMutableMetadataItem()
            titleItem.identifier = .commonIdentifierTitle
            titleItem.value = chapter.title as NSString
            titleItem.extendedLanguageTag = "und"
            
            var items: [AVMetadataItem] = [titleItem]
            
            if let data = chapter.artworkData {
                let artworkItem = AVMutableMetadataItem()
                artworkItem.identifier = .commonIdentifierArtwork
                artworkItem.dataType = kCMMetadataBaseDataType_PNG as String
                artworkItem.value = data as NSData
                artworkItem.extendedLanguageTag = "und"
                items.append(artworkItem)
            }
            
            let time = CMTime(seconds: chapter.seconds, preferredTimescale: 1)
            let range = CMTimeRange(start: time, duration: CMTime(seconds: 10, preferredTimescale: 1))
            return AVTimedMetadataGroup(items: items, timeRange: range)
        }
        
        let navGroup = AVNavigationMarkersGroup(title: "Chapters", timedNavigationMarkers: groups)
        item.navigationMarkerGroups = [navGroup]
        
        let player = AVPlayer(playerItem: item)
        let controller = AVPlayerViewController()
        controller.player = player
        player.play()
        
        return controller
    }
    
    func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {}
}

struct ContentView: View {
    @StateObject var loader = ChaptersLoader()
    let streamURL = URL(string: "https://ireplay.tv/test/blender.m3u8")!
    
    var body: some View {
        Group {
            if loader.ready {
                AVPlayerViewControllerRepresentable(streamURL: streamURL, chapters: loader.chapters)
                    .edgesIgnoringSafeArea(.all)
            } else {
                ProgressView("Loading cat artwork...")
                    .onAppear {
                        loader.load()
                    }
            }
        }
    }
}

@main
struct YourProjectNameApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

我已经尝试过的方法

  • 播放前预加载所有图片
  • 更换不同的图片格式和尺寸
  • 对图片进行缓存
    但这些方法都没能解决闪烁问题。

我的疑问

这是tvOS 18中直播流章节封面图的已知限制吗?有没有人遇到过类似问题,能提供解决方案或绕过方法?或者我的代码里有没有遗漏什么细节?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 14:43:02