Skip to content

Commit

Permalink
Prevent SRG SSR livestreams from being played in the past (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
defagos authored Oct 11, 2022
1 parent 21aec7b commit c2f3483
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 17 deletions.
11 changes: 8 additions & 3 deletions Demo/Sources/BasicPlayerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ struct BasicPlayerView: View {
@StateObject private var player = Player(item: AVPlayerItem(url: Stream.appleAdvanced_16_9_HEVC_h264))

var body: some View {
VideoView(player: player)
.onAppear {
player.play()
ZStack {
VideoView(player: player)
if player.isBuffering {
ProgressView()
}
}
.onAppear {
player.play()
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions Demo/Sources/Media.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
import Foundation

struct Media: Identifiable {
static var liveMedia = Media(
id: "live",
title: "Couleur 3",
description: "Couleur 3 livestream",
source: .url(Stream.couleur3_livestream)
)

static var urnPlaylist: [Media] = [
Media(
id: "playlist:1",
Expand Down
6 changes: 3 additions & 3 deletions Demo/Sources/MediasView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ struct MediasView: View {
id: "assets:video_live_hls",
title: "Video livestream - HLS",
description: "Couleur 3 en vidéo",
source: .url(URL(string: "https://rtsc3video.akamaized.net/hls/live/2042837/c3video/3/playlist.m3u8?dw=0")!)
source: .url(Stream.couleur3_livestream)
),
Media(
id: "assets:video_live_dvr_hls",
title: "Video livestream with DVR - HLS",
description: "Couleur 3 en vidéo",
source: .url(URL(string: "https://rtsc3video.akamaized.net/hls/live/2042837/c3video/3/playlist.m3u8")!)
source: .url(Stream.couleur3_livestream_dvr)
),
Media(
id: "assets:video_live_dvr_timestamps_hls",
Expand Down Expand Up @@ -172,7 +172,7 @@ struct MediasView: View {

// MARK: Preview

struct DemosView_Previews: PreviewProvider {
struct MediasView_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
MediasView()
Expand Down
12 changes: 9 additions & 3 deletions Demo/Sources/PlayerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ private struct PreviousButton: View {

struct PlayerView: View {
let medias: [Media]
let buffered: Bool

@StateObject private var player = Player()
@State private var isUserInterfaceHidden = false
Expand Down Expand Up @@ -125,16 +126,21 @@ struct PlayerView: View {
}
}

init(medias: [Media]) {
init(medias: [Media], buffered: Bool = true) {
self.medias = medias
self.buffered = buffered
}

init(media: Media) {
self.init(medias: [media])
init(media: Media, buffered: Bool = true) {
self.init(medias: [media], buffered: buffered)
}

private func play() {
medias.compactMap(\.source.playerItem).forEach { item in
if !buffered {
item.automaticallyPreservesTimeOffsetFromLive = true
item.preferredForwardBufferDuration = 1
}
player.append(item)
}
player.play()
Expand Down
3 changes: 3 additions & 0 deletions Demo/Sources/ShowcaseView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ struct ShowcaseView: View {
Cell(title: "Basic") {
BasicPlayerView()
}
Cell(title: "Unbuffered live playback") {
PlayerView(media: .liveMedia, buffered: false)
}
Cell(title: "Stories") {
StoriesView()
}
Expand Down
5 changes: 5 additions & 0 deletions Demo/Sources/Stream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ enum Stream {
static let appleAdvanced_16_9_fMP4 = URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8")!
static let appleAdvanced_16_9_HEVC_h264 = URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_adv_example_hevc/master.m3u8")!

// Livestreams
static let couleur3_livestream = URL(string: "https://rtsc3video.akamaized.net/hls/live/2042837/c3video/3/playlist.m3u8?dw=0")!
static let couleur3_livestream_dvr = URL(string: "https://rtsc3video.akamaized.net/hls/live/2042837/c3video/3/playlist.m3u8")!

// Other
static let local = URL(string: "http://localhost:8123/on_demand/master.m3u8")!
}
31 changes: 27 additions & 4 deletions Sources/CoreBusiness/PlayerItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,35 @@ public extension AVPlayerItem {
/// Create a player item from a URN played in the specified environment.
/// - Parameters:
/// - urn: The URN to play.
/// - automaticallyLoadedAssetKeys: The asset keys to load before the item is ready to play, ensuring they are
/// available, but increasing startup time.
/// - automaticallyLoadedAssetKeys: The asset keys to load before the item is ready to play.
/// - environment: The environment which the URN is played from.
convenience init(urn: String, automaticallyLoadedAssetKeys: [String], environment: Environment = .production) {
convenience init(urn: String, automaticallyLoadedAssetKeys keys: [String], environment: Environment = .production) {
self.init(asset: Self.asset(fromUrn: urn, environment: environment), automaticallyLoadedAssetKeys: keys)
preventLivestreamDelayedPlayback()
}

/// Create a player item from a URN played in the specified environment. Loads standard asset keys before the item
/// is ready to play.
/// - Parameters:
/// - urn: The URN to play.
/// - environment: The environment which the URN is played from.
convenience init(urn: String, environment: Environment = .production) {
self.init(asset: Self.asset(fromUrn: urn, environment: environment))
preventLivestreamDelayedPlayback()
}

private static func asset(fromUrn urn: String, environment: Environment) -> AVAsset {
let asset = AVURLAsset(url: URLCoding.encodeUrl(fromUrn: urn))
asset.resourceLoader.setDelegate(kAssetResourceLoaders[environment], queue: .global(qos: .userInitiated))
self.init(asset: asset, automaticallyLoadedAssetKeys: automaticallyLoadedAssetKeys)
return asset
}

/// Limit buffering and force the player to return to the live edge when re-buffering. This ensures livestreams
/// cannot be paused and resumed in the past, as requested by business people.
///
/// Remark: These settings do not negatively affect on-demand or DVR livestream playback.
private func preventLivestreamDelayedPlayback() {
automaticallyPreservesTimeOffsetFromLive = true
preferredForwardBufferDuration = 1
}
}
7 changes: 3 additions & 4 deletions Sources/Player/PlayerItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ public extension AVPlayerItem {
/// Create a player item from a URL.
/// - Parameters:
/// - url: The URL to play.
/// - automaticallyLoadedAssetKeys: The asset keys to load before the item is ready to play, ensuring they are
/// available, but increasing startup time.
convenience init(url: URL, automaticallyLoadedAssetKeys: [String]) {
/// - automaticallyLoadedAssetKeys: The asset keys to load before the item is ready to play.
convenience init(url: URL, automaticallyLoadedAssetKeys keys: [String]) {
let asset = AVURLAsset(url: url)
self.init(asset: asset, automaticallyLoadedAssetKeys: automaticallyLoadedAssetKeys)
self.init(asset: asset, automaticallyLoadedAssetKeys: keys)
}
}

0 comments on commit c2f3483

Please sign in to comment.