wai-rs gains the feature floor (the mandatory floor of SPEC 5: PNG, FLAC and zstd, decoded with no system codec library) and quickstart (the floor, and the receipt checks hello and verify run). The wai command line gains four verbs, one module each, named in one table (dx::VERBS): - hello: name, dispatch, rebuild and receipt on a picture made here, the receipt laid out as http-delivery 8 lays receipts beside an object; - create: a new project from one of the examples, whose files the binary holds; tests/cli_dx.rs holds them to wai/examples/ byte for byte; - dev: the relay, the room service and the room page on one loopback origin, as a production front serves them; --check checks each piece through it; - verify: the receipt indexes under a directory, checked offline. The project file a template writes, wai.json, is read in one place (dx/project.rs). wai/examples/ holds video-room, watch-together, messaging, encoder-ingest, package-and-deliver, verify-receipts, apple-app, android-app and remote-desktop, each with a README and a test that runs it through wai create and wai dev. wai/quickstart/ builds the room client and the archive that holds wai, the relay, the room service, the remote host and the packager. Co-Authored-By: Claude Opus 5.5 <[email protected]>
50 lines
2 KiB
Swift
50 lines
2 KiB
Swift
// The player: an HLS address, played by the platform's player through the WAI native sink. The
|
|
// sink verifies each playlist against its receipt and each object against the hash its name
|
|
// states, withholds what does not verify, signs an acknowledgement of everything received, and,
|
|
// when the session ends, a bundle the stream's owner verifies.
|
|
import AVKit
|
|
import SwiftUI
|
|
import WaiSink
|
|
|
|
struct PlayerApp: App {
|
|
var body: some Scene {
|
|
WindowGroup("WAI player") { PlayerView() }
|
|
}
|
|
}
|
|
|
|
struct PlayerView: View {
|
|
@State private var address = "http://127.0.0.1:8080/package/hls/classic/floor-v.m3u8"
|
|
@State private var player: AVPlayer?
|
|
@State private var session: WaiPlayerSession?
|
|
@State private var status = "Give an HLS address and press Play."
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
HStack {
|
|
TextField("HLS address", text: $address).textFieldStyle(.roundedBorder)
|
|
Button("Play", action: play)
|
|
}
|
|
VideoPlayer(player: player).frame(minWidth: 480, minHeight: 270)
|
|
Text(status).font(.callout).foregroundStyle(.secondary)
|
|
}
|
|
.padding()
|
|
}
|
|
|
|
private func play() {
|
|
guard let url = URL(string: address) else { status = "Not an address."; return }
|
|
do {
|
|
// The owner is the key of the collector the bundle is meant for; this player keeps it.
|
|
let config = WaiSink.Config(channel: "my-channel", owner: String(repeating: "0", count: 64))
|
|
let s = try WaiPlayerSession(url: url, config: config, attention: WaiFixedAttention())
|
|
s.onEnd = { bundle, ended in
|
|
status = "Ended (\(ended.rawValue)): a signed bundle of \(bundle.count) bytes."
|
|
}
|
|
s.start()
|
|
session = s
|
|
player = s.player
|
|
status = "Playing through the sink."
|
|
} catch {
|
|
status = "The sink refused: \(error)"
|
|
}
|
|
}
|
|
}
|