Skip to content

Sources and sinks

A MediaSource is something to read media from, and a MediaSink is somewhere to write an export. Both are plain values, built through their companion factories.

val fromPath = MediaSource.of("/path/to/clip.mp4")
val fromUri = MediaSource.ofUri("content://media/external/video/media/42")
val fromBytes = MediaSource.ofBytes(bytes, hint = FormatHint.Mp4)
  • of reads a file path the process can read.
  • ofUri reads a platform reference, an android.net.Uri string on Android or an NSURL string on Apple.
  • ofBytes reads media already in memory. On Android, Apple and ffmpeg the buffer is written to a temporary file, so it costs its size on disk as well as in memory. The file stays until a later process first uses a buffer, so a process reading many distinct buffers grows disk use with no bound. ffmpeg moves its file when another user holds its directory. In a browser it stays in memory. The hint names the container, and without one it is read off the bytes. See Limits.
val toPath = MediaSink.of("/path/to/out.mp4")
val toUri = MediaSink.ofUri("content://media/external/video/media/43")
val toTemporary = MediaSink.temporary()

A path sink needs its parent directory to exist and be writable. A temporary sink lets filmstrip pick the file, and the export reports where it went.

if (status is ExportStatus.Success) {
val written = status.output as MediaSink.Path
println("Exported to ${written.path}")
}

filmstrip-io-okio and filmstrip-io-kotlinx turn their Path types into sources and sinks.

val source = "/path/to/clip.mp4".toPath().toMediaSource()
val sink = "/path/to/out.mp4".toPath().toMediaSink()
val source = Path("/path/to/clip.mp4").toMediaSource()
val sink = Path("/path/to/out.mp4").toMediaSink()

Both also read a BufferedSource or Source into a bytes source with readMediaSource().

filmstrip-io-filekit turns a picked PlatformFile into a source or a sink. Each target maps it to the kind of source its backend reads, a content uri on Android, a file url on Apple and an object url in the browser.

val source = picked.toMediaSource()
val composition = compositionOf { clip(source) }
filmstrip.export(composition, ExportSpec.Upload, saveTo.toMediaSink()).collect { status ->
println(status)
}
source.release()

In a browser a sink becomes a download named after the file. release() frees the object url a browser source holds. Calling it is optional, and does nothing on the other targets, but only call it once nothing reads the source any more.