Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Interstitial Tag Support #144

Merged
merged 13 commits into from
Nov 1, 2024
112 changes: 72 additions & 40 deletions mamba.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Foundation

extension String {

private struct DateFormatter {
struct DateFormatter {
static let iso8601MS: Foundation.DateFormatter = {
let formatter = Foundation.DateFormatter()
formatter.calendar = Calendar(identifier: Calendar.Identifier.iso8601)
Expand Down
112 changes: 112 additions & 0 deletions mambaSharedFramework/HLSInterstitialValueTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// HLSInterstitialValueTypes.swift
// mamba
//
// Created by Migneco, Ray on 10/22/24.
//

import Foundation

/// specifies how the client should align interstitial content to the primary content
public struct HLSInterstitialAlignment: FailableStringLiteralConvertible, Equatable {

public enum Snap: String, CaseIterable {
/// client SHOULD locate the segment boundary closest to the scheduled resumption point from the
/// interstitial in the Media Playlist of the primary content and resume playback of primary content at that boundary.
case `in` = "IN"

/// client SHOULD locate the segment boundary closest to the START-DATE of the interstitial in the
/// Media Playlist of the primary content and transition to the interstitial at that boundary.
case out = "OUT"
}

/// the set of snap options for aligning interstitial content
public let values: Set<Snap>

/// creates a snap guide based on provided values
///
/// - Parameter values: array of `Snap` values
public init(values: [Snap]) {
self.values = Set(values)
}

/// creates a snap guide based on the provided string value
///
/// - Parameter string: a comma separated string indicating snap values
public init?(string: String) {
let snapValues = string.components(separatedBy: ",")
.compactMap({ Snap(rawValue: $0 )})

guard !snapValues.isEmpty else { return nil }

self.init(values: snapValues)
}
}

/// specifies how the player should enforce seek restrictions for the interstitial content
public struct HLSInterstitialSeekRestrictions: FailableStringLiteralConvertible, Equatable {

public enum Restriction: String, CaseIterable {
/// If the list contains SKIP then while the interstitial is being played, the client MUST NOT
/// allow the user to seek forward from the current playhead position or set the rate to
/// greater than the regular playback rate until playback reaches the end of the interstitial.
case skip = "SKIP"

/// If the list contains JUMP then the client MUST NOT allow the user to seek from a position
/// in the primary asset earlier than the START-DATE attribute to a position after it without
/// first playing the interstitial asset, even if the interstitial at START-DATE was played
/// through earlier.
case jump = "JUMP"
}

/// set of restrictions applied to the interstitial content
public let restrictions: Set<Restriction>

/// Creates a set of restrictions based on provided values
///
/// - Parameter restrictions: array of `Restriction`
public init(restrictions: [Restriction]) {
self.restrictions = Set(restrictions)
}

/// creates a snap guide based on the provided string value
///
/// - Parameter string: a comma separated string indicating snap values
public init?(string: String) {
let restrictions = string.components(separatedBy: ",")
.compactMap({ Restriction(rawValue: $0 )})

guard !restrictions.isEmpty else { return nil }

self.init(restrictions: restrictions)
}
}

public enum HLSInterstitialTimelineStyle: String, FailableStringLiteralConvertible {

/// indicates whether the interstitial is intended to be presented as distinct from the content
case highlight = "HIGHLIGHT"

/// indicates that the interstitial should NOT be presented as differentiated from the content
case primary = "PRIMARY"

/// Creates a timeline style from the provided string
public init?(string: String) {
self.init(rawValue: string)
}
}

/// Type that indicates how an interstitial event should be presented on a timeline
public enum HLSInterstitialTimelineOccupation: String, FailableStringLiteralConvertible {

/// the interstitial should be presented as a single point on the timeline
case point = "POINT"

/// the interstitial should be presented as a range on the timeline
case range = "RANGE"

/// Creates a timeline occupation from the provided string
public init?(string: String) {
self.init(rawValue: string)
}
}
2 changes: 2 additions & 0 deletions mambaSharedFramework/HLSValidationIssue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,7 @@ public enum IssueDescription: String {
case EXT_X_DATERANGETagPLANNED_DURATIONMustNotBeNegative = "PLANNED-DURATION MUST NOT be negative."
case EXT_X_DATERANGEExistsWithNoEXT_X_PROGRAM_DATE_TIME = "If a Playlist contains an EXT-X-DATERANGE tag, it MUST also contain at least one EXT-X-PROGRAM-DATE-TIME tag."
case EXT_X_DATERANGEAttributeMismatchForTagsWithSameID = "If a Playlist contains two EXT-X-DATERANGE tags with the same ID attribute value, then any AttributeName that appears in both tags MUST have the same AttributeValue."
case EXT_X_DATERANGEMissingAssetListOrAssetUriAttribute = "A Date Range tag specifying CLASS=com.apple.hls.interstitial must contain either an X-ASSET-LIST OR X-ASSET-URI attribute"
case EXT_X_DATERANGEContainsBothAssetListAndAssetUriAttribute = "A Date Range tag specifying CLASS=com.apple.hls.interstitial cannot contain both an X-ASSET-LIST AND X-ASSET-URI attribute"
}

Loading