Repository navigation
0.3.0 is a big one: 193 issues closed since 0.2.0. A good part of the engine changed under the hood. If something that used to work now acts up, tell me on the tracker.
The main things that landed:
- HLS and DASH run on KitePlayer's own network stack now. The player lists the qualities, then steps between them on its own. Most switches happen in place. Segments can also be cached on disk (opt-in).
- Live sources: RTSP, RTMP, UDP, RTP. Internet radio reconnects when the server drops it, showing the current song as the title.
- HDR10 and HLG show as real HDR on Apple or Android screens that can do it, instead of being tone mapped down. Dolby Vision profile 5 gets its colours right.
- Subtitles: closed captions (CEA-608/708), DVB teletext, PGS, VobSub, SAMI, MicroDVD, TTML, synced lyrics. A second subtitle can follow your language list.
- Audio: speed changes without a seek or a gap, pitch shift, night mode, dialogue level, stereo modes, skip silence, crossfade between queue items.
- Playlists (M3U, PLS, XSPF) open straight into the queue, cue sheets too. The queue can skip past an item that will not open.
- The browser gets a player that lives in a Web Worker.
- Setup is shorter:
KitePlayer(),rememberKitePlayer(),PlayerConfig { },attachMediaSession, built-in player controls, a Java layer (KitePlayerJava). - The Anime4K upscaler runs on the Android GPU renderer or on Metal.
- Windows decodes video on the GPU through Direct3D 11.
Under all of it is still FFmpeg 9.0.2, which I build for every platform as KiteFFmpeg 0.5.1.
Usage
// Native views, no Compose: the engine, FFmpeg, HTTP(S), libass subtitles, the input doors
implementation("io.github.yuroyami:kiteplayer:0.3.0")
// Compose: everything above, plus KitePlayerVideo, which picks a video path at runtime
implementation("io.github.yuroyami:kiteplayer-compose:0.3.0")
// Optional, next to either line
implementation("io.github.yuroyami:kiteplayer-audioviz:0.3.0") // an audio visualiser for files with no picture, in Compose
// Or build your own player from the parts
implementation("io.github.yuroyami:kiteplayer-core:0.3.0") // the engine and its service interfaces, with no FFmpeg, Ktor or Compose
implementation("io.github.yuroyami:kiteplayer-ffmpeg:0.3.0") // the FFmpeg backend (pulls KiteFFmpeg)
implementation("io.github.yuroyami:kiteplayer-output:0.3.0") // audio sinks and video renderers for each platform
implementation("io.github.yuroyami:kiteplayer-network:0.3.0") // HTTP and HTTPS through Ktor, with the DASH reader
implementation("io.github.yuroyami:kiteplayer-io:0.3.0") // input doors for files, streams, content URIs and assets
implementation("io.github.yuroyami:kiteplayer-libass:0.3.0") // libass typesetting for ASS and SSA subtitles
implementation("io.github.yuroyami:kiteplayer-view:0.3.0") // native video views
implementation("io.github.yuroyami:kiteplayer-compose-ui:0.3.0") // KitePlayerVideo, for a player you build yourself
implementation("io.github.yuroyami:kiteplayer-compose-video:0.3.0") // KiteVideo, the Compose-drawn video path, to use it directly
implementation("io.github.yuroyami:kiteplayer-compose-interop:0.3.0") // KitePlayerSurface, the platform-view video path, to use it directlykiteplayer-rt, kiteplayer-subtitles, kiteplayer-adaptive and kiteplayer-view-bindings arrive with the lines above. Do not add them yourself. kiteplayer-phone is gone.
Changelog
🔴 Breaking changes
| Change | What to do |
|---|---|
#387: kiteplayer-compose-ui no longer exposes KitePlayerSurface, KiteVideo, rememberKiteVideoState or KiteVideoState. kiteplayer-compose loses them too |
Add kiteplayer-compose-interop or kiteplayer-compose-video yourself. Or draw with KitePlayerVideo |
#387: kiteplayer-phone is gone, with its phoneBackends(), KitePlayerView, KitePlayerUIView |
Depend on kiteplayer. Use the views of kiteplayer-view |
#91, #209, #376, #392, #430, #433, #453, #461, #487, #497, #547: PlaybackWarning has 11 new values: ExternalClockSilent, SegmentSkipped, VariantLowered, AudioSourceUnreadable, GrowthUnavailable, ThumbnailsUnreadable, AddressRenewed, Reconnecting, QueueItemSkipped, CropIgnored, SegmentStoreFailed |
Handle them in a when that has no else |
#102, #395: PlaybackError has new RendererIncompatible and SchemeUnsupported values |
Handle them in a when that has no else |
#509, 7517c2b: PlayerEvent has new values: TracksAdded, TrackChosenByPlayer, AudioOutputBecameNoisy |
Handle them in a when that has no else |
#68, 7517c2b: The service interface events have new values: RendererEvent.HdrShown, AudioSinkEvent.BecameNoisy |
Handle them in a when that has no else |
#389: VizNeed has new WaveformHistory and MemoryField values |
Handle them in a when that has no else |
#497: The onVideoGeometry callback of AndroidPlayerViewRendererFactory.create takes a third parameter, the PictureCrop?. So does the one of the AndroidSurfaceVideoRenderer constructor |
Add the parameter to your lambda or override |
#513: PlayerSnapshot and PlayerMemento have a new forcedPicturesOnly parameter in the middle of their constructors |
Pass the parameters after it by name |
| 5634c33: The Android artifacts need an app compileSdk of 37 with minor API level 2 (0.2.0 needed 37) | Set compileSdk { version = release(37) { minorApiLevel = 2 } } |
#392: PlayerMemento.FORMAT_VERSION is 10. An older build refuses a memento that this one wrote |
Update every reader of stored mementos |
| #376, #447, #543: HLS and DASH streams now list their qualities, switching between them by themselves. The first choice follows the renderer: HDR on an HDR display, never larger than the drawn size | Call selectVariant to hold one quality. DemuxPolicy.fit = VariantFit() gives the old first choice |
#68: HdrPolicy.Auto is the default, so HDR10 and HLG show as HDR on a display that can |
Set HdrPolicy.ToneMap to keep the tone mapped picture |
| #238: The native Mac renderer keeps the display awake while a picture plays. The web renderers do too | Pass keepDisplayAwake = false to manage the display yourself |
e0f33b2: PlayerConfig.flashGuard is FlashGuard.FollowSystem, so Apple renderers dim flashing video when Dim Flashing Lights is on |
Pass FlashGuard.Off to leave the picture as it is |
#499: SubtitleConfig.assColorMatching is on, so ASS colours follow the video's colour matrix when the script names another |
Set assColorMatching = false to keep the colours as written |
30d282f: On Windows HwdecPolicy.Auto decodes H.264, HEVC, VP9, MPEG-2, VC-1, WMV3 through Direct3D 11. Those frames report PlayerPixelFormat.Opaque |
Read planes through planeFormat, or pass HwdecPolicy.Off |
#452: An open that FFmpeg refuses as invalid data fails with PlaybackError.NotMedia, not SourceUnavailable |
Check for NotMedia where you handled bad input |
#467: A repeated item or A-B loop no longer passes through Ended or Buffering. PlayerEvent.Opened no longer fires at each turn |
Count the turns of a repeated item with PlayerEvent.Ended |
#483: A call made while 1024 requests already wait throws IllegalStateException |
Await earlier calls before you send more |
| #390: The Fireworks, Musical Spectrum and Muser drawings left the catalogue. A saved name of one now opens the first drawing | Offer the user another drawing |
The last ten rows still compile. They change behaviour only. A by wrapper of PlayerMediaSource that overrides only the old seekToKeyframe misses its new overload.
Everything since 0.2.0
| Area | Type | Change |
|---|---|---|
| Playback (32) | New Features | 🔴 #91: Added KitePlayer.setExternalClock, so playback follows a clock the app owns: small gaps close by a speed trim of at most 0.5 percent, large gaps by one seek. |
🔴 #392: Added MediaItem.externalAudio, to play audio files or streams, such as a dub or a commentary, beside an item's picture. | ||
#394: Added KitePlayerJava, a Java layer with listeners on an Executor. It has CompletableFuture versions of the 28 suspending calls. The Duration calls get millisecond versions. | ||
#425: Showed an item's own cover picture on the lock screen and in the notification. The new KitePlayer.coverArt reads that picture. | ||
🔴 #430: Added MediaItem.growth, which plays a file that is still being written. It plays to the current end, then on as the file grows. | ||
#457: Played a Compose Multiplatform Res.getUri address as it is on Android and the desktop. The new MediaIo.ofResourceUri is for apps with their own resolver. | ||
#496: Added KeyframeChoice, set with PlayerConfig.keyframeChoice or KitePlayer.setKeyframeChoice, so a keyframe seek can land after or nearest its target. | ||
| API Changes | #383: Added KitePlayer(config) for the default player and rememberKitePlayer() for Compose. The KitePlayerPlatform members they replace are deprecated. | |
#385: Added KitePlayer.attachMediaSession to own the notification, background handling and audio focus of a media session, with KitePlayer.awaitClose. The three KitePlayerPlatform attach functions are deprecated. | ||
#386: Added KitePlayer.requestSeek. It asks for a seek, then returns at once. seekLater is deprecated in its favour. The docs now say which calls wait. | ||
#388: Added PlayerConfig { }, which builds a config in a block. KitePlayer { } builds a player the same way. Both take nested audio, subtitles, network, buffer and queue blocks. | ||
| Performance | #481: Stopped a player from keeping every past status transition for its whole life: only the test harness records them now. | |
| Fixes | #234: Made MediaItem.formatHint force the named demuxer, as its KDoc says, so headerless input such as raw s16le PCM opens. | |
#235: Sent FFmpeg's warnings and errors to KiteLog with the tag ffmpeg, so FFmpeg prints nothing to stderr by default. | ||
| #374: Stopped a parked video lane from blocking the open. A file opened with video off now opens in 0.2 s instead of 10 s. Its sound stays in order. | ||
| #398: Let a stop or a close cancel the reader an open waits on, so a cancelled open of a silent server is Idle in 0.17 s, not 10.1 s. | ||
#410: Made a cancelled open stop only the session it built, so a late cancel no longer sends a newer item back to Idle. | ||
| #413: Kept the external clock's speed correction inside the legal speed range, so following a clock at 4x or 0.25x no longer fails playback. | ||
#414: Added KitePlayer.losslessEvents, a flow that never drops events. KitePlayerJava listeners now collect it. | ||
#422: Stopped cutting seeks at a media length that FFmpeg only guessed. The new PlayerSnapshot.durationIsEstimate flags such a length. | ||
| #424: Made the system's previous button restart a song that is more than three seconds in. The player now offers the button on the first item too. | ||
#435: Matched preferred languages as languages, not spellings: ja finds a track tagged jpn, pt-BR ranks above pt-PT. | ||
#471: Stopped startRecording from emptying the file at its path at once, so recording into the playing file, or a link to it, no longer destroys it. | ||
#472: Kept releasing the rest of a session when a renderer refuses to take the subtitles down. The player reports the refusal as ResourcesNotReleased. | ||
#473: Held every blocking step of close inside its one deadline, so a slow recording file no longer holds closeAndAwait past it. | ||
🔴 #483: Bounded the player's command queue. Repeated plain settings merge into the newest value. A call past 1024 waiting requests is refused with IllegalStateException. | ||
| #484: Gave back what a filter graph held when a stream changes format. The video filter graph now rebuilds when the picture size changes. | ||
#505: Played one channel of a multi-programme transport stream with its own picture, sound and subtitles. The new Tracks.programs lists the programmes. The new KitePlayer.selectProgram chooses one. | ||
| #526: Played silence from the first picture up to the first sound sample, so a file whose sound starts late no longer skips its first pictures. | ||
| #527: Played a picture that a stream starts carrying after the open, in place, as when a radio service adds a slideshow. | ||
#529: Made selectTrack(TrackKind.Video, null) turn the picture off in place, with the sound still playing. A live stream now accepts it. | ||
| #570: Kept all media of the longer stream when a file's sound or picture ends more than the read-ahead before the other. | ||
| Queue and gapless (8) | New Features | #434: Added QueueConfig.crossfade, an opt-in overlap of up to 30 seconds between the end of one queue item and the start of the next. |
#456: Added MediaItem.clip to play part of a file. .cue sheets now read as clipped tracks. Neighbouring tracks of one file join with no gap. | ||
🔴 #487: Added QueueConfig.onItemFailure. With QueueItemFailure.Skip the queue moves past an item that cannot be opened, reporting PlaybackWarning.QueueItemSkipped. | ||
#488: Added QueueConfig.reshuffleEachLap, off by default, to draw a new shuffle order on each lap of a repeating queue. | ||
#490: Added Playlists.parse, KitePlayer.readPlaylist and KitePlayer.openPlaylist, to read M3U, PLS or XSPF files into a queue. | ||
| Fixes | #409: Made openQueue and addToQueue copy the caller's list, so a later edit to it no longer changes the queue or fails the session. | |
🔴 #467: Repeated one item, or an A-B loop section, with no gap: the sound never stops, the status stays Playing at each turn. | ||
| #524: Made a repeat or a queue turn of a silent item follow on by its picture, with no seek and no Buffering blink. | ||
| Audio (14) | New Features | #429: Added KitePlayer.setSkipSilence, off by default, which cuts every pause longer than 0.2 seconds down to 0.2 seconds while no picture is shown. |
#442: Added KitePlayer.setNightMode, which brings quiet speech up and loud effects down. setDialogueLevel raises or lowers a downmix's centre channel. | ||
#462: Added KitePlayer.setStereoMode, with mono, left only, right only and swapped channels that also apply to a surround film folded to stereo. | ||
#465: Added KitePlayer.setPitch, to change the pitch in semitones on its own, together with any speed. | ||
#466: Added AudioConfig.matchOutputChannels, off by default, to prefer the track whose channel count is closest to the output's. | ||
| Fixes | #16: Kept the audio device running through a live speed change, so the 60 ms of silence and its hard cuts no longer happen. | |
| #373: Changed speed without a seek. An overlap-add stretch replaced the tempo stage. It makes about 20 waveform discontinuities a minute at 0.5x, against about 3,000. | ||
| #375: Sized the audio ring at two device buffers, so a speed, balance, equaliser or ReplayGain change on Android is heard after 463 to 543 ms, not 1,182 to 1,280 ms. | ||
#411: Linked the cancellation of scanAudio to the source's interrupt, so a cancelled scan ends even when FFmpeg is blocked in a read. | ||
#417: Moved the audio ring's segment table to the standard library's AtomicLongArray, which removes the atomicfu deprecation warnings. | ||
| #436: Left another app's music playing when an item has no audio track on Android. On iOS the music keeps playing until the first play. | ||
#446: Added MediaItem.audioContent, so Android and iOS are told that an item is music, speech or a film, with Automatic as the default. | ||
| #486: Faded the sound out before a pause, seek or stop, then back in after, so none of them clicks on Apple, the desktop or the web. | ||
#504: Turned sound past full scale down smoothly with a peak limiter instead of letting the device clip it, counted in PlaybackStats.audioLimitedFrames. | ||
| Video and rendering (16) | New Features | #67: Added RenderQuality.animationUpscaler, which offers a Fast or a Quality tier of the Anime4K CNN x2 networks. It runs on the Android GPU and Metal renderers. It is off by default. |
🔴 #68: Showed HDR10 and HLG as HDR on displays that can, through Metal on Apple or KitePlayerView on Android. HdrPolicy.ToneMap keeps the old tone mapping. | ||
🔴 #238: Kept the display awake while a picture plays in a Mac app and on a web page, through keepDisplayAwake, which is on by default. | ||
#428: Added a quarter turn and two mirrors to VideoTransform (rotationDegrees, mirrorHorizontal, mirrorVertical), applied by every renderer. | ||
| #470: Composed Dolby Vision profile 5 and 10.0 frames to HDR10, so they no longer show green or purple. Each scene is tone mapped from its own peak. | ||
#561: Counted flashes to and from a saturated red in VideoFlashGuard, as WCAG 2.2 describes. The guard dims the picture during a red run. | ||
🔴 e0f33b2: Added the flash guard, which dims flashing video in the built-in renderers (not a direct MediaCodec surface), set by PlayerConfig.flashGuard or KitePlayer.setFlashGuard. | ||
| API Changes | 🔴 #102: Refused an impossible pairing of a decoder with a renderer at open or attach. The check uses the new VideoDecoder.output and VideoRenderer.accepts. The error is the new PlaybackError.RendererIncompatible. | |
| Performance | #246: Reused the plane and RGBA arrays of each software-decoded frame on the JVM as on Android, so a 1080p frame allocates 2,016 bytes where it allocated 11,406,864. | |
| #468: Skipped decoding the frames that nothing is predicted from during a precise seek's run up: 78 frames instead of 285 in a 12 s H.264 test. | ||
| 🔴 30d282f: Decoded H.264, HEVC, VP9, MPEG-2, VC-1 and WMV3 on the GPU through Direct3D 11 on Windows, with a software fallback. | ||
| Fixes | #233: Played a mirrored video mirrored on every renderer, through the new VideoFrame.mirrored and PlayerStreamInfo.mirrored, where a left-right mirror used to play upside down. | |
#377: Passed a setRenderQuality change made during playback on to the attached renderer, where it used to reach only a renderer attached afterwards. | ||
#378: Rolled HDR tone mapping off from the content's own peak. The peak is read into the new HdrStaticMetadata on PlayerStreamInfo.hdr and VideoFrame.hdr. On Android it is passed to MediaCodec. | ||
🔴 #497: Applied the picture crop that a container states, such as Matroska PixelCrop. The player reports the size that is left. | ||
#530: Added VideoRenderer.clearPicture(), which the engine calls when no picture plays, so every renderer takes the last frame off the screen. | ||
| Subtitles (27) | New Features | #236: Offered the closed captions (CEA-608 and CEA-708) inside a video stream as a subtitle track, CC1, shown in real time like any other track. |
| #439: Showed the TTML (IMSC) subtitles of an HLS stream by serving each segment to FFmpeg as WebVTT. | ||
#443: Read synced lyrics from an .lrc file or a song's tags into a timed subtitle track. Unsynced tag lyrics go into PlayerSnapshot.lyrics. | ||
#491: Added KitePlayer.seekToSubtitleLine and stepSubtitleDelay, to jump to the previous, current or next subtitle line, or to start it now with the delay. | ||
| #492: Loaded external subtitle files in the formats FFmpeg reads, such as SAMI, MicroDVD, SubViewer, SBV, VobSub or PGS. TTML and DFXP files are read with their regions. | ||
#493: Added SubtitleConfig.hearingImpairedNotes, with Hide or HideStrict, to remove sound descriptions and speaker names from text subtitles. | ||
#494: Added SubtitleConfig.secondaryLanguages to choose the second subtitle by language at open. secondaryPlacement stacks it by the first. | ||
#506: Made a self-chosen subtitle follow an audio change. The new SubtitleConfig.withMatchingAudio keeps only forced subtitles, or none, under a preferred language. | ||
| #510: Showed DVB teletext subtitles, with each subtitle page listed as its own track. A Kotlin page reader decodes the pages into cues. | ||
🔴 #513: Added SubtitleConfig.forcedPicturesOnly and forcedPicturesWhenOff, to draw only the forced captions of a PGS or VobSub track. | ||
| #514: Read an external subtitle's language, forced flag and hearing-impaired flag from its file name. Language preferences can then pick it at open. | ||
#515: Added SubtitleSource.encoding, SubtitleConfig.fallbackEncoding and KitePlayer.reloadExternalSubtitle, to pick the encoding of an external subtitle file, then reload it. | ||
| Performance | #408: Resolved each TTML style once. A loop of style names is cut at its first repeat, so a self-naming style reads once, not 111 million times. | |
| Fixes | #381: Dropped a subtitle track whose decoder cannot open, with PlaybackWarning.TrackDeselected, instead of failing the whole open, which stopped some files from playing on the web. | |
#412: Moved the read of an added subtitle file off the player's command loop, so a slow reader factory no longer holds up Stop and closeAndAwait. | ||
| #480: Sent each subtitle decoder the drain signal at the end of a stream, so the last closed caption of a file is shown. | ||
| #482: Closed a held secondary subtitle packet when a session stops, closes or is replaced. | ||
#498: Showed WebVTT colours from the standard colour classes and from STYLE blocks with ::cue rules. Added CueStyle.relativeSize. | ||
🔴 #499: Converted ASS colours from the script's YCbCr Matrix to the video's, so typeset signs match the picture, with SubtitleConfig.assColorMatching to turn it off. | ||
| #511: Stopped WebVTT ruby and language spans from showing their tags: ruby now shows as the base text followed by its reading in parentheses. | ||
| #512: Kept the italics, bold, underline and colours of MP4 or MOV subtitles. | ||
| #516: Stopped the subtitle encoding guess from reading Western European files, such as Italian, Danish and Dutch, as Hebrew, Greek or Cyrillic. | ||
| #518: Made the subtitle encoding guess weigh each reading by the letters of its languages, so Turkish, Hungarian, Polish, Romanian and Thai files read right. | ||
| #520: Made the subtitle encoding guess weigh Chinese, Japanese and Korean encodings for every file. The guess uses the track language too, so short files read right. | ||
| #536: Drew a secondary subtitle that is chosen while the primary subtitle is off. Its last line now comes off when it is cleared. | ||
#542: Showed each caption of a caption track of its own, such as MOV c608, from the packet that sets it, not as it leaves. | ||
| 4ad3f29: Made the system's Latin sans face the default font for ASS subtitles on Android and Linux, instead of whichever Noto script file loaded first. | ||
| Visualiser (3) | New Features | 🔴 #389: Added shared visualiser pieces: the Evolution pacer for form changes, Impulses for recent hits, a shader waveform history (scopeHistory) and a memory field that a flow carries. |
34c71a8: Rebuilt the Alchemy drawing as a memory field of ink that the music writes, carried by a flow. Its form changes as the music gets busier. | ||
| API Changes | 🔴 #390: Removed the Fireworks, Musical Spectrum and Muser drawings from the audioviz catalogue, with their licence texts, leaving 21 drawings. | |
| Network and streaming (29) | New Features | 🔴 #209: Played HLS through the default network stack, with https segments, AES-128 and seeking. It chooses the best variant within the new DemuxPolicy.maxBitrate. The new DemuxPolicy.maxVideoHeight limits the video height of that choice. |
| #295: Played DASH with video and audio in separate adaptation sets. Its segment streams now seek, because the manifest goes to the HLS path. | ||
🔴 #376: Listed the variants of an HLS stream in Tracks.variants. Added KitePlayer.selectVariant. The stream steps down or up by itself as the network allows. | ||
#400: Played DASH and HLS addresses that have no extension. A DASH manifest is recognised by type, .mpd path or root element. An HLS playlist is recognised by its #EXTM3U start. | ||
#401: Played WebM DASH manifests through the HLS path. This lets them seek, play with separate audio or play live. A single WebM file with a SegmentBase plays as segments. | ||
#402: Converted the subtitle sets of a DASH manifest to WebVTT, from TTML or MP4 (stpp, wvtt). The result keeps text, line breaks, italic, bold and underline. | ||
| #403: Played a DASH manifest of several Periods as one presentation, for fMP4, WebM and MPEG-TS media, instead of refusing it. | ||
#404: Counted live DASH on the manifest's UTCTiming clock. It follows Location and endNumber. Tracks take their names from Label or Role. Protected sets are left out. | ||
#423: Showed an internet radio station's current song as the title. The new KitePlayer.setItemDetails sets the playing item's title, artist and album. | ||
🔴 #433: Read the thumbnails of an HLS image playlist, a DASH thumbnail set and a WebVTT thumbnail file, so thumbnailAt answers without decoding video. | ||
#440: Played a DASH segment from the next BaseURL location when the first server fails. An HLS variant does the same with its redundant backup. | ||
🔴 #453: Reopened an item for a fresh address when a server answers 401 or 403 during playback. The player reports the reopen with PlaybackWarning.AddressRenewed. | ||
🔴 #461: Added NetworkConfig.recovery, which waits for the network to return. It then reopens a failed network item at its position or at the live edge. | ||
| #464: Changed the quality of an HLS or DASH stream in place when the variants share a codec, so the picture no longer freezes on a reopen. | ||
🔴 #547: Added SegmentStore and fileSegmentStore, an opt-in cache that keeps the segments of an HLS or DASH stream between players. Set it with NetworkConfig.segmentStore. | ||
| #564: Changed quality in place on HLS and DASH streams whose fMP4 pictures are AV1 or VP9, with neither a picture hold nor a reopen. | ||
| #565: Changed quality in place on AES-128 encrypted fMP4 HLS streams, by decrypting each segment in Kotlin before it is written again. | ||
| #566: Changed quality in place, instead of reopening the stream, on DASH with several Periods or on WebM segments that share a timestamp scale. | ||
| Performance | #407: Let go of DASH initialization segments once no kept segment uses them, with a 32 MB limit, so a long live session no longer grows without bound. | |
| #455: Stopped an HLS or DASH stream from downloading every audio language. Only the rendition being heard downloads. A switch fetches the new one. | ||
| Fixes | #405: Kept every DASH segment address that a playlist still lists, so long subtitle tracks and multi-Period presentations no longer lose their first segments. | |
#406: Bound a live DASH track to its set and representation by id on a refresh. The player refuses the track with a SegmentSkipped warning when its set is gone. | ||
🔴 #447: Chose the HDR variant of an HLS stream on a screen that shows HDR. Through DemuxPolicy.fit, the variant is capped at the height the view draws. | ||
#448: Sent the credentials of an http://user:pass@host address as a Basic Authorization header on Android and the JVM. | ||
| #449: Kept the cookies a streaming server sets. The player sends them back for the rest of the item, so cookie-protected streams play. | ||
| #450: Played radio links that name their stream in a PLS file or a plain M3U list, which FFmpeg misread as lyrics. | ||
🔴 #452: Made HLS playlist variables resolve through FFmpeg. An open that FFmpeg refuses as invalid data now fails with NotMedia instead of SourceUnavailable. | ||
#525: Made selectVariant fail with IllegalStateException, not wait for ever, when a stop or close lands while the new quality refills. | ||
🔴 #543: Listed the qualities of an HLS or DASH stream in Tracks.variants on a real open, so selectVariant and the automatic quality steps work. | ||
| Live streams (5) | New Features | 🔴 #395: Played live RTSP, RTMP, UDP and RTP addresses or SDP files, keeping the delay bounded by playing 1.1 times faster. Other schemes fail with PlaybackError.SchemeUnsupported. |
#444: Published the time of day of live stream positions as Progress.timeOfDayMillis and KitePlayer.timeOfDayAt. It comes from EXT-X-PROGRAM-DATE-TIME or from DASH availabilityStartTime. | ||
| Fixes | #441: Kept a paused RTSP camera's session alive. Play now continues at the live edge, reopening the stream if the server dropped it. | |
| #508: Reconnected to a Shoutcast or Icecast station when its server closes the stream, instead of ending the playback. | ||
🔴 #509: Played sound or subtitles that a transport stream starts carrying after the open. The new events are PlayerEvent.TracksAdded and PlayerEvent.TrackChosenByPlayer. | ||
| Views and Compose UI (3) | New Features | 63764fc: Added default player controls, off by default: KitePlayerControls over KitePlayerVideo in Compose, showsControls on KitePlayerView, KitePlayerUIView or KitePlayerAwtView. |
| API Changes | #384: Made a player view work after view.player = player alone, through PlayerViewDefaults.rendererFactory. The video path rebuilds once when a renderer arrives after the open. | |
🔴 #387: Reduced Compose to one video composable, KitePlayerVideo, by moving the direct paths out of the kiteplayer-compose-ui API. Removed the deprecated kiteplayer-phone module. | ||
| Android (17) | New Features | 0a7f386: Added KitePlayerMediaSession.setMediaRequestHandler, which hands Android's search, item and prepare requests to the application. setMediaButtonHandler hands it the media keys. |
0ad72f9: Added KitePlayerResumption and KitePlayerResumptionService, so Android's media resume card plays the last saved item again. | ||
| API Changes | #427: Added onDismissed to MediaNotificationOptions, which says whether the user swiped the notification away or removed the app from the recent apps screen. | |
| Fixes | #23: Made PlaybackWarning.HdrToneMapped fire on the Android GPU path, the Android software surface and the Compose canvas, once for each HDR item opened. | |
| #415: Gave back the audio focus that a request returns after the media session closed, so other apps are no longer left ducked or paused. | ||
| #426: Turned the notification badge off on the playback channel on Android 8.0 and 8.1, so the app icon shows no dot while something plays. | ||
| #437: Played or paused at once on a play-pause media key, with no 300 ms wait. A second press no longer skips to the next song. | ||
#438: Added KitePlayer.redrawPicture, which KitePlayerView calls when its surface returns, so an ended video shows its last frame instead of black. | ||
| #445: Fell back to 16-bit PCM with dither on Android when the device refuses float output. The player reports the fallback once as an audio device change. | ||
| #451: Waited for delayed audio focus on Android, so play during a phone call starts when the call ends. Short sounds now ask for transient focus. | ||
| #463: Redrew a paused or ended picture on Android when picture adjustments, scale, framing or subtitle settings change, without moving the position or changing the status. | ||
#477: Stopped KitePlayerView from starting a new player state collector when it leaves the window. | ||
| #478: Kept right-to-left subtitles on the side their cue names on Android. Short positioned lines are no longer cropped away. | ||
| #541: Redrew the held picture on Android's software path when its subtitle, adjustments, scale or framing change, instead of waiting for the next frame. | ||
#560: Passed RenderQuality on to the GL blit of the Compose video on Android, which switched on its dither, debanding, kernel, linear light and upscaler. | ||
#569: Stopped keepPictureInPictureParamsCurrent from crashing when an update runs after the handle is closed or the Activity ends. | ||
| ca75bbf: Asked for audio focus a second time on Android 15 once the media service is in the foreground, so a lock screen play after a long pause stays playing. | ||
| Apple (13) | New Features | #421: Ran RenderQuality.animationUpscaler on the Metal renderer, so it works on Mac, iPhone and iPad as it does on Android. |
3c8b628: Added KitePlayerMediaSession for native Mac apps, attached with attachMediaSession. Now Playing and the media keys then control the player. | ||
🔴 7517c2b: Paused a native Mac player when its headphones give way to the speakers, through InterruptionPolicy.pauseWhenBecomingNoisy. | ||
| Fixes | #379: Turned a quarter-turned picture clockwise on the Metal renderer, so a portrait phone clip no longer plays upside down on macOS and iOS. | |
| #380: Showed a turned or mirrored video the right way round in Apple picture in picture, by drawing it through the GPU pass into a window of the turned shape. | ||
| #475: Stopped the Metal renderer from reusing a software plane texture before earlier GPU reads of it finish, by keeping a pool of three plane sets. | ||
#476: Made MetalPictureReader AutoCloseable so it releases its CoreVideo texture cache. Each iOS Compose video renderer now has its own converter. | ||
#540: Made MetalVideoRenderer.showsHdr true on an HDR display under HdrPolicy.Auto, so HLS and DASH streams pick their HDR version. | ||
| 18bb9a3: Dated Apple audio at the ear by adding the output latency the system reports, 28 ms on a MacBook Air's speakers and more on Bluetooth. | ||
| 34eb4b5: Opened Apple audio with seven and eight channels, so 7.1 is no longer folded to 5.1. An iPhone is now told when an item is surround. | ||
| 871bb91: Reopened the audio output when the route changes its channel count, so surround is no longer folded to stereo after a move to a receiver. | ||
| c566f53: Said that no output is left, instead of that playback follows device 0, when the last macOS audio device goes away. | ||
| d8b21b7: Named the picture's colour space to macOS in the Metal renderer, so BT.709 video no longer looks oversaturated on a P3 display. | ||
| Desktop JVM (1) | Fixes | #308: Failed a desktop player with PlaybackError.AudioDeviceUnavailable within about a second when its bound output mixer disappears, where it used to stay Playing in silence. |
| Web (10) | New Features | #58: Shipped the web worker binary as kiteplayer-wasm-js-<version>-web.zip. The new KiteWebModules.codecModuleUrl picks a threaded codec module only on a cross-origin isolated page. |
#100: Added KitePlayerWorker, an opt-in web player that runs in a worker. It plays http, https and blob addresses. The longest page frame gap stays under 30 ms. | ||
| 29b59f2: Drew text subtitles on the web canvas with the browser's own text engine, so SubRip, WebVTT, TTML and ASS without libass show on a page or in the worker. | ||
| 92427f6: Played HLS streams in the player in a Web Worker, including live playlists, encrypted fMP4 and quality changes in place. | ||
| Performance | #519: Cut the web worker download from 1.46 MiB to 0.50 MiB gzipped, by packing the optimised build. The unused network transport is left out. | |
| Fixes | #479: Made a cancelled wait on a browser audio promise end at once. An audio context that finished setup after its caller left is now closed. | |
#517: Passed PlayerSnapshot.failedQueueItems, PlayerSnapshot.durationIsEstimate, TrackInfo.dolbyVision and PlaybackWarning.QueueItemSkipped through the web worker protocol, so a page sees them. | ||
#535: Made the web canvas renderer pass on its outputSize, so subtitles are laid out for the canvas instead of stretched over the bars. | ||
| #537: Escaped two regular expression brackets that Kotlin/JS refuses, so external subtitle files open on the JavaScript target. WebVTT styles load there too. | ||
9e9c1f1: Ended a browser worker that does not answer a close within three seconds, so closeAndAwait returns when a live stream has stopped growing. | ||
| Build and packaging (6) | API Changes | 4485c83: Moved to KiteFFmpeg 0.5.1, which adds RTMP, UDP and RTP. It fixes WebM seeking plus DASH subtitle cues. In the browser it now reads HLS or WebVTT. |
| 🔴 5634c33: Moved to Compose Multiplatform 1.13.0-alpha02. Every module now compiles against Android SDK 37.2. | ||
6efa805: Moved the DASH reader and the HLS TTML reader into the new module kiteplayer-adaptive, which has no HTTP client, so a Web Worker can play DASH. | ||
| 7e48a00: Moved the build from Kotlin 2.4.20 to Kotlin 2.4.21, with no change to the ABI dumps. | ||
| Fixes | #157: Built the macOS libass chain for macOS 12 (release ass-chain-r2). The macOS adapter now links at 12.0, so the shipped files no longer claim macOS 11 over macOS 26 code. | |
#420: Merged the two MediaItemBuilder classes into one in kiteplayer-core, so Android apps that package both modules merge their dex again. | ||
| Tests and CI (27) | Tests | #13: Added an Android device test of a 35 second pause and resume, where the largest position step on an ASUS ROG Phone 9 was 24 ms. |
| #208: Tested that an MP3 with a Latin-1 ID3v1 title opens on the JVM and macOS, with the bad byte read as U+FFFD. | ||
| #396: Made the emulator job skip the HDR view test on a display without HDR10. The reopen test now waits out the emulator's graphics stalls. | ||
| #397: Kept the event sources race test running past its 40,000 frames until the race has happened, with an upper bound, so busy machines no longer fail it. | ||
#418: Made scripts/testmedia.sh generate every clip on FFmpeg 6.1 and 8. The script skips the fragmented TTML clip by name in MANIFEST.txt. | ||
#419: Made media-backed JVM tests report as skipped when a clip or device is missing. They fail when KITEPLAYER_REQUIRE_TESTMEDIA=1 says the clips exist. | ||
#521: Made SubtitleAcquisitionTest.aReaderThatNeverOpensHoldsNoStopOrClose wait for the answer to the close, which ended a failure in about one run in eight. | ||
#522: Made the PlaybackCoreTest close-during-open test wait for the close. Its virtual finish time is bounded, so it no longer fails on a busy machine. | ||
| #523: Raised the size budget of the KiteFFmpeg web codec module from 1,530,921 to 1,541,407 bytes gzipped, for the readers that KiteFFmpeg 0.5.0 adds. | ||
#528: Labelled the frames of the test_sink_callback long run inside full scale, so the peak limiter stops turning them down on macOS. | ||
| #531: Spelled the long dash in three strings that hold it as data as an escape, so the tier 1 gate's dash scan passes. | ||
| #532: Fixed the Compose video host tests so they compile. Their 47 tests now run in the Android host step of CI. | ||
#533: Made the recording canvas in the web resize test take the drawn size from the last two numbers of each drawImage call. | ||
#534: Corrected the coverage counts in WorkerProtocolTest to 35 controls and 41 warning kinds, keeping every round-trip assertion. | ||
| #538: Spelled the long dash in the teletext character tables and their test as an escape, so the gate's dash scan passes again. | ||
| #539: Moved the seek of two DASH Period tests from a segment boundary to 31 s, so they pass on clips made by FFmpeg 6.1. | ||
| #544: Made the chained Ogg fixture and four shared tests portable to the CI runners, on Native or JavaScript. | ||
| #545: Made two real-media JVM tests compare the right pixel layouts. They now wait for an actual picture, so they no longer accept a missing frame. | ||
| #549: Ran the macOS timing tests in CI with no project parallelism and no configuration cache, so other tasks no longer skew the tempo gate. | ||
| #550: Made the RTSP pause test allow the player's startup PAUSE and PLAY. It counts one extra pair for the application's pause. | ||
| #551: Sent the multicast test stream through a Java multicast socket instead of the host FFmpeg sender, so the test measures the player. | ||
| #552: Made the late-stream recording test declare both audio streams in its prefix. It probes in a bounded window, so its unknown-format premise holds. | ||
| #553: Added the video colour export to the fake libass module, with a small font for the colour test. That fixed four failing web cases. | ||
| #554: Raised the web worker size budget to 639,631 bytes gzipped (0.61 MiB). The measured files are kept as a CI artifact. | ||
| #555: Made the RTSP test relay send each packet once to a resumed player. The relay stops a paused player on PAUSE. | ||
| #558: Made the RTSP test relay release the UDP sockets of a paused or disconnected player. | ||
#571: Made RtspPauseTest wait for one steady second of Playing. Its failure message lists every status change. | ||
| Docs (4) | Docs | #60: Deployed the API reference site to GitHub Pages at yuroyami.github.io/KitePlayer. The README links to it. |
| #382: Said at the top of the README that KitePlayer is a library with its own engine that uses FFmpeg, not a wrapper around ExoPlayer, AVPlayer, mpv or VLC. | ||
| #399: Laid the README out as a page, with a banner for each theme, grids, platform chips, accordions and a module diagram, keeping its words. | ||
#416: Corrected the KitePlayer KDoc. A player runs eight serial lanes over the shared thread pools. It owns no thread of its own. |
Dependencies
| Version | |
|---|---|
| Kotlin | 2.4.21 |
| Compose Multiplatform | 1.13.0-alpha02 (the Compose modules and kiteplayer-audioviz) |
| Android Gradle Plugin | 9.4.1 (built with) |
| Gradle | 9.8.1 (built with) |
| JDK | 21 (built with), JVM bytecode 11 |
| Android SDK | compileSdk 37.2, minSdk 26 |
| KiteFFmpeg | 0.5.1, with FFmpeg 9.0.2 (kiteplayer-ffmpeg) |
| Ktor | 3.6.0 (kiteplayer-network) |
| libass | 0.17.5, with HarfBuzz 14.5.0, FreeType 2.14.3 and FriBidi 1.0.17 (kiteplayer-libass) |
| Kite3D | 0.2.0 (kiteplayer-audioviz) |
| kotlinx.coroutines | 1.11.0 |
Targets: kiteplayer builds for Android, JVM, iOS, macOS, Linux, Windows, JS, Wasm. kiteplayer-core adds tvOS, watchOS, Android Native. kiteplayer-compose and kiteplayer-audioviz build for Android, JVM, iOS. The Linux and Windows native builds have no audio output, video output or HTTPS, by design.
Full changelog: v0.2.0...v0.3.0