123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- //
- // TSVideoPlayer.swift
- // AIEmoji
- //
- // Created by 100Years on 2025/7/16.
- //
- class AVPlayerPool {
- private static var pool: [URL: AVPlayer] = [:]
- private static let lock = NSLock()
-
- static func player(for url: URL) -> AVPlayer {
- lock.lock()
- defer { lock.unlock() }
-
- // 查找可复用的播放器
- if let player = pool[url] {
- player.seek(to: .zero)
- return player
- }
-
- // 创建新播放器
- let asset = AVAsset(url: url)
- let playerItem = AVPlayerItem(asset: asset)
- let player = AVPlayer(playerItem: playerItem)
- pool[url] = player
-
- // 设置自动清理
- NotificationCenter.default.addObserver(
- forName: .AVPlayerItemDidPlayToEndTime,
- object: playerItem,
- queue: nil
- ) { _ in
- player.seek(to: .zero)
- }
-
- return player
- }
-
- static func releasePlayer(for url: URL) {
- lock.lock()
- defer { lock.unlock() }
- pool[url]?.pause()
- pool[url] = nil
- }
- }
- import AVKit
- class TSVideoPlayer:NSObject {
- var player: AVPlayer?
- var playerLayer: AVPlayerLayer?
- var playerItem: AVPlayerItem?
- var loopInterval: TimeInterval = 0
- private var timer: Timer?
- private var isBackgroundPaused = false
-
- // 播放状态回调
- var onPlaybackStart: (() -> Void)?
- var onPlaybackEnd: (() -> Void)?
-
- // 最大同时播放数限制
- private static var activePlayers = 0
- private static let maxActivePlayers = 100 // 同时最多2个播放器
-
- var url:URL?
- deinit {
- cleanup()
- }
-
- func setupPlayer(with url: URL) {
- // 检查是否超过最大播放器数量
- guard Self.activePlayers < Self.maxActivePlayers else {
- print("已达到最大播放器数量限制")
- return
- }
- self.url = url
-
- playerItem = AVPlayerItem(asset: AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: false]))
-
- player = AVPlayer(playerItem: playerItem)
- player?.volume = 0.5
- player?.isMuted = true // 静音播放减少资源占用
- player?.actionAtItemEnd = .pause
- playerLayer = AVPlayerLayer(player: player)
- playerLayer?.videoGravity = .resizeAspectFill
- playerLayer?.shouldRasterize = true
- playerLayer?.rasterizationScale = UIScreen.main.scale
-
-
- // 设置音频会话
- do {
- let audioSession = AVAudioSession.sharedInstance()
- try audioSession.setCategory(.playback)
- try audioSession.setActive(true)
- } catch {
- print("TSAudioPlayer 音频会话设置失败: \(error.localizedDescription)")
- }
-
- setupNotifications()
- Self.activePlayers += 1
- }
-
- func replaceUrl(with url: URL) {
- playerItem = AVPlayerItem(asset: AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: false]))
- player?.replaceCurrentItem(with: playerItem)
- }
- private func setupNotifications() {
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(playerItemDidReachEnd),
- name: .AVPlayerItemDidPlayToEndTime,
- object: playerItem
- )
-
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(applicationDidEnterBackground),
- name: UIApplication.didEnterBackgroundNotification,
- object: nil
- )
-
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(applicationWillEnterForeground),
- name: UIApplication.willEnterForegroundNotification,
- object: nil
- )
-
- }
-
- static func getFirstFrameOnly(url:URL)->UIImage? {
- let urlString = "TSVideoPlayer/" + url.lastPathComponent
-
- if let image = TSImageStoreTool.retrieveImageInMemoryCache(urlString: urlString) {
- dePrint("从缓存中取图片")
- return image
- }
-
- let asset = AVAsset(url: url)
- let generator = AVAssetImageGenerator(asset: asset)
- generator.appliesPreferredTrackTransform = true
-
- do {
- let cgImage = try generator.copyCGImage(at: CMTime(seconds: 0, preferredTimescale: 1), actualTime: nil)
- let image = UIImage(cgImage: cgImage)
- TSImageStoreTool.storeImage(image: image, urlString: urlString)
- return image
- } catch {
- dePrint("获取第一帧失败: \(error)")
- }
-
- return nil
- }
-
- @objc private func applicationDidEnterBackground() {
- if player?.rate != 0 {
- isBackgroundPaused = true
- pause()
- }
- }
-
- @objc private func applicationWillEnterForeground() {
- if isBackgroundPaused {
- play()
- isBackgroundPaused = false
- }
- }
-
- @objc private func playerItemDidReachEnd(notification: Notification) {
- onPlaybackEnd?()
-
- if loopInterval > 0 {
- timer?.invalidate()
- timer = Timer.scheduledTimer(
- withTimeInterval: loopInterval,
- repeats: false,
- block: { [weak self] _ in
- self?.restartPlayback()
- }
- )
- } else {
- restartPlayback()
- }
- }
-
- private func restartPlayback() {
- player?.seek(to: .zero)
- player?.play()
- onPlaybackStart?()
- }
-
- func addPlayerLayer(to view: UIView, frame: CGRect) {
- guard let playerLayer = playerLayer else { return }
-
- playerLayer.frame = frame
- view.layer.insertSublayer(playerLayer, at: 0)
- }
-
- func play() {
- playerLayer?.isHidden = false
- // 检查是否超过最大播放器数量
- guard Self.activePlayers < Self.maxActivePlayers else {
- print("已达到最大播放器数量限制")
- return
- }
-
- // if player?.currentItem?.status == .readyToPlay {
- player?.play()
- onPlaybackStart?()
- // }
- }
-
- func pause() {
- player?.pause()
- playerLayer?.isHidden = true
- }
-
- func stop() {
- pause()
- player?.seek(to: .zero)
- timer?.invalidate()
- timer = nil
- }
-
- func cleanup() {
- stop()
- NotificationCenter.default.removeObserver(self)
- self.playerLayer?.removeFromSuperlayer()
- playerItem = nil
- player = nil
- playerLayer = nil
- Self.activePlayers -= 1
- }
-
- func updateFrame(_ frame: CGRect) {
- playerLayer?.frame = frame
- }
- }
|