Skip to content

Commit

Permalink
fix video size detection, and audio track detection to work with HLS,…
Browse files Browse the repository at this point in the history
… add live stream indicator

Closes: #1560
Signed-off-by: William Casarin <[email protected]>
  • Loading branch information
fishcakeday authored and jb55 committed Oct 1, 2023
1 parent 2e74300 commit c9b440c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 14 deletions.
22 changes: 22 additions & 0 deletions damus/Views/Video/DamusVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -93,6 +96,25 @@ struct DamusVideoPlayer: View {
}
}
}

private var live_indicator: some View {
VStack {
HStack {
Text("LIVE")
.bold()
.foregroundColor(.red)
.padding(.horizontal)
.padding(.vertical, 5)
.background(
Capsule()
.fill(Color.black.opacity(0.5))
)
.padding([.top, .leading])
Spacer()
}
Spacer()
}
}
}
struct DamusVideoPlayer_Previews: PreviewProvider {
static var previews: some View {
Expand Down
56 changes: 42 additions & 14 deletions damus/Views/Video/DamusVideoPlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<AnyCancellable>()

private var videoSizeObserver: NSKeyValueObservation?
private var videoDurationObserver: NSKeyValueObservation?

private var is_scrolled_into_view = false {
didSet {
if is_scrolled_into_view && !oldValue {
Expand Down Expand Up @@ -78,6 +81,31 @@ final class DamusVideoPlayerViewModel: ObservableObject {
model_id == self?.id ? self?.player.play() : self?.player.pause()
}
.store(in: &cancellables)

observeVideoSize()
observeDuration()
}

private func observeVideoSize() {
videoSizeObserver = player.currentItem?.observe(\.presentationSize, options: [.new], changeHandler: { [weak self] (playerItem, change) in
guard let self else { return }
if let newSize = change.newValue, newSize != .zero {
DispatchQueue.main.async {
self.video_size = newSize // Update the bound value
}
}
})
}

private func observeDuration() {
videoDurationObserver = player.currentItem?.observe(\.duration, options: [.new], changeHandler: { [weak self] (playerItem, change) in
guard let self else { return }
if let newDuration = change.newValue, newDuration != .zero {
DispatchQueue.main.async {
self.is_live = newDuration == .indefinite
}
}
})
}

private func load() async {
Expand All @@ -86,11 +114,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
Expand All @@ -114,4 +137,9 @@ final class DamusVideoPlayerViewModel: ObservableObject {
player.seek(to: CMTime.zero)
player.play()
}

deinit {
videoSizeObserver?.invalidate()
videoDurationObserver?.invalidate()
}
}

0 comments on commit c9b440c

Please sign in to comment.