MediaIo

Reads media bytes from anywhere Kotlin can reach.

This is how an application plays from its own HTTP client with its own authentication, from an Android content:// URI, from KiteTorrent, from an encrypted store, or from a byte array it already holds.

Threading: called from the demux worker only, one call at a time, never concurrently. Implementations do not need to be thread safe. They may suspend.

Implemented by the FFmpeg backend since the custom AVIO bridge, and accepted at both MediaItem.io and SubtitleSource.io. The demux worker waits on read. When no byte arrives for BufferPolicy.stallTimeout, the engine interrupts the source and the session ends with PlaybackError.SourceStalled. A backend interrupts a read by cancelling its coroutine, so read and seek must suspend in a way that cancellation can end. A read that blocks its thread instead cannot be stopped.

Inheritors

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
open val contentType: String?

The media type the bytes arrived with, such as the Content-Type of an HTTP response, or null when the reader does not know it. The backend uses it to recognise an HLS playlist whose address does not end in .m3u8.

Link copied to clipboard
open val location: String?

The address these bytes came from, after any redirect, or null when the reader has none. The backend resolves the relative addresses inside the media against it, such as the segments of an HLS playlist. A reader that sets it should implement openRelated too.

Link copied to clipboard
abstract val seekable: Boolean

False disables seeking in the player for this item.

Link copied to clipboard
abstract val size: Long?

Total size in bytes, or null when unknown, for example a live stream.

Functions

Link copied to clipboard
abstract override fun close()

Releases whatever this reader holds. Called exactly once by the engine per reader it made, and it must tolerate being called twice: an open that fails part way is unwound from both sides, and a reader that throws on a second close turns a handled failure into a new one.

Link copied to clipboard

How fast the network delivers this reader's bytes, in bits per second, or null when the reader does not measure it or has not measured enough yet. The default answers null.

Link copied to clipboard
open suspend fun openRelated(uri: String): MediaIo?

A new reader for uri, an absolute address that this reader's media names, or null to refuse it. An HLS playlist names its variant playlists, segments and keys this way, and the backend opens each one through here. The addresses come from the media, which is untrusted input, so open only the schemes and hosts you expect. The caller closes the reader. The default refuses every address.

Link copied to clipboard
abstract suspend fun read(into: ByteArray, offset: Int, length: Int): Int

Reads at most length bytes into into starting at offset.

Link copied to clipboard
abstract suspend fun seek(position: Long)

Moves the read cursor. Only called when seekable is true.

Link copied to clipboard
open fun setWarningSink(sink: (PlaybackWarning) -> Unit)

Where this reader reports a problem that it recovered from, such as a dropped connection that it opened again. The engine installs its warning reporter here before the first read, and the warnings reach KitePlayer.events. The sink may be called from any thread and must stay cheap. The default ignores it, for a reader with nothing to report.

Link copied to clipboard

A server's refusal of an address this reader, or one it opened, had been reading, once, or null, the default (#453). A signed address that expired gets one: the server answers 401 or 403 to the next segment, the next playlist reload or the next range of the file, after the item had opened. The engine asks on its own passes and opens the item again through its resolver or its io factory, which hand out a fresh address, at the position it reached.

Link copied to clipboard
open fun takeTags(): Map<String, String>?

The tags the bytes of the last read brought, or null, the default, when it brought none (#423): above all the song an internet radio station names in a title block between its audio bytes. The backend asks after every read that returned bytes, on the thread that read, and the tags belong at the first byte of that read, so a reader that stops each read where its next tags belong places them exactly.