Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions Examples/LumenPlayer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DerivedData/
LumenPlayer.xcodeproj/
Info.plist
*.log
36 changes: 36 additions & 0 deletions Examples/LumenPlayer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Lumen Player for Apple TV

A minimal tvOS app that plays one URL through `KSVideoPlayerView`, with `ProAVPlayer` as the first engine and `KSMEPlayer` as the fallback. It exists to test the remux engine on a real Apple TV, including TrueHD Atmos and DTS:X tracks converted to APAC.

## Build and install

Use macOS with Xcode and [XcodeGen](https://github.com/yonaskolb/XcodeGen). Signing stays out of the project: pass your team and a bundle identifier it can sign.

```sh
cd Examples/LumenPlayer
LUMEN_DEVELOPMENT_TEAM="TEAM_ID" LUMEN_BUNDLE_ID="com.example.lumen" ./build-and-install.sh "DEVICE_ID"
```

`xcrun devicectl list devices` shows the Apple TV's identifier. The script generates the project, builds the app with the repository's Lumen and installs it.

## Run

The app reads its configuration from the launch environment. With `devicectl`, prefix each variable with `DEVICECTL_CHILD_`:

| Variable | Meaning |
|---|---|
| `LUMEN_URL` | The media to play, such as a media server's direct-play URL |
| `LUMEN_TITLE` | Optional title for the player interface |
| `LUMEN_START` | Optional start time in seconds |
| `LUMEN_LOG` | `info` to log the engines' decisions, such as a fallback from APAC to FLAC |
| `OS_ACTIVITY_DT_MODE` | `enable` to mirror the log to the console |

For Jellyfin, with an API key in `KEY` and the item and media source ID in `ID`:

```sh
export DEVICECTL_CHILD_LUMEN_URL="https://jellyfin.example.com/Videos/$ID/stream?static=true&MediaSourceId=$ID&ApiKey=$KEY"
export DEVICECTL_CHILD_LUMEN_START=420
xcrun devicectl device process launch --device "DEVICE_ID" --terminate-existing --console "com.example.lumen"
```

Keep the key out of shell history and logs. Use the server's HTTPS address with a certificate the Apple TV trusts: over plain HTTP the key travels unencrypted.
41 changes: 41 additions & 0 deletions Examples/LumenPlayer/Sources/LumenPlayerApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Lumen
import SwiftUI

/// Plays the URL in the launch environment through the remux engine, with the FFmpeg engine behind it.
@main
struct LumenPlayerApp: App {
init() {
KSOptions.firstPlayerType = ProAVPlayer.self
KSOptions.secondPlayerType = KSMEPlayer.self
if ProcessInfo.processInfo.environment["LUMEN_LOG"] == "info" {
KSOptions.logLevel = .info
}
}

var body: some Scene {
WindowGroup {
PlayerScreen()
}
}
}

struct PlayerScreen: View {
private let environment = ProcessInfo.processInfo.environment

var body: some View {
if let url = environment["LUMEN_URL"].flatMap(URL.init(string:)) {
KSVideoPlayerView(url: url, options: makeOptions(), title: environment["LUMEN_TITLE"])
.ignoresSafeArea()
} else {
Color.black
.ignoresSafeArea()
.onAppear { print("Launch with LUMEN_URL set to the media to play.") }
}
}

private func makeOptions() -> KSOptions {
let options = KSOptions()
options.startPlayTime = environment["LUMEN_START"].flatMap(TimeInterval.init) ?? 0
return options
}
}
16 changes: 16 additions & 0 deletions Examples/LumenPlayer/build-and-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
set -euo pipefail

# Usage: LUMEN_DEVELOPMENT_TEAM=TEAM_ID LUMEN_BUNDLE_ID=BUNDLE_ID ./build-and-install.sh DEVICE_ID
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
DEVICE_ID="${1:?Usage: ./build-and-install.sh DEVICE_ID}"
TEAM="${LUMEN_DEVELOPMENT_TEAM:?Set LUMEN_DEVELOPMENT_TEAM to the team that signs the app}"
BUNDLE_ID="${LUMEN_BUNDLE_ID:?Set LUMEN_BUNDLE_ID to a bundle identifier that team can sign}"
xcodegen generate --spec "$APP_DIR/project.yml"
xcodebuild -project "$APP_DIR/LumenPlayer.xcodeproj" -scheme LumenPlayer \
-destination "generic/platform=tvOS" -derivedDataPath "$APP_DIR/DerivedData" \
-allowProvisioningUpdates DEVELOPMENT_TEAM="$TEAM" PRODUCT_BUNDLE_IDENTIFIER="$BUNDLE_ID" \
build > "$APP_DIR/build.log" 2>&1 \
|| { tail -n 30 "$APP_DIR/build.log"; echo "Build failed; see $APP_DIR/build.log" >&2; exit 1; }
xcrun devicectl device install app --device "$DEVICE_ID" \
"$APP_DIR/DerivedData/Build/Products/Debug-appletvos/LumenPlayer.app"
41 changes: 41 additions & 0 deletions Examples/LumenPlayer/project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: LumenPlayer
options:
deploymentTarget:
tvOS: '16.0'
packages:
Lumen:
path: ../..
settings:
base:
SWIFT_VERSION: '5.0'
CODE_SIGN_STYLE: Automatic
TARGETED_DEVICE_FAMILY: '3'
targets:
LumenPlayer:
type: application
platform: tvOS
sources:
- Sources
dependencies:
- package: Lumen
product: Lumen
info:
path: Info.plist
properties:
NSAppTransportSecurity:
NSAllowsLocalNetworking: true
NSAllowsArbitraryLoadsForMedia: true
settings:
base:
# Signing team and bundle identifier come from build-and-install.sh.
PRODUCT_BUNDLE_IDENTIFIER: com.example.LumenPlayer
GENERATE_INFOPLIST_FILE: YES
INFOPLIST_KEY_CFBundleDisplayName: Lumen
INFOPLIST_KEY_UILaunchScreen_Generation: YES
INFOPLIST_KEY_UIUserInterfaceStyle: Dark
ASSETCATALOG_COMPILER_APPICON_NAME: ''
schemes:
LumenPlayer:
build:
targets:
LumenPlayer: all