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

feat: program date time instant clipping #63

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ class SinglePlayerExampleController: UIViewController {
}
}

var programStartTimeInSeconds: Double = .nan {
didSet {
preparePlayerViewController()
}
}

var programEndTimeInSeconds: Double = .nan {
didSet {
preparePlayerViewController()
}
}

func preparePlayerViewController() {
playerViewController.prepare(
playbackID: playbackID,
Expand Down Expand Up @@ -344,6 +356,44 @@ class SinglePlayerExampleController: UIViewController {
assetEndTimeTextField.keyboardType = .decimalPad
assetEndTimeTextField.placeholder = "Clip ending time if desired"

let programStartTimeTextField = UITextField(
frame: .zero,
primaryAction: UIAction(
handler: { action in
if let text = (action.sender as? UITextField)?.text {
self.programStartTimeInSeconds = (
try? Double(
text,
format: .number
)
) ?? .nan
}
}
)
)

programStartTimeTextField.keyboardType = .decimalPad
programStartTimeTextField.placeholder = "Clip ending program date and time if desired"

let programEndTimeTextField = UITextField(
frame: .zero,
primaryAction: UIAction(
handler: { action in
if let text = (action.sender as? UITextField)?.text {
self.programEndTimeInSeconds = (
try? Double(
text,
format: .number
)
) ?? .nan
}
}
)
)

programEndTimeTextField.keyboardType = .decimalPad
programEndTimeTextField.placeholder = "Clip ending program date and time if desired"

let stackView = UIStackView(
arrangedSubviews: [
maximumResolutionLabel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ internal extension URLComponents {
)
)
}

if !publicPlaybackOptions.instantClipping.assetStartTimeInSeconds.isNaN {
queryItems.append(
URLQueryItem(
name: "program_start_time",
value: publicPlaybackOptions.instantClipping.programStartTimeEpochInSeconds.description
)
)
}

if !publicPlaybackOptions.instantClipping.assetEndTimeInSeconds.isNaN {
queryItems.append(
URLQueryItem(
name: "program_end_time",
value: publicPlaybackOptions.instantClipping.programEndTimeEpochInSeconds.description
)
)
}
}

self.queryItems = queryItems
Expand Down
61 changes: 61 additions & 0 deletions Sources/MuxPlayerSwift/PublicAPI/Options/PlaybackOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public struct InstantClipping: Equatable {
var assetStartTimeInSeconds: Double
var assetEndTimeInSeconds: Double

var programStartTimeEpochInSeconds: Double
var programEndTimeEpochInSeconds: Double

var noInstantClipping: Bool {
return self.assetStartTimeInSeconds.isNaN && self.assetEndTimeInSeconds.isNaN
andrewjl-mux marked this conversation as resolved.
Show resolved Hide resolved
}
Expand All @@ -92,6 +95,8 @@ public struct InstantClipping: Equatable {
init() {
self.assetStartTimeInSeconds = .nan
self.assetEndTimeInSeconds = .nan
self.programStartTimeEpochInSeconds = .nan
self.programEndTimeEpochInSeconds = .nan
}

/// Streams a clip whose starting time are based on
Expand Down Expand Up @@ -125,6 +130,8 @@ public struct InstantClipping: Equatable {
) {
self.assetStartTimeInSeconds = assetStartTimeInSeconds
self.assetEndTimeInSeconds = assetEndTimeInSeconds
self.programStartTimeEpochInSeconds = .nan
self.programEndTimeEpochInSeconds = .nan
}

/// Streams a clip whose starting time is based on
Expand All @@ -140,6 +147,8 @@ public struct InstantClipping: Equatable {
) {
self.assetStartTimeInSeconds = assetStartTimeInSeconds
self.assetEndTimeInSeconds = .nan
self.programStartTimeEpochInSeconds = .nan
self.programEndTimeEpochInSeconds = .nan
}

/// Streams a clip with an ending time based on the
Expand All @@ -154,6 +163,58 @@ public struct InstantClipping: Equatable {
) {
self.assetStartTimeInSeconds = .nan
self.assetEndTimeInSeconds = assetEndTimeInSeconds
self.programStartTimeEpochInSeconds = .nan
self.programEndTimeEpochInSeconds = .nan
}


/// Streams a clip with a start and end time based
/// on the program date time exposed in the video stream
/// metadata. Only supported for use with livestream-
/// originating assets.
/// - Parameters:
/// - programStartTimeEpochInSeconds: program date and
/// time epoch timestamp with which to start the clip
/// - programEndTimeEpochInSeconds: program date and
/// time epoch timestamp with which to end the clip
public init(
programStartTimeEpochInSeconds: Double,
programEndTimeEpochInSeconds: Double
) {
self.assetStartTimeInSeconds = .nan
self.assetEndTimeInSeconds = .nan
self.programStartTimeEpochInSeconds = programStartTimeEpochInSeconds
self.programEndTimeEpochInSeconds = programEndTimeEpochInSeconds
}

/// Streams a clip with a start time based on the program
/// date time exposed in the video stream metadata. Only
/// supported for use with livestream-originating assets.
/// - Parameters:
/// - programStartTimeEpochInSeconds: program date and
/// time epoch timestamp with which to start the clip
public init(
programStartTimeEpochInSeconds: Double
) {
self.assetStartTimeInSeconds = .nan
self.assetEndTimeInSeconds = .nan
self.programStartTimeEpochInSeconds = programStartTimeEpochInSeconds
self.programEndTimeEpochInSeconds = .nan
}

/// Streams a clip with a start time based on the program
/// date time exposed in the video stream metadata. Only
/// supported for use with livestream-originating assets.
/// - Parameters:
/// - programEndTimeEpochInSeconds: program date and
/// time epoch timestamp with which to start the clip
public init(
programEndTimeEpochInSeconds: Double
) {
self.assetStartTimeInSeconds = .nan
self.assetEndTimeInSeconds = .nan
self.programStartTimeEpochInSeconds = .nan
self.programEndTimeEpochInSeconds = programEndTimeEpochInSeconds
}
}

Expand Down
94 changes: 94 additions & 0 deletions Tests/MuxPlayerSwift/PlaybackURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -595,4 +595,98 @@ final class PlaybackURLTests: XCTestCase {
})
)
}

func testClippingProgramStartTime() throws {
let item = AVPlayerItem(
playbackID: "abc123",
playbackOptions: PlaybackOptions(
clipping: InstantClipping(
programStartTimeEpochInSeconds: 1730214200
)
)
)

let url = try XCTUnwrap(
(item.asset as? AVURLAsset)?.url,
"Expected player item with URL"
)

let queryItems = try XCTUnwrap(
URLComponents(
url: url,
resolvingAgainstBaseURL: false
)?.queryItems
)

XCTAssertNotNil(
queryItems.first(where: {
$0.name == "program_start_time"
})
)
}

func testClippingProgramEndTime() throws {
let item = AVPlayerItem(
playbackID: "abc123",
playbackOptions: PlaybackOptions(
clipping: InstantClipping(
programEndTimeEpochInSeconds: 1730217200
)
)
)

let url = try XCTUnwrap(
(item.asset as? AVURLAsset)?.url,
"Expected player item with URL"
)

let queryItems = try XCTUnwrap(
URLComponents(
url: url,
resolvingAgainstBaseURL: false
)?.queryItems
)

XCTAssertNotNil(
queryItems.first(where: {
$0.name == "program_end_time"
})
)
}

func testClippingProgramStartAndEndTime() throws {
let item = AVPlayerItem(
playbackID: "abc123",
playbackOptions: PlaybackOptions(
clipping: InstantClipping(
programStartTimeEpochInSeconds: 1730214200,
programEndTimeEpochInSeconds: 1730217200
)
)
)

let url = try XCTUnwrap(
(item.asset as? AVURLAsset)?.url,
"Expected player item with URL"
)

let queryItems = try XCTUnwrap(
URLComponents(
url: url,
resolvingAgainstBaseURL: false
)?.queryItems
)

XCTAssertNotNil(
queryItems.first(where: {
$0.name == "program_start_time"
})
)

XCTAssertNotNil(
queryItems.first(where: {
$0.name == "program_end_time"
})
)
}
}