From ce2f8aa6905698baf7b880433aa937bd1759cfd7 Mon Sep 17 00:00:00 2001 From: Fishcake Date: Tue, 26 Sep 2023 16:14:18 +0900 Subject: [PATCH] fix video size detection, and audio track detection to work with HLS, add live stream indicator --- damus/Views/Video/DamusVideoPlayer.swift | 24 ++++++++ .../Video/DamusVideoPlayerViewModel.swift | 57 ++++++++++++++----- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/damus/Views/Video/DamusVideoPlayer.swift b/damus/Views/Video/DamusVideoPlayer.swift index bbca2a3758..9b08a61a85 100644 --- a/damus/Views/Video/DamusVideoPlayer.swift +++ b/damus/Views/Video/DamusVideoPlayer.swift @@ -43,6 +43,9 @@ struct DamusVideoPlayer: View { if model.has_audio { mute_button } + if model.is_live { + live_indicator + } } .onChange(of: centerY) { _ in update_is_visible(centerY: centerY) @@ -93,6 +96,27 @@ struct DamusVideoPlayer: View { } } } + + private var live_indicator: some View { + ZStack { + VStack { + HStack { + Capsule() + .fill(Color.black.opacity(0.5)) + .frame(width: 80, height: 30) + .overlay( + Text("LIVE") + .bold() + .foregroundColor(.red) + .padding(.horizontal) + ) + .padding([.top, .leading]) + Spacer() + } + Spacer() + } + } + } } struct DamusVideoPlayer_Previews: PreviewProvider { static var previews: some View { diff --git a/damus/Views/Video/DamusVideoPlayerViewModel.swift b/damus/Views/Video/DamusVideoPlayerViewModel.swift index 761b8a4d5a..ced88ab7d8 100644 --- a/damus/Views/Video/DamusVideoPlayerViewModel.swift +++ b/damus/Views/Video/DamusVideoPlayerViewModel.swift @@ -10,16 +10,15 @@ import Combine import Foundation import SwiftUI -func get_video_size(player: AVPlayer) async -> CGSize? { - let res = Task.detached(priority: .background) { - return player.currentImage?.size - } - return await res.value -} - func video_has_audio(player: AVPlayer) async -> Bool { - let tracks = try? await player.currentItem?.asset.load(.tracks) - return tracks?.filter({ t in t.mediaType == .audio }).first != nil + do { + let hasAudibleTracks = ((try await player.currentItem?.asset.loadMediaSelectionGroup(for: .audible)) != nil) + let tracks = try? await player.currentItem?.asset.load(.tracks) + let hasAudioTrack = tracks?.filter({ t in t.mediaType == .audio }).first != nil // Deal with odd cases of audio only MOV + return hasAudibleTracks || hasAudioTrack + } catch { + return false + } } @MainActor @@ -32,12 +31,16 @@ final class DamusVideoPlayerViewModel: ObservableObject { let id = UUID() @Published var has_audio = false + @Published var is_live = false @Binding var video_size: CGSize? @Published var is_muted = true @Published var is_loading = true private var cancellables = Set() + private var videoSizeObserver: NSKeyValueObservation? + private var videoLiveObserver: NSKeyValueObservation? + private var is_scrolled_into_view = false { didSet { if is_scrolled_into_view && !oldValue { @@ -78,6 +81,32 @@ final class DamusVideoPlayerViewModel: ObservableObject { model_id == self?.id ? self?.player.play() : self?.player.pause() } .store(in: &cancellables) + + setupVideoSizeObserver() + setupLiveObserver() + } + + private func setupVideoSizeObserver() { + videoSizeObserver = player.currentItem?.observe(\.presentationSize, options: [.new], changeHandler: { [weak self] (playerItem, change) in + guard let self = self else { return } + if let newSize = change.newValue, newSize != .zero { + DispatchQueue.main.async { + self.video_size = newSize // Update the bound value + } + } + }) + } + + private func setupLiveObserver() { + videoLiveObserver = player.currentItem?.observe(\.duration, options: [.new], changeHandler: { [weak self] (playerItem, change) in + guard let self = self else { return } + if let newDuration = change.newValue, newDuration != .zero { + DispatchQueue.main.async { + print("DEBUG:: newDuration: \(newDuration)") + self.is_live = newDuration == .indefinite + } + } + }) } private func load() async { @@ -86,11 +115,6 @@ final class DamusVideoPlayerViewModel: ObservableObject { video_size = meta.size } else { has_audio = await video_has_audio(player: player) - if let video_size = await get_video_size(player: player) { - self.video_size = video_size - let meta = VideoMetadata(has_audio: has_audio, size: video_size) - controller.set_metadata(meta, url: url) - } } is_loading = false @@ -114,4 +138,9 @@ final class DamusVideoPlayerViewModel: ObservableObject { player.seek(to: CMTime.zero) player.play() } + + deinit { + videoSizeObserver?.invalidate() + videoLiveObserver?.invalidate() + } }