PipedMediaIo

class PipedMediaIo(capacityChunks: Int = 16) : MediaIo

A bounded pipe for bytes that arrive by push: a socket you read yourself, a decryptor, or a download in flight. Your code calls write, then finish or fail. The engine reads.

It reads like a live stream. Its size is unknown and it cannot seek, so the player cannot seek in it or switch its video track. It holds capacityChunks chunks of up to 256 KiB, which is 4 MiB by default, and write waits while it is full.

A pipe is read once. A track switch, a loop or a recovery opens the item again, and that open needs a new pipe with its own producer. So a factory that can reopen makes a new pipe per call:

val item = MediaItem.from(MediaIoFactory { PipedMediaIo().also { scope.launch { produce(it) } } }, "live")

Constructors

Link copied to clipboard
constructor(capacityChunks: Int = 16)

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
open override val seekable: Boolean

False disables seeking in the player for this item.

Link copied to clipboard
open override val size: Long?

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

Functions

Link copied to clipboard
open override fun close()

Closes the reader side. A write that waits for room, and every later write, is cancelled.

Link copied to clipboard
fun fail(cause: Throwable)

Fails the stream. The next read throws cause, and the open or the playback fails with it.

Link copied to clipboard
fun finish()

Ends the stream. The engine reads what is still in the pipe and then sees the end.

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
open suspend override fun read(into: ByteArray, offset: Int, length: Int): Int

Reads at most length bytes into into starting at offset.

Link copied to clipboard
open suspend override 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.

Link copied to clipboard
suspend fun write(bytes: ByteArray, offset: Int = 0, length: Int = bytes.size - offset)

Adds a copy of length bytes of bytes from offset, so bytes is free again when this returns. Waits while the pipe is full. Call it from one producer at a time.