mirror of
git://jb55.com/damus
synced 2024-09-18 19:23:49 +00:00
fix video size detection, and audio track detection to work with HLS, add live stream indicator
Closes: https://github.com/damus-io/damus/pull/1560 Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
parent
d6c72403a3
commit
35c581066a
@ -43,6 +43,9 @@ struct DamusVideoPlayer: View {
|
|||||||
if model.has_audio {
|
if model.has_audio {
|
||||||
mute_button
|
mute_button
|
||||||
}
|
}
|
||||||
|
if model.is_live {
|
||||||
|
live_indicator
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: centerY) { _ in
|
.onChange(of: centerY) { _ in
|
||||||
update_is_visible(centerY: centerY)
|
update_is_visible(centerY: centerY)
|
||||||
@ -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 {
|
struct DamusVideoPlayer_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
@ -10,16 +10,15 @@ import Combine
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
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 {
|
func video_has_audio(player: AVPlayer) async -> Bool {
|
||||||
let tracks = try? await player.currentItem?.asset.load(.tracks)
|
do {
|
||||||
return tracks?.filter({ t in t.mediaType == .audio }).first != nil
|
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
|
@MainActor
|
||||||
@ -32,12 +31,16 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
let id = UUID()
|
let id = UUID()
|
||||||
|
|
||||||
@Published var has_audio = false
|
@Published var has_audio = false
|
||||||
|
@Published var is_live = false
|
||||||
@Binding var video_size: CGSize?
|
@Binding var video_size: CGSize?
|
||||||
@Published var is_muted = true
|
@Published var is_muted = true
|
||||||
@Published var is_loading = true
|
@Published var is_loading = true
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
private var videoSizeObserver: NSKeyValueObservation?
|
||||||
|
private var videoDurationObserver: NSKeyValueObservation?
|
||||||
|
|
||||||
private var is_scrolled_into_view = false {
|
private var is_scrolled_into_view = false {
|
||||||
didSet {
|
didSet {
|
||||||
if is_scrolled_into_view && !oldValue {
|
if is_scrolled_into_view && !oldValue {
|
||||||
@ -78,6 +81,31 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
model_id == self?.id ? self?.player.play() : self?.player.pause()
|
model_id == self?.id ? self?.player.play() : self?.player.pause()
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.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 {
|
private func load() async {
|
||||||
@ -86,11 +114,6 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
video_size = meta.size
|
video_size = meta.size
|
||||||
} else {
|
} else {
|
||||||
has_audio = await video_has_audio(player: player)
|
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
|
is_loading = false
|
||||||
@ -114,4 +137,9 @@ final class DamusVideoPlayerViewModel: ObservableObject {
|
|||||||
player.seek(to: CMTime.zero)
|
player.seek(to: CMTime.zero)
|
||||||
player.play()
|
player.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
videoSizeObserver?.invalidate()
|
||||||
|
videoDurationObserver?.invalidate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user