Skip to content

Commit

Permalink
Merge pull request #144 from Comcast/interstitial-tags
Browse files Browse the repository at this point in the history
Add Interstitial Tag Support
  • Loading branch information
rmigneco authored Nov 1, 2024
2 parents 4fa1b2c + c3fff36 commit 9ec6fcf
Show file tree
Hide file tree
Showing 10 changed files with 903 additions and 43 deletions.
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

0 comments on commit 9ec6fcf

Please sign in to comment.