### Run Sample Android Application Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Command to install the debug build of the Android sample application. ```bash ./gradlew :kiteplayer-sample-android:installDebug ``` -------------------------------- ### Run the demo via Gradle Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-sample-desktop/MEASUREMENTS.md Starts the sample desktop application in demo mode, showing a window with play/pause, a seek bar, and the modifier toggle. ```bash ./gradlew :kiteplayer-sample-desktop:run ``` -------------------------------- ### Play a song on desktop JVM Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md A basic example demonstrating how to open a media item, play it, perform a seek operation, and close the player using Kotlin. ```kotlin import io.github.yuroyami.kiteplayer.KitePlayer import io.github.yuroyami.kiteplayer.MediaItem import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlin.time.Duration.Companion.seconds fun main() = runBlocking { val player = KitePlayer() player.open(MediaItem("/path/to/song.mp3")) // returns when the item is open and paused player.play() delay(10.seconds) player.seek(60.seconds) // returns when the seek has landed delay(10.seconds) player.closeAndAwait() } ``` -------------------------------- ### Run verification script Source: https://github.com/yuroyami/kiteplayer/blob/main/verification/central-consumer/README.md Executes the verification script to read install lines from the README using an empty Gradle cache. ```bash ./scripts/verify-central-consumer.sh ``` -------------------------------- ### Run smoke test on simulator Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-sample/iosApp/README.md Runs the test media script and the iOS sample smoke script. The smoke script picks the newest iPhone simulator, builds and installs the app, launches it with --s1b-smoke, and waits for Documents/s1b-smoke.json. The result must contain exactly nine keys. ```bash ./scripts/testmedia.sh ./scripts/ios-sample-smoke.sh ``` -------------------------------- ### MediaIo.ofUrl(NSURL) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory for reading a file URL on Apple platforms. It starts the URL's security scope when opening and stops it when closing. ```APIDOC ## MediaIo.ofUrl(NSURL) ### Description Creates a `MediaIoFactory` for reading a file URL on Apple platforms. It starts the URL's security scope when opening and stops it when closing, so a file from the document picker needs no cleanup from the caller. ### Method Factory function ### Parameters - **url** (NSURL) - Required - The file URL to read. ### Returns - **MediaIoFactory** - A factory that opens the file for each read. ### Exceptions - **MediaIoException** - Thrown when the file cannot be opened. ``` -------------------------------- ### SubtitleTypesetter Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md Optional seam for subtitle rendering. Implement the interface, wrap it in a SubtitleTypesetterProvider with a stable id, and install the provider. ```APIDOC ## SubtitleTypesetter ### Description Optional seam for subtitle rendering. Implement the interface, wrap it in a SubtitleTypesetterProvider with a stable id, and install the provider the way the transport providers install: a META-INF/services entry on the JVM and Android, an eagerly initialised SubtitleTypesetters.register(...) on native and the web. ### Methods - Opens a track from the container header or a whole script. - Adds one event per packet in the Matroska form. - Clears events on a seek. - Renders once per video frame while playing. ### render - Returns null when the picture is what it was; the engine republishes nothing for that answer, which keeps the cadence cheap. - Returns an empty list when the picture became nothing. - The images returned are positioned in the output surface's pixels and are owned by the engine from then on, so never reuse their byte arrays. ### Threading - The engine calls every member from one lane and never concurrently, so hold no lock. ### Implementations - kiteplayer-libass is the one implementation. ``` -------------------------------- ### MediaIo.ofAsset(AssetManager, String) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory that plays a file from the app's assets directory on Android. Each open gets a new descriptor from the asset manager and reads by position. ```APIDOC ## MediaIo.ofAsset(AssetManager, String) ### Description Creates a `MediaIoFactory` that plays a file from the app's `assets` directory on Android. Each open gets a new descriptor from the asset manager and reads by position, so a reopen never moves a descriptor that the caller holds. ### Method Factory function ### Parameters - **assetManager** (AssetManager) - Required - The asset manager to use. - **assetName** (String) - Required - The name of the asset file. ### Returns - **MediaIoFactory** - A factory that opens the asset for each read. ### Notes - An asset is a window inside the app package, and the door reads only that window. - An asset must be stored uncompressed; the Android build already stores common media extensions that way. ``` -------------------------------- ### MediaIo.ofUri(ContentResolver, Uri) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory that plays what a content provider serves on Android, such as a file from the system picker. Each open gets a new descriptor from the provider and reads by position. ```APIDOC ## MediaIo.ofUri(ContentResolver, Uri) ### Description Creates a `MediaIoFactory` that plays what a content provider serves on Android, such as a file from the system picker. Each open gets a new descriptor from the provider and reads by position, so a reopen never moves a descriptor that the caller holds. ### Method Factory function ### Parameters - **contentResolver** (ContentResolver) - Required - The content resolver to use. - **uri** (Uri) - Required - The content URI to read. ### Returns - **MediaIoFactory** - A factory that opens the content for each read. ### Notes - A provider that answers with a pipe plays forward only. ``` -------------------------------- ### Declare a layered drawing's drives with mappingOf Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/audioviz-mapping-api.md A layered drawing declares its drives through mappingOf, which fills in what the drawing needs and can give up from the drawing itself. The example shows a mapping with four VizDrive entries, each combining a driver, property, optional curve, and response. ```kotlin override val mapping: VizMapping by mappingOf( VizDrive(VizDriver.Bands, VizProperty.Size), VizDrive(VizDriver.BodyHit, VizProperty.Spawn, VizCurve.Scaled, VizResponse.lifetime(1.6f)), VizDrive(VizDriver.Drop, VizProperty.Shape, VizCurve.Discrete, VizResponse.envelope(0.2f)), VizDrive(VizDriver.Pulse, VizProperty.Speed, response = VizResponse.Rate), ) ``` -------------------------------- ### Progress.firstTimeOfDayMillis and Progress.lastTimeOfDayMillis Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Returns the earliest and latest moments the stream lists, representing the start and end of the latest playlist. For recordings, these are the edges of the seekable range; for live streams, they indicate the listed window and the live edge. ```APIDOC ## Progress.firstTimeOfDayMillis and Progress.lastTimeOfDayMillis ### Description Returns the earliest and latest moments the stream lists, as milliseconds since 1970 UTC, or null if unavailable. For recordings, they are the edges of the seekable range; for live streams, they indicate the listed window and the live edge. ### Properties - `val Progress.firstTimeOfDayMillis: Long?` - `val Progress.lastTimeOfDayMillis: Long?` ### Returns - **Long?** - The earliest (first) or latest (last) time of day in milliseconds since 1970 UTC, or null if unavailable. ``` -------------------------------- ### scanAudio (generic) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/audioviz-song-scan-api.md Scans audio from a media item using a specified backend, decoding without playing. It opens its own backend session, selects an audio stream, and decodes from start to end, handing interleaved buffers to the sink. ```APIDOC ## scanAudio (generic) ### Description Scans audio from a media item using a specified backend, decoding without playing. It opens its own backend session, selects an audio stream, and decodes from start to end, handing interleaved buffers to the sink. ### Method suspend fun scanAudio(media: MediaItem, backend: MediaBackend, track: TrackId? = null, range: AudioScanRange? = null, sink: AudioScanSink): AudioScanResult ### Parameters - **media** (MediaItem) - Required - The media item to scan. - **backend** (MediaBackend) - Required - The backend to use for decoding. - **track** (TrackId?) - Optional - The audio stream to scan. Null picks the default stream: the container's default ordinary track, else the first ordinary track, else the first audio track. - **range** (AudioScanRange?) - Optional - Scans one stretch of the stream instead of all of it. Null scans all of it. - **sink** (AudioScanSink) - Required - The sink that receives decoded audio buffers. Can be passed as a trailing lambda. ### Returns AudioScanResult - Reports the stream scanned, the frames decoded, the first timestamp, the end of the last block, and whether the stream's end was reached. ### Behavior - Opens its own backend session, selects one audio stream, creates a decoder from the same factory list in the same order as playback, and decodes from start to end. - Interleaves each decoded buffer the way the playback feed does and hands it to the sink with the decoder's timestamp. - Opens no audio output and never touches the reader of a playing session. - The call blocks on reads in the caller's context. Call it from a context that may block. - Cancel the calling coroutine to stop it. The decoder, the session and its reader are closed before the call returns or throws. ``` -------------------------------- ### Run Sample macOS and iOS Applications Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Commands to link and run the macOS and iOS sample applications. ```bash ./gradlew :kiteplayer-sample:linkDebugExecutableMacosArm64", then run "kiteplayer.kexe testmedia/sync1080p30.mp4 --window"; for iOS, link "linkDebugFrameworkIosSimulatorArm64" and open "kiteplayer-sample/iosApp/KitePlayerSample.xcodeproj ``` -------------------------------- ### Run Sample Desktop Application Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Command to run the desktop sample application, with an optional argument for modifiers. ```bash ./gradlew :kiteplayer-sample-desktop:run", or add "--args='--modifiers' ``` -------------------------------- ### Run Sample Web Application Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Command to build the wasmJs browser distribution for the web sample application. ```bash ./gradlew :kiteplayer-sample-web:wasmJsBrowserDistribution", then read "kiteplayer-sample-web/MEASUREMENTS.md ``` -------------------------------- ### Open and control media in Kotlin Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Demonstrates opening a media item, playing, seeking, and closing the player using Kotlin coroutines. ```kotlin import io.github.yuroyami.kiteplayer.MediaItem import kotlin.time.Duration.Companion.seconds player.open(MediaItem("https://example.com/movie.mkv")) player.play() player.seek(90.seconds) // When the screen goes away, unless rememberKitePlayer owns the player: player.closeAndAwait() ``` -------------------------------- ### Run performance benchmarks with Gradle Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-audioviz/PERFORMANCE.md Commands to run the CPU benchmark, desktop benchmark, and survey tests. The CPU benchmark reports minimum, median, and 95th percentile. The desktop benchmark reports graphics backend, canvas size, frame cadence, CPU preparation, and feedback scale. Run with the machine idle and keep the temporary window visible. ```shell ./gradlew :kiteplayer-audioviz:audiovizBenchmark ./gradlew :kiteplayer-audioviz:audiovizDesktopBenchmark ./gradlew :kiteplayer-audioviz:audiovizSurvey --tests '*DrawCostTest' --tests '*PostSheetTest' ``` -------------------------------- ### Open and control media in Java Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Demonstrates using KitePlayerJava to handle media playback with listeners and CompletableFuture for asynchronous operations. ```java KitePlayerJava player = KitePlayerJava.create(); player.addListener(new KitePlayerListener() { @Override public void onState(PlayerSnapshot state) { statusView.setText(state.getStatus().name()); } @Override public void onProgress(Progress progress) { seekBar.setProgress((int) progress.getPositionMillis()); } }, ContextCompat.getMainExecutor(context)); player.openAsync(new MediaItemBuilder("https://example.com/movie.mkv").build()) .thenRun(() -> player.getPlayer().play()); player.seekAsync(90_000); // When the screen goes away: player.close(); ``` -------------------------------- ### Create a KitePlayer instance Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Initializes a player on the platform's default stack. Settings can be configured using a block. ```kotlin import io.github.yuroyami.kiteplayer.KitePlayer val player = KitePlayer() ``` -------------------------------- ### Configure media item settings Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Shows how to use the mediaItem builder block to configure headers, probe depth, packet corruption handling, and low latency mode. ```kotlin import io.github.yuroyami.kiteplayer.CorruptPackets import io.github.yuroyami.kiteplayer.ProbeDepth import io.github.yuroyami.kiteplayer.mediaItem val item = mediaItem("https://cdn.example.com/live/channel.ts") { header("Authorization", "Bearer $token") probe(ProbeDepth.Fast) corruptPackets(CorruptPackets.Drop) lowLatency() } ``` -------------------------------- ### KitePlayerWorker.start(canvas, workerUrl = "./kiteplayer-web-worker.mjs", codecUrl = "./kite.mjs", libassUrl = "./kiteass.mjs") Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Preserves the original public entry point for display wake compatibility. Delegates with keepDisplayAwake = true. ```APIDOC ## KitePlayerWorker.start(canvas, workerUrl = "./kiteplayer-web-worker.mjs", codecUrl = "./kite.mjs", libassUrl = "./kiteass.mjs") ### Description Preserves the original public entry point for display wake compatibility. Delegates with keepDisplayAwake = true. ### Method start ### Parameters - **canvas** - Required - The canvas element. - **workerUrl** - Optional - Defaults to "./kiteplayer-web-worker.mjs". - **codecUrl** - Optional - Defaults to "./kite.mjs". - **libassUrl** - Optional - Defaults to "./kiteass.mjs". ### Notes - Delegates with keepDisplayAwake = true. - Existing callers keep their constructor and startup signatures. ``` -------------------------------- ### Create SongMapStore and rememberAudioVizState Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/audioviz-song-scan-api.md Creates a SongMapStore in the app's cache directory and passes it to rememberAudioVizState. The store writes one file per map and deletes the oldest when exceeding the default limit of 128 maps. ```kotlin val maps = SongMapStore.inDirectory(File(context.cacheDir, "songmaps").absolutePath) val viz = rememberAudioVizState(player, songMapStore = maps) ``` -------------------------------- ### MediaIo.ofFile Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory for reading a file on JVM and Android. Each open opens its own channel and reads by position. ```APIDOC ## MediaIo.ofFile ### Description Creates a `MediaIoFactory` for reading a file on JVM and Android. Each open opens its own channel and reads by position. ### Method Factory function ### Parameters - **file** (File) - Required - The file to read. ### Returns - **MediaIoFactory** - A factory that opens the file for each read. ### Exceptions - **MediaIoException** - Thrown when the file cannot be opened. ``` -------------------------------- ### Open a content URI with MediaIo Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Demonstrates using MediaIo.ofUri to open a content provider URI, such as one obtained from a file picker, and playing it with the player. ```kotlin import io.github.yuroyami.kiteplayer.MediaIo import io.github.yuroyami.kiteplayer.MediaItem import io.github.yuroyami.kiteplayer.from import io.github.yuroyami.kiteplayer.io.ofUri player.open(MediaItem.from(MediaIo.ofUri(contentResolver, uri), label = "picked.mkv")) player.play() ``` -------------------------------- ### Build and test native code with plain, asan, and tsan variants Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-rt/README.md Runs the build and test scripts for the plain, asan, and tsan variants. The interpose command runs the plain binaries with allocation accounting required. The render-audit and source-discipline scripts can be run with --prove-it-can-fail for negative controls and planted defects respectively. ```bash cd native ./scripts/build-host.sh plain && ./scripts/run-c-tests.sh plain ./scripts/build-host.sh asan && ./scripts/run-c-tests.sh asan ./scripts/build-host.sh tsan && ./scripts/run-c-tests.sh tsan ./scripts/run-c-tests.sh interpose # the plain binaries, with allocation accounting required ./scripts/render-audit.sh # add --prove-it-can-fail for the seven negative controls ./scripts/source-discipline.sh # add --prove-it-can-fail for the sixteen planted defects ``` -------------------------------- ### Run ShaderBenchmark with Gradle classpath Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-sample-desktop/MEASUREMENTS.md Command to rerun the benchmark from the kiteplayer-sample-desktop module. Uses the printRunClasspath Gradle task to build the classpath and invokes the ShaderBenchmarkKt main class. ```bash java -cp "$(./gradlew -q :kiteplayer-sample-desktop:printRunClasspath)" \ io.github.yuroyami.kiteplayer.sample.desktop.ShaderBenchmarkKt ``` -------------------------------- ### Test a backend through the public API with KitePlayer Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md Builds a player with a custom backend and a desktop output backend, plays a media item to completion, and asserts that all handles handed to the engine were closed. Runs in real time and requires an audio device when the file has sound. The `openHandles` counter is your own ledger that must be decremented in `close`. ```kotlin @Test fun myBackendPlaysAFileToTheEnd() = runBlocking { val backend = MyMediaBackend() KitePlayer.create(PlayerConfig(backends = Backends(backend, DesktopOutputBackend))).use { player -> player.open(MediaItem("my://five-seconds")) player.play() withTimeout(30.seconds) { player.state.first { it.status == PlaybackStatus.Ended } } } assertEquals(0, backend.openHandles, "the engine closes everything it was handed") } ``` -------------------------------- ### Run the measurement via Gradle Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-sample-desktop/MEASUREMENTS.md Runs the measurement through Gradle, setting the measurement flag, frame count, and repeat count via project properties. ```bash ./gradlew :kiteplayer-sample-desktop:run \ -Pkiteplayer.sample.measure -Pkiteplayer.sample.frames=320 -Pkiteplayer.sample.repeats=4 ``` -------------------------------- ### Create fileSegmentStore and configure player Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/segment-cache.md Creates a file-based segment store with a directory and size limit, then passes it to the player via NetworkConfig.segmentStore. The store is off by default; without it, the player sends the same requests as before. fileSegmentStore returns null in a browser or when the directory cannot be used, which is safe to pass on. Close the store when the application is done with it; the player never closes it. ```kotlin val store = fileSegmentStore( directory = "$cacheDirectory/kite-segments", maxBytes = 512L * 1024 * 1024, ) val player = KitePlayer.create( PlayerConfig(network = NetworkConfig(segmentStore = store)), ) ``` -------------------------------- ### Development Workflow Scripts Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Essential scripts for generating test media and running the local contribution gate. ```bash ./scripts/testmedia.sh # generate the test clips, needs ffmpeg on PATH ./scripts/check-gate.sh tier1 # the checks every change runs ``` -------------------------------- ### Run the scripted backend with CoreHarness in virtual time Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md Uses the repository's own test fake, CoreHarness, to run a full playback session in milliseconds of virtual time. The harness opens a scripted media item, plays it, and asserts the final status is Ended. This is only available inside the repository, not for external projects. ```kotlin val harness = CoreHarness(this) // in a runTest block harness.openWithRenderer() // open scripted://media, first frame presented, Paused harness.core.play() harness.run(2.seconds) // virtual: the whole file plays in wall-microseconds assertEquals(PlaybackStatus.Ended, harness.core.snapshots.value.status) ``` -------------------------------- ### KitePlayerMediaSession(player, skipInterval = 15.seconds) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a media session for Apple platforms. The session manages Now Playing metadata, transport controls, and interruption policies. ```APIDOC ## KitePlayerMediaSession(player, skipInterval = 15.seconds) ### Description Creates a media session for Apple platforms. The session manages Now Playing metadata, transport controls, and interruption policies. ### Method Constructor ### Parameters - **player** - Required - The player instance. - **skipInterval** - Optional - Defaults to 15.seconds. Must be positive; invalid nonpositive values are refused before registering a system handler. ### Notes - Construction belongs to the main thread. - One process has one Now Playing owner. A new successfully registered session takes ownership, retires the previous owner's command targets, and leaves the previous player's transport alone. - The previous session keeps its policy subscriptions until it closes. - A setup failure removes every handler acquired so far. - The session returned by attachMediaSession closes with its player. ``` -------------------------------- ### RecordingCapable Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md Optional interface on the source that enables KitePlayer.startRecording. Implement startRecording, stopRecording, and recordingPath to support recording. ```APIDOC ## RecordingCapable ### Description Optional interface on the source. Implement it to enable KitePlayer.startRecording. Without it, that call throws UnsupportedOperationException. ### Methods #### startRecording(path) - **path** (string) - Required - The file path where packets will be copied. - Starts copying the packets your source reads into a file. #### stopRecording() - Finishes the file. Closing the source also finishes the recording. #### recordingPath - Property that is null when no recording runs. ### Threading and Locking - The engine calls these three members from its own thread while the demux worker reads, so guard the recording state with a lock. - The engine ends a recording before it seeks or retires the session, so a file never gets a jump in it. ### Implementations - ScriptedRecordingSource logs the calls and writes nothing. - SourceRecorder in kiteplayer-ffmpeg is the real one, and it writes Matroska with no re-encode. ``` -------------------------------- ### Build per-target archives with Gradle Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-rt/README.md Builds the shipped per-target archives for macOS and iOS targets using Gradle, one directory per konan target. Also runs tests for macOS and iOS simulator. ```bash ./gradlew :kiteplayer-rt:compileKiteRtCForMacosArm64 \ :kiteplayer-rt:compileKiteRtCForIosArm64 \ :kiteplayer-rt:compileKiteRtCForIosSimulatorArm64 ./gradlew :kiteplayer-rt:macosArm64Test ./gradlew :kiteplayer-rt:iosSimulatorArm64Test --device "Test iPhone 17" ``` -------------------------------- ### AppKitVideoRenderer(window, convert, toneMapped = { false }) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Preserves the original public entry point for display wake compatibility. Delegates with keepDisplayAwake = true. ```APIDOC ## AppKitVideoRenderer(window, convert, toneMapped = { false }) ### Description Preserves the original public entry point for display wake compatibility. Delegates with keepDisplayAwake = true. ### Method Constructor ### Parameters - **window** - Required - The window. - **convert** - Required - The conversion function. - **toneMapped** - Optional - Defaults to { false }. A trailing tone-map lambda. ### Notes - Delegates with keepDisplayAwake = true. - Existing callers keep their constructor and startup signatures. ``` -------------------------------- ### Configure native view controls Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Enables default controls and full-screen callbacks for native views. ```kotlin view.showsControls = true view.onFullScreen = { /* your window, your rule */ } ``` -------------------------------- ### Run network verification per target with --run and --target Source: https://github.com/yuroyami/kiteplayer/blob/main/verification/network-consumer/README.md Runs the selected target against a server on the host. Android uses 10.0.2.2 as the emulator's host alias; a physical device needs a reachable server address. For TLS use an HTTPS URL with a valid trusted certificate and nonempty response. --target all requires an iOS simulator, and Android joins only when --android-serial is set. ```bash scripts/verify-network-distribution.sh --run --target jvm --url http://localhost:8765/media scripts/verify-network-distribution.sh --run --target macos --url http://localhost:8765/media scripts/verify-network-distribution.sh --run --target ios --simulator UDID --url http://localhost:8765/media scripts/verify-network-distribution.sh --run --target wasm --node /path/to/node --url http://localhost:8765/media scripts/verify-network-distribution.sh --run --target android --android-serial emulator-5554 --url http://10.0.2.2:8765/media ``` -------------------------------- ### PlayerMediaSource SPI methods Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md SPI methods for media sources to provide time-of-day information. Default implementations return null, so existing sources keep compiling. ```APIDOC ## PlayerMediaSource SPI methods ### Description SPI methods for media sources to provide time-of-day information. Default implementations return null, so existing sources keep compiling. ### Methods - `timeOfDayAt(position: Pts): Long?` - Returns the time of day for a given position. - `positionAtTimeOfDay(epochMillis: Long): Pts?` - Returns the position for a given time of day. - `timeOfDaySpan: LongRange?` - Returns the range of times of day available, or null. ``` -------------------------------- ### Allowed kitert cinterop file: AudioRingDifferentialTest.kt Source: https://github.com/yuroyami/kiteplayer/blob/main/kitert-coupling-baseline.txt Allows the file `kiteplayer-core/src/nativeTest/kotlin/io/github/yuroyami/kiteplayer/AudioRingDifferentialTest.kt` to reference kitert cinterop symbols without failing the coupling check. ```text allowed_kitert_file kiteplayer-core/src/nativeTest/kotlin/io/github/yuroyami/kiteplayer/AudioRingDifferentialTest.kt ``` -------------------------------- ### Display player in Compose with KitePlayerVideo Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Uses rememberKitePlayer to manage the player lifecycle and KitePlayerVideo to render the output in Compose. ```kotlin import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import io.github.yuroyami.kiteplayer.MediaItem import io.github.yuroyami.kiteplayer.compose.KitePlayerVideo import io.github.yuroyami.kiteplayer.compose.rememberKitePlayer val player = rememberKitePlayer() KitePlayerVideo(player, Modifier.fillMaxSize()) LaunchedEffect(Unit) { player.open(MediaItem("https://example.com/movie.mkv")) player.play() } ``` -------------------------------- ### Add external audio to a media item Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Demonstrates attaching an external audio source to a media item, which allows the player to treat the audio file as an additional selectable track. ```kotlin val item = mediaItem("https://cdn.example.com/film.mp4") { externalAudio(AudioSource("https://cdn.example.com/film.fr.m4a", title = "French", language = "fr")) } ``` -------------------------------- ### MediaItem.from(io, label) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Constructs a MediaItem using a given MediaIoFactory and a label used as its URI for probing and display. This is a companion extension in the core package. ```APIDOC ## MediaItem.from(io, label) ### Description Constructs a `MediaItem` using the provided `MediaIoFactory` and a label that serves as its URI for probing and display. This is a companion extension in the core package. ### Method Constructor function ### Parameters - **io** (MediaIoFactory) - Required - The factory to use for reading media data. - **label** (String) - Required - The label used as the URI for probing and display. ### Returns - **MediaItem** - A media item that uses the given factory. ``` -------------------------------- ### Run the measurement without Gradle daemon Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-sample-desktop/MEASUREMENTS.md Runs the measurement directly with Java, using the classpath printed by Gradle and system properties for measurement settings. This is the method used to produce the numbers in the document. ```bash CP=$(./gradlew -q :kiteplayer-sample-desktop:printRunClasspath) java -cp "$CP" \ -Dkiteplayer.sample.measure=true \ -Dkiteplayer.sample.frames=320 \ -Dkiteplayer.sample.repeats=4 \ -Dkiteplayer.sample.media="$PWD/testmedia/sync1080p30.mp4" \ io.github.yuroyami.kiteplayer.sample.desktop.MainKt ``` -------------------------------- ### WebCanvasVideoRenderer(canvas, painter) and WebCanvasVideoRendererFactory(canvas, painter) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Delegating overloads that preserve the original public entry points for display wake compatibility. They delegate with keepDisplayAwake = true. ```APIDOC ## WebCanvasVideoRenderer(canvas, painter) and WebCanvasVideoRendererFactory(canvas, painter) ### Description Preserves the original public entry points for display wake compatibility. These overloads delegate with keepDisplayAwake = true. ### Method Constructor / Factory ### Parameters - **canvas** - Required - The canvas element. - **painter** - Required - The WebFramePainter lambda. ### Notes - Delegates with keepDisplayAwake = true. - Existing callers keep their constructor and startup signatures. ``` -------------------------------- ### Allowed kitert cinterop file: NativeAudioRing.kt Source: https://github.com/yuroyami/kiteplayer/blob/main/kitert-coupling-baseline.txt Allows the file `kiteplayer-core/src/nativeMain/kotlin/io/github/yuroyami/kiteplayer/internal/NativeAudioRing.kt` to reference kitert cinterop symbols without failing the coupling check. ```text allowed_kitert_file kiteplayer-core/src/nativeMain/kotlin/io/github/yuroyami/kiteplayer/internal/NativeAudioRing.kt ``` -------------------------------- ### Add KitePlayer dependencies to Gradle Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Add these dependencies to your commonMain source set. Choose between the base library or the Compose-enabled version, and optionally include the audio visualizer. ```kotlin commonMain.dependencies { implementation("io.github.yuroyami:kiteplayer:0.3.0") // native views, no Compose // or implementation("io.github.yuroyami:kiteplayer-compose:0.3.0") // Compose, plus everything above implementation("io.github.yuroyami:kiteplayer-audioviz:0.3.0") // optional: a visualiser for audio } ``` -------------------------------- ### create (factory method) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md Creates a decoder instance. Return null to refuse a stream; the engine tries the next factory and deselects the stream when every candidate refused. ```APIDOC ## create (factory method) ### Description Creates a decoder instance. Return null to refuse a stream; the engine tries the next factory and deselects the stream when every candidate refused. ### Method create ### Returns - **decoder** - A decoder instance, or null to refuse the stream. ### Notes - The factory list is ordered best first. - When every candidate refuses, the stream is deselected with a TrackDeselected warning naming why. ``` -------------------------------- ### Add default controls to KitePlayerVideo Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Displays the default control set over the video component. ```kotlin KitePlayerVideo(player, Modifier.fillMaxSize()) { KitePlayerControls(player) } ``` -------------------------------- ### MediaIo.ofPath Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory for reading a file by path. On JVM and Android, each open opens its own channel. On Apple and Linux, each open opens the file and reads with pread. ```APIDOC ## MediaIo.ofPath ### Description Creates a `MediaIoFactory` for reading a file by path. On JVM and Android, each open opens its own channel. On Apple and Linux, each open opens the file and reads with `pread`. ### Method Factory function ### Parameters - **path** (String or Path) - Required - The path to the file. ### Returns - **MediaIoFactory** - A factory that opens the file for each read. ### Exceptions - **MediaIoException** - Thrown when the file cannot be opened. ``` -------------------------------- ### SpectrumTimeline.eventCursor() Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/audioviz-events-api.md Creates a cursor for one consumer to sample events in a time range. The cursor delivers retained events in (previousMediaTime, currentMediaTime] in sequence order, without repetition, and independent cursors do not consume each other's events. ```APIDOC ## SpectrumTimeline.eventCursor() ### Description Creates a cursor for one consumer to sample events in a time range. The cursor delivers retained events in (previousMediaTime, currentMediaTime] in sequence order, without repetition, and independent cursors do not consume each other's events. ### Method Create cursor ### Parameters None ### Returns A cursor object with a `sample` method. ### Cursor.sample(currentMediaTime) #### Description Delivers every retained event in (previousMediaTime, currentMediaTime] in sequence order, including multiple hits of one kind. Sampling again does not repeat them. #### Parameters - **currentMediaTime** (number) - Required - The current media time in seconds. #### Behavior - Events are delivered in sequence order. - Multiple hits of one kind are delivered. - Sampling again does not repeat events. - Independent cursors do not consume one another's events. ### Lateness Handling - If a detection arrives after a cursor has passed its original time, the cursor offers it once with its lateness when that lateness is within its source's budget: 30 ms for a transient and 3 s for a structural confirmation. - Older late detections are discarded and counted. - Budget is in media time; presentation-time lateness also depends on playback rate and output route. - An on-time event inside the normal display interval is not classified as a late arrival merely because its timestamp precedes the current frame's time. ### Reset Conditions The cursor resets at the current time under the following conditions: - Initial attachment - Explicit reset - Changed history - Backward media movement - Media-time advance over 250 ms - Overwritten unread event On reset, it discards missed past bursts and preserves future events. ### Paused Sampling Paused sampling advances the discard boundary without emitting events. Detections that arrive later but predate this boundary are also counted as discarded. No event is retimed to the current frame to hide lateness. ``` -------------------------------- ### MediaIo.ofBytes(bytes) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory that reads from an in-memory byte array. Each open owns an independent cursor and close state. The reader is seekable, reports its size, and rejects reads and seeks after close. ```APIDOC ## MediaIo.ofBytes(bytes) ### Description Creates a `MediaIoFactory` in `kiteplayer-core` that reads from an in-memory byte array. Each open owns an independent cursor and close state over the original array. No copy is made; the caller must keep the bytes unchanged while a reader is open. ### Method Factory function ### Parameters - **bytes** (ByteArray) - Required - The byte array to read from. ### Behavior - The reader is seekable and reports its size. - Accepts positions from zero through size. - Rejects reads and seeks after close. - Invalid destination slices and seek positions throw `IllegalArgumentException` without moving the cursor. - Empty reads return zero, including at EOF; other EOF reads return -1. ### Returns - **MediaIoFactory** - A factory that creates readers over the given byte array. ``` -------------------------------- ### MediaIo.ofStream Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Creates a MediaIoFactory that reads from an input stream on JVM and Android. ```APIDOC ## MediaIo.ofStream ### Description Creates a `MediaIoFactory` that reads from an input stream on JVM and Android. ### Method Factory function ### Parameters - **stream** (InputStream) - Required - The input stream to read from. ### Returns - **MediaIoFactory** - A factory that reads from the given stream. ``` -------------------------------- ### KitePlayerMediaSession.isAvailable Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Indicates whether the media session is available. Always true on both iOS and macOS. ```APIDOC ## KitePlayerMediaSession.isAvailable ### Description Indicates whether the media session is available. Always true on both iOS and macOS. ### Property - **isAvailable** (Boolean) - Always true. ``` -------------------------------- ### QueueConfig data class Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/gapless-queue.md Configuration for queue gapless playback. Fields are preloadNext (defaults to 5 seconds), gapless (defaults to true), reshuffleEachLap (false), onItemFailure (QueueItemFailure.Stop), crossfade (Duration.ZERO). ```kotlin public data class QueueConfig( val preloadNext: Duration = 5.seconds, val gapless: Boolean = true, val reshuffleEachLap: Boolean = false, val onItemFailure: QueueItemFailure = QueueItemFailure.Stop, val crossfade: Duration = Duration.ZERO, ) ``` -------------------------------- ### Configure KitePlayer to use KiteFFmpegResampler Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md Sets the resampler to KiteFFmpegResampler in the audio configuration. This replaces the default windowed-sinc filter. Note that KiteFFmpegResampler throws on the web because the web build lacks a filter graph. ```kotlin val player = KitePlayer { audio { resampler = KiteFFmpegResampler() } } ``` -------------------------------- ### Configure File Segment Store Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Initializes a file-based segment store to cache HLS or DASH segments on disk, reducing network usage across sessions. ```kotlin val store = fileSegmentStore("$cacheDirectory/kite-segments", maxBytes = 512L * 1024 * 1024) val player = KitePlayer.create(PlayerConfig(network = NetworkConfig(segmentStore = store))) ``` -------------------------------- ### Link unsigned device app Source: https://github.com/yuroyami/kiteplayer/blob/main/kiteplayer-sample/iosApp/README.md Builds the release framework for iOS arm64 and then uses xcodebuild to build the app without code signing. The bundle identifier is io.github.yuroyami.kiteplayer.sample.ios. The project copies testmedia/sync1080p30.mp4 into the app bundle. ```bash ./gradlew :kiteplayer-sample:linkReleaseFrameworkIosArm64 --rerun-tasks xcodebuild \ -project kiteplayer-sample/iosApp/KitePlayerSample.xcodeproj \ -scheme KitePlayerSample -configuration Release \ -destination 'generic/platform=iOS' \ -derivedDataPath kiteplayer-sample/iosApp/build/DeviceDerivedData \ CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO build ``` -------------------------------- ### Attach Media Session for Background Playback Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Attaches the media session to the player to enable background playback, notification controls, and audio focus management. ```kotlin import io.github.yuroyami.kiteplayer.session.MediaNotificationOptions import io.github.yuroyami.kiteplayer.session.attachMediaSession player.attachMediaSession(context, MediaNotificationOptions(smallIcon = R.drawable.ic_notification)) ``` -------------------------------- ### KitePlayer.attachMediaSession(background = ContinueAudio, interruptions = InterruptionPolicy(), pictureInPicture = null, skipInterval = 15.seconds) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Attaches a media session to a KitePlayer instance. Returns a KitePlayerMediaSession that closes with the player. ```APIDOC ## KitePlayer.attachMediaSession(background = ContinueAudio, interruptions = InterruptionPolicy(), pictureInPicture = null, skipInterval = 15.seconds) ### Description Attaches a media session to a KitePlayer instance. Returns a KitePlayerMediaSession that closes with the player. ### Method attachMediaSession ### Parameters - **background** - Optional - Defaults to ContinueAudio. The background behavior. - **interruptions** - Optional - Defaults to InterruptionPolicy(). The interruption policy. - **pictureInPicture** - Optional - Defaults to null. Picture-in-picture configuration. - **skipInterval** - Optional - Defaults to 15.seconds. Must be positive; invalid nonpositive values are refused before registering a system handler. ### Returns - **KitePlayerMediaSession** - The attached media session. ### Notes - Construction belongs to the main thread. - One process has one Now Playing owner. A new successfully registered session takes ownership, retires the previous owner's command targets, and leaves the previous player's transport alone. - The previous session keeps its policy subscriptions until it closes. - A setup failure removes every handler acquired so far. - The session returned by attachMediaSession closes with its player. ``` -------------------------------- ### CueLayout and CueRegion Data Models Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/subtitle-regions.md Defines the data classes for subtitle layout and region configuration, including optional fields for region positioning and styling. ```kotlin public data class CueLayout( // ... the fields of today, then: val region: CueRegion? = null, val regionOrder: Int = 0, ) public data class CueRegion( val id: String, val left: Float, val top: Float, val width: Float, val height: Float, val padding: CueInsets = CueInsets.None, val displayAlign: CueDisplayAlign = CueDisplayAlign.Before, val backgroundColor: Int = 0x00000000, val showBackground: CueShowBackground = CueShowBackground.Always, val clip: Boolean = true, ) public data class CueInsets(val left: Float, val top: Float, val right: Float, val bottom: Float) { public companion object { public val None: CueInsets } } public enum class CueDisplayAlign { Before, Center, After } public enum class CueShowBackground { Always, WhenActive } ``` -------------------------------- ### KitePlayerMediaSession.platformToken Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Exposes the platform token as null on both iOS and macOS. ```APIDOC ## KitePlayerMediaSession.platformToken ### Description Exposes the platform token as null on both iOS and macOS. ### Property - **platformToken** (Any?) - Always null. ``` -------------------------------- ### Display KiteAudioViz conditionally Source: https://github.com/yuroyami/kiteplayer/blob/main/README.md Use KiteAudioViz to render audio visualizations when the player state indicates audio-only content. Ensure the state is initialized outside the conditional branch to maintain listening status. ```kotlin import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import io.github.yuroyami.kiteplayer.audioviz.KiteAudioViz import io.github.yuroyami.kiteplayer.audioviz.isAudioOnly import io.github.yuroyami.kiteplayer.audioviz.rememberAudioVizState import io.github.yuroyami.kiteplayer.compose.KitePlayerVideo val viz = rememberAudioVizState(player) val snapshot by player.state.collectAsState() if (snapshot.isAudioOnly) { KiteAudioViz(viz, Modifier.fillMaxSize()) } else { KitePlayerVideo(player = player, modifier = Modifier.fillMaxSize()) } ``` -------------------------------- ### WebCanvasRendererFactory(canvas) Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/module-contract.md Preserves the original public entry point for display wake compatibility. Delegates with keepDisplayAwake = true. ```APIDOC ## WebCanvasRendererFactory(canvas) ### Description Preserves the original public entry point for display wake compatibility. Delegates with keepDisplayAwake = true. ### Method Factory ### Parameters - **canvas** - Required - The canvas element. ### Notes - Delegates with keepDisplayAwake = true. - Existing callers keep their constructor and startup signatures. ``` -------------------------------- ### KiteFFmpegResampler Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/spi-cookbook.md The provided implementation of AudioResamplerFactory that uses FFmpeg's libswresample. It throws on web builds because the web build has no filter graph. ```APIDOC ## KiteFFmpegResampler ### Description The provided implementation of AudioResamplerFactory that uses FFmpeg's libswresample. It throws on web builds because the web build has no filter graph. ### Usage ```kotlin val player = KitePlayer { audio { resampler = KiteFFmpegResampler() } } ``` ### Notes - Implements AudioResamplerFactory. - Throws on web builds. ``` -------------------------------- ### Visualization.mapping Source: https://github.com/yuroyami/kiteplayer/blob/main/docs/audioviz-mapping-api.md Returns a VizMapping for a drawing, or null if the drawing declares nothing. Built-in drawings declare one. Layered drawings use mappingOf to declare their drives. ```APIDOC ## Visualization.mapping ### Description Returns a `VizMapping` for a drawing, or null if the drawing declares nothing. Every built-in drawing declares one. A drive covers the whole picture, including the ground, the detail layer and the camera. ### Method Property access ### Endpoint `Visualization.mapping` ### Returns - **VizMapping** - The mapping for the drawing, or null if none declared. ### Example ```kotlin override val mapping: VizMapping by mappingOf( VizDrive(VizDriver.Bands, VizProperty.Size), VizDrive(VizDriver.BodyHit, VizProperty.Spawn, VizCurve.Scaled, VizResponse.lifetime(1.6f)), VizDrive(VizDriver.Drop, VizProperty.Shape, VizCurve.Discrete, VizResponse.envelope(0.2f)), VizDrive(VizDriver.Pulse, VizProperty.Speed, response = VizResponse.Rate), ) ``` ```