Klarity is a media (video and audio) player for Compose Multiplatform (desktop-only), built on top of the native FFMpeg and PortAudio libraries, and rendered using the Skiko library.
Since frames are rendered directly into the Composable
, this eliminates the need for compatibility components like
SwingPanel
, making it possible to display any Composable
as an overlay on top of a frame.
- Renderer no longer depends on video format - uses width and height instead
📦 Previous versions
- Fixed incorrect argument names
- Enhanced seeking precision
- Extended external state machine
- Changed rendering behavior - renders first video frame during: preparation, playback stop, seeking
- Performance and stability enhancements
- Fixed delayed external state updates after command execution
- Fixed incorrect first frame display after seeking
- Fixed event handling issues
- Fixed improper loop stopping
- Fixed buffer cleanup errors during channel closure
- Improved internal state machine
- Improved playback synchronization
- Improved seeking
- Stable release
- Windows x64
- Linux x64
- macOS x64
- Media files probing
- Audio and video playback of media files
- Slow down and speed up playback speed without changing pitch
- Getting a preview of a media file
- Getting frames (snapshots) of a media file
- Coroutine/Flow API
graph TD
KlarityPlayer --> PlayerController
PlayerController --> Pipeline
PlayerController --> BufferLoop
PlayerController --> PlaybackLoop
PlayerController --> Settings
PlayerController --> PlayerState
PlayerController --> BufferTimestamp
PlayerController --> PlaybackTimestamp
PlayerController --> Renderer
PlayerController --> Events
BufferLoop --> Pipeline
PlaybackLoop --> BufferLoop
PlaybackLoop --> Pipeline
PlaybackLoop --> Renderer
PlaybackLoop --> Settings
subgraph Pipeline
Pipeline.AudioVideo --> Media
Pipeline.AudioVideo --> AudioDecoder
Pipeline.AudioVideo --> VideoDecoder
Pipeline.AudioVideo --> AudioBuffer
Pipeline.AudioVideo --> VideoBuffer
Pipeline.AudioVideo --> Sampler
Pipeline.AudioVideo --> VideoPool
Pipeline.Audio --> Media
Pipeline.Audio --> AudioDecoder
Pipeline.Audio --> AudioBuffer
Pipeline.Audio --> Sampler
Pipeline.Video --> Media
Pipeline.Video --> VideoDecoder
Pipeline.Video --> VideoBuffer
Pipeline.Video --> VideoPool
end
Sampler --> JNI\nNativeSampler --> C++\nSampler
AudioDecoder --> JNI\nNativeDecoder
VideoDecoder --> JNI\nNativeDecoder
JNI\nNativeDecoder --> C++\nDecoder
stateDiagram-v2
state PlayerState {
[*] --> Empty
Empty --> Preparing: Prepare
Preparing --> Ready: Success
Preparing --> Error: Error
Preparing --> Empty: Release
state Ready {
[*] --> Stopped
Stopped --> Playing: Play
Playing --> Paused: Pause
Playing --> Stopped: Stop
Playing --> Seeking: SeekTo
Playing --> Error: Error
Paused --> Playing: Resume
Paused --> Stopped: Stop
Paused --> Seeking: SeekTo
Paused --> Error: Error
Stopped --> Completed: Playback Complete
Stopped --> Seeking: SeekTo
Stopped --> Error: Error
Completed --> Stopped: Stop
Completed --> Seeking: SeekTo
Completed --> Error: Error
Seeking --> Paused: Seek Complete
Seeking --> Stopped: Stop
Seeking --> Seeking: SeekTo
Seeking --> Error: Error
}
Ready --> Releasing: Release
Releasing --> Empty: Success
Releasing --> Error: Error
Error --> Empty: Reset
}
Current State \ Action | Empty | Preparing | Releasing | Ready.Stopped | Ready.Playing | Ready.Paused | Ready.Completed | Ready.Seeking | Error |
---|---|---|---|---|---|---|---|---|---|
Empty | - | Prepare | - | - | - | - | - | - | - |
Preparing | Release | - | - | Success | - | - | - | - | Error |
Releasing | Success | - | - | - | - | - | - | - | Error |
Error | Reset | - | - | - | - | - | - | - | - |
Ready.Stopped | - | - | Release | - | Play | - | Playback Complete | SeekTo | Error |
Ready.Playing | - | - | Release | Stop | - | Pause | - | SeekTo | Error |
Ready.Paused | - | - | Release | Stop | Resume | - | - | SeekTo | Error |
Ready.Completed | - | - | Release | Stop | - | - | - | SeekTo | Error |
Ready.Seeking | - | - | Release | Stop | - | Seek Complete | - | SeekTo | Error |
Download the latest release and include jar files to your project depending on your system.
Note
Check out the example to see a full implementation in Clean Architecture using the Reduce & Conquer pattern.
- The
KlarityPlayer.load()
method should be called once during the application lifecycle
KlarityPlayer.load().onFailure { t -> }.getOrThrow()
Get probe (information about a media)
val media = ProbeManager.probe("path/to/media").onFailure { t -> }.getOrThrow()
Important
Snapshot must be closed using the close()
method.
val snapshots = SnapshotManager.snapshots("path/to/media") { timestamps }.onFailure { t -> ... }.getOrThrow()
snapshots.forEach { snapshot ->
snapshot.close().onFailure { t -> }.getOrThrow()
}
val snapshot = SnapshotManager.snapshot("path/to/media") { timestamp }.onFailure { t -> ... }.getOrThrow()
snapshot.close().onFailure { t -> }.getOrThrow()
Important
PreviewManager must be closed using the
close()
method.
val previewManager = PreviewManager.create("path/to/media").onFailure { t -> ... }.getOrThrow()
previewManager.render(renderer, timestamp).onFailure { t -> }.getOrThrow()
previewManager.close().onFailure { t -> }.getOrThrow()
Important
KlarityPlayer
and Renderer must be closed using the
close()
method
val playback = KlarityPlayer.create().onFailure { t -> }.getOrThrow()
val format = checkNotNull(playback.state.media.videoFormat)
val renderer = Renderer.create(format).onFailure { t -> }.getOrThrow()
playback.attach(renderer).getOrThrow()
playback.prepare("path/to/media").onFailure { t -> }.getOrThrow()
playback.play().onFailure { t -> }.getOrThrow()
playback.stop().onFailure { t -> }.getOrThrow()
playback.close().onFailure { t -> }.getOrThrow()
renderer.close().onFailure { t -> }.getOrThrow()