Repository navigation
Releases: rmotafreitas/react-native-anything-player
Release list
v1.5.1
1.5.1 (2026-11-04)
A fix for the jump a live quality switch reports, and corrected docs and notes after 1.5.0. Nothing is removed or renamed; upgrading needs no change.
Fixed
- A switch's
jumpisnullwhen a connection jumped first. A live quality switch that could not be lined up because a connection was replaced or jumped reported, as itsdiscontinuity'sjump, a gap measured before that jump: a precise-looking wrong number. It is nownull(not known). The same applies to a switch already given up for another reason when a connection jumps before it takes over. On iOS, Android and the web. - The docs describe 1.5.0's rule everywhere. The
discontinuityTSDoc, Events and Internet radio say that any live switch while the app holds a pick can go ahead with adiscontinuity. Playback's WebKit measurement says it was taken under 1.4.0's refusal rule. 1.5.0's notes now say why it is a minor, that a pick goes ahead once lining it up is given up (within 30 s), and thatQUALITY_SWITCH_FAILEDhas other causes too (Errors).
v1.5.0
1.5.0 (2026-11-04)
A live quality pick that cannot be lined up goes ahead instead of being refused, and an app that keeps its own place in the station's timeline hears of the jump before the title. Nothing is removed or renamed. One documented live-stream behaviour changes: see Upgrading. By what a version number promises that would call for a major; it is a minor because 1.4.0 had been out for a few hours, the change only lets a pick through that was refused, and an app that follows 1.4.0's advice needs no change.
Added
- A live quality pick that cannot be lined up goes ahead. A live switch the app picked with
selectQualityused to be refused (QUALITY_SWITCH_FAILED) when it could not be lined up with what is heard, and tried again a minute later. A pick that can never be lined up, such as a new connection behind what is heard on iOS (AVPlayer cannot run a live stream faster), then never landed. While the app holds a pick, a live switch is now lined up when it can be, and otherwise goes ahead with adiscontinuity(reason: 'switch') once lining it up is given up (within 30 s), as a switch the player needs already did. Automatic quality (a step up) still waits for one that can be lined up. See Live renditions (a4f6ac5).
Fixed
- A switch that went ahead without being lined up reports its
discontinuitybefore the title the new connection reached. The title came first, so an app that places its song again ondiscontinuitytook it for a song change heard at the song's start. Now the title told after thediscontinuityis known to be partway into its song. On iOS, Android and the web (2c7e5f9).
Upgrading
- A pick is no longer refused when it cannot be lined up. It goes ahead with a
discontinuityinstead. An app that re-opened the source onQUALITY_SWITCH_FAILEDto force its pick through can stop doing so, and should place its position again ondiscontinuity, as 1.4.0 already advised.QUALITY_SWITCH_FAILEDno longer comes from a pick that could not be lined up; it still comes from a switch whose new connection fails or is not ready in time (it sits out a minute and is tried again), and from an automatic step up that was refused. See Errors.
v1.4.0
1.4.0 (2026-11-03)
Live quality switches that line up with what is heard, and voice and car playback on a fresh install. Nothing is removed or renamed. A few live-stream behaviours change, and an app that handled them itself may need to stop doing so: see Upgrading.
Added
- Live quality switches line up with what is heard. A switch between live
renditions(Icecast mounts at different bitrates, HLS or DASH renditions with program dates, live MPEG-TS sound channels) no longer repeats or skips audio. The new connection is played muted, a little faster or slower, until it reaches the same point in the program, then takes over: within about 0.3 s in Chromium, Firefox, WebKit and on Android, where switches used to land up to 18 s off. Sources that can never be lined up switch as before: a browser's plain<audio>(a stream without CORS), a live TS channel with a picture in a browser, and every live TS channel on iOS. iOS has the code for the rest but has not been recorded on a device yet. See Live renditions. liveOffsetis the whole delay behind live: the server's burst, the buffer and the decoder. On a progressive stream (ICY, plain HTTP, live TS) it is measured from what has arrived, so it holds steady while audio plays, grows while paused or stalled, and carries on across a lined-up switch. HLS and DASH report their own clock.liveOffsetSettled(ongetProgress()andprogress) turnstrueonce the connect burst has arrived andliveOffsethas held for 3 s, so a delay shown to the user, or lyrics placed against it, does not move. It turnsfalseagain after adiscontinuityor a new load.programDate: the epoch milliseconds that the stream's own clock gives what plays now (HLSEXT-X-PROGRAM-DATE-TIME, DASH),nullwithout one. On iOS, DASH converted for AVPlayer now carries program dates too.discontinuitysays why and by how much:reason('reconnect'or'switch') andjump(seconds skipped, or heard again when negative;nullwhen unknown).- A catalog the app bundles, for voice assistants and cars on a fresh install that was never opened. With Expo:
["react-native-anything-player", { "catalog": "./catalog.json" }], checked at prebuild withsetCatalog's rules. Bare apps write the same file into their assets. See Voice assistants (#80). - A voice request or car pick for what is already loaded plays on: no gap, and the app's
updateNowPlayingfields, headers and DRM stay. A catalog item can set thequalityit starts at. - Cars for one platform: the plugin's
car: { ios: false }sets up Android Auto alone, and{ android: false }sets up CarPlay alone. CarPlay's scene is added in any plugin order, including with expo-build-properties'ios.enableSceneSupport.
Fixed
- A player released and created again with the same id is a new one. A remount in the same tick used to attach to the player being released, which then ended it ("The player was released" on the next command). A released player's late commands, events and status never reach the new one either (#90).
- After a JavaScript reload, the old runtime's late release can no longer end the new runtime's player.
- On Android, an assistant request that opens a fresh install before the app has a catalog now waits for
setCatalogand plays then, instead of opening the app idle (#79). - On Safari for iPhone and iPad, a live stream plays through MSE again. It had fallen back to the plain element, which meant no ICY titles and no visualizer.
- The visualizer works for streams in Safari and every iOS browser. It used to draw a flat line, because WebKit reads silence from a stream played through MSE.
Upgrading
- A live switch that cannot be lined up is refused when it is optional. A quality pick or an automatic step up that cannot be lined up (for example, the new mount has not settled) keeps the current quality and reports a non-fatal
QUALITY_SWITCH_FAILED. Do not re-open the source when you get it: that brings back the gap this release removes. A switch the player needs, because the current connection stalled, still goes ahead and reports adiscontinuitywithreason: 'switch'. - A lined-up switch reports no
discontinuity, and positions carry on. An app that keeps its own place in the station's timeline (a song's progress, lyrics) should place it again ondiscontinuity, not on every change ofstatus.quality. liveOffsetreads larger than before on progressive streams (it now includes the whole delay) and can benulluntil there is something to measure. Wait forliveOffsetSettledbefore showing it.- TypeScript: progress readings gain
liveOffsetSettledandprogramDate, anddiscontinuitygainsreasonandjump. Code that only reads them needs no change. A test fake that builds these objects needs the new fields.
v1.3.1
1.3.1 (2026-10-30)
Quality switching for audio players. This release adds features, so by Semantic Versioning it should have been 1.4.0: the release tool missed the commits that added them. Nothing is removed or renamed, so upgrading is safe.
Added
- Audio renditions. An audio
Playertakesrenditions, separate files of one sound (e.g. MP3 at 320 and 192 kbps and AAC+ at 64 kbps), and switches between them with no gap, as video does. On iOS and Android this needs no video module; the second player is made only at the first switch. On the web, AirPlay and the visualizer follow the file that is playing. See Qualities. - Automatic quality for audio players. Every player now reports its bandwidth. An audio player starts at the highest quality (a file starts at what the last reading fits), and two stalls within 30 s step it down. A live stream steps up again after 120 s without a stall; a step up that stalls again waits twice as long next time.
- A quality pick that holds.
selectQuality(id)and the newload(source, { quality })are kept for the next loads, matched by id;'auto'clears the pick.Rendition.idgives stable ids (defaultr<index>). JavaScript refuses an unknown or duplicate id, and'auto', before anything changes. - A failed switch is reported: a non-fatal
errorwith the newQUALITY_SWITCH_FAILEDcode and the new file's own failure incause. The current file plays on. See Errors.
Fixed
selectQuality('auto')lets go of a kept pick even when the current source has nothing to pick from.- On Safari with AirPlay (as on Cast), a quality switch re-opens the item in place instead of moving to an element the receiver isn't playing.
- The background, listen-only mode and the audio fallback never lower an audio player's quality.
v1.3.0
1.3.0 (2026-10-30)
Video, on every platform, as an opt-in part of the same player. Audio apps get the fixes below; the only change to plan for is the JavaScript size (see Upgrading).
Added
- Video.
new Player({ video: true })plays a picture with the same engine, recovery, events and lock-screen integration as audio.<VideoView player={player} />shows it in React Native, React Native Web, Expo web and React DOM, andplayer.attachTo(element)on any web page; a player outlives its views, and moving it between views has no gap. Files, HLS and DASH (Shaka on the web, rewritten as HLS for AVPlayer on iOS); renditions (one file per quality) switched behind the picture without a gap; quality caps for the view's size, metered networks and Data Saver; fullscreen, Picture in Picture andwhenBackgrounded(pause,audio,pip);nativeControls; posters, thumbnails,seekTo(t, { exact }), loop and playback stats. The qualities to pick from are on the status itself (status.qualities,status.quality,status.selectedQuality), forselectQuality(). Opt-in on iOS and Android with the Expo plugin'svideooption (npx rnap add video,rnapVideo=true/RNAP_VIDEO=1without Expo); always available on the web. See Video. - Matroska, WebM and MPEG-TS everywhere, files and live IPTV channels, with their audio languages and subtitles. Nothing is transcoded: ExoPlayer reads them, and RNAP remuxes them for AVPlayer and for browsers (on the web the server must allow CORS).
- Subtitles and tracks. SubRip, WebVTT, TTML and basic ASS files, encoding detection, a source's own audio languages and subtitles, one overlay that follows the OS caption style,
useCues(),subtitleDelay, and an automatic choice from the OS languages and accessibility settings. See Subtitles. - DRM. FairPlay, Widevine, PlayReady and Clear Key: license servers with headers, Clear Key keys in the source, or the app's own
getLicenseexchange. FairPlay is not yet tried on hardware. - Video over Google Cast and AirPlay, with the app's subtitle files on Cast receivers.
- Android software decoders (
npx rnap add video-decoders,rnapVideoDecoders=true): FFmpeg for Dolby, DTS and MPEG audio, libvpx and dav1d for VP9 and AV1, used only for what the device cannot decode. They are built in CI from pinned sources and downloaded from this repository's releases, checked against pinned checksums. - A feature matrix of what audio features do on a video player, per OS and browser engine (Features).
Fixed
- A playback rate holds across loads. On the web, a new source (or hls.js and Shaka attaching later) reset it to 1x while
status.ratekept the old one; on iOS, a load could start at the default rate; on Android, the rate did not follow the audio to a Cast receiver and back. A Cast receiver now plays the rate set, between 0.5x and 2x, on every platform (#49). - The duration and seek range follow what the platform learns. A fragmented MP4 that reports 18 s at first, then 600 s, no longer keeps seeks clamped to 18 s, and a start position past an early duration waits for the longer one; a Cast receiver's later duration reaches the player too, and an unknown one taken for live becomes a file once known (#50).
- A stall in the last frame of a file ends it. WebKitGTK could stop a few milliseconds before the end without
ended; the engine re-opened the item at its end every few seconds, forever, so a queue or a sleep-at-end never moved on. All three engines now end (or loop) the item (#59). - A transient media-pipeline failure is retried. On the web, a
MediaError4 from an element that hls.js, the remuxer or MSE feeds, after its metadata loaded, was reported as a fatalUNSUPPORTED_FORMAT; on iOS and Android, a decode or parse failure after the item was ready was fatal too. It is now a recoverableDECODER_ERROR, and the engine re-opens the item (#58). - A player that plays without saying so (WebKitGTK at
readyState2) is treated as playing once its playhead runs, instead of being re-opened as a dead stall. - Android reports no duration for a live stream (the live window's length changed on every refresh), as iOS and the web do.
Changed
- A start position costs no wasted request. hls.js starts loading at the segment that holds it, and iOS seeks the item before it reaches the player, instead of fetching the first segment first (#57).
Upgrading
- More JavaScript. The React Native entry is about 16 KB gzipped (6 KB before), and a web page loads about 56–57 KB (31 KB before), because the video API, the engine's video rules and the subtitle parsers' entry points are part of the main entries, even for an app that plays only audio. Native code for video is only built when you turn it on. See Comparison.
- Nothing else changes for audio apps: no API was removed or renamed.
Video preview 0 (feat/video @ dc61d03)
Pre-release build of the feat/video branch (draft PR #46) at dc61d03, packed for apps that need video before it is merged and released. Not a release-please release: releases/latest stays on v1.2.1.
- Package version inside:
1.3.0-video.0 - Built with
yarn prepare && npm pack(same steps as the release workflow), Node 24.22, Yarn 4.11 - SHA-256 (
react-native-anything-player.tgz):ed383a46f6020d626ae6da77aa17430189ccf2034082819b596df6178e3b8b21
bun add https://github.com/rmotafreitas/react-native-anything-player/releases/download/video-preview.0/react-native-anything-player.tgzv1.2.1
1.2.1 (2026-10-24)
Reliability fixes across the three engines and the iOS, Android and web hosts. Late callbacks, timeouts, stalls and reconnect: false now behave the same everywhere (22 new conformance scenarios, 127 in all). Three behaviours are stricter (see Upgrading).
Fixed
- Engine (iOS, Android, web)
- A late end or failure from an item that was stopped, failed or released no longer turns the player into
endedorerror. An item that ends during a stop's fade-out staysstopped, and a repeated end is reported once. load()without autoplay (or paused while loading, or refused audio focus) times out after 12 s instead of waiting forever.- A silent stall (the playhead freezes while the native player says it is playing) ends as soon as audio moves again, instead of being re-opened from the start. A re-opened file resumes where the playhead got to.
recovery.reconnect: falseis honoured everywhere. Stalls, network changes, returning to the foreground, media-services resets and the drivers' own connection splicing no longer re-open on their own.- A source loaded with
live: falseends at its end even when its duration is unknown, instead of reconnecting. - Returning to the foreground at the same moment as a network restore no longer aborts the fresh connection.
- A sleep timer or fade longer than 24 h is rejected instead of crashing (iOS) or firing at once (Android).
- ICY: whitespace between fields (
StreamTitle='…'; StreamUrl='…';) no longer leaks into the title, and the artwork URL is read.
- A late end or failure from an item that was stopped, failed or released no longer turns the player into
- iOS
- HLS streams whose URL does not end in
.m3u8play: the stream proxy hands the playlist to AVFoundation instead of breaking its relative segment URLs. - A file served without
Content-Lengthand loaded withlive: falseends instead of starting over. - The Sound API no longer switches a Player's audio session to
ambient, which would make the Player obey the silent switch and lose background audio. A Player restores its category when something changed it. - The visualizer no longer works on the audio render thread, and it stops with its item.
- HLS streams whose URL does not end in
- Android: the Cast title reader keeps asking for ICY metadata, even when the source's headers say
Icy-MetaData: 0. - Web
- A play refused by the browser's autoplay policy (from a media key, for instance) no longer makes the next, unrelated command reject with
AUTOPLAY_BLOCKED. - Releasing a player closes its visualizer's
AudioContext(browsers limit how many a page may have). - Google Cast tries to load its SDK again after a failure (offline at page load, for instance).
- A stream whose buffer stays full is ended and recovered, instead of silently skipping audio.
- A play refused by the browser's autoplay policy (from a media key, for instance) no longer makes the next, unrelated command reject with
- Packaging: tarballs packed with Yarn include the Codegen specs (
src/native), and theexportsmap lists./app.plugin.js. - Expo plugin: a project name containing a backslash or a quote no longer breaks the App Intents entry in the Xcode project.
Upgrading
reconnect: falseis now strict. A stall or a lost connection ends in a fatalNETWORK_ERRORinstead of recovering; callplay()to retry. Withreconnectleft on (the default) nothing changes.load()withautoplay: falsecan reject withTIMEOUTwhen the source does not answer within 12 s.- Durations over 24 h in
sleep(),fadeTo()andfadereject withINVALID_ARGUMENT. - Files served without
Content-Length: load them withlive: falseso they end at their end (iOS used to treat them as radio).
Android software decoders (android-decoders-1.11.1-1)
Media3's software decoders for Android, built by scripts/decoders/build-android.sh at 494d392.
Apps get them with rnapVideoDecoders=true. FFmpeg is LGPL; see the Video guide.
3960872ec58ff265c71314cf8224629e1d0ab6db3d0761ad4761f6d9bc1f35e9 media3-decoder-av1-1.11.1.aar
8377f18592455cda0a954103e0c08bbad54072173fcf65580430c7b1fcd07256 media3-decoder-ffmpeg-1.11.1.aar
443b0bf5baaceb44b826713cc3203326d8dfd53791916efa8cfc9709d70fb5ad media3-decoder-vp9-1.11.1.aar
v1.2.0
1.2.0 (2026-10-23)
Short sounds, cars and watches, a sleep timer, and a CLI that sets up the optional parts. Everything is additive except one rule for catalog ids (see Upgrading).
Added
- Sound API (
react-native-anything-player/sound): short sounds for UI clicks and games, preloaded into memory and played with low latency. Several voices at once (the oldest is stolen when all are busy), per-play volume, rate, pan and loop, a handle per voice, andstopAll(). It mixes with other audio and has no media session. iOS on AVAudioEngine, Android on one shared SoundPool (32 voices), the web on Web Audio. Opt-in on iOS and Android with the Expo plugin'ssoundoption (rnapSound=true/RNAP_SOUND=1without Expo); always available on the web. New error codeSOUND_UNAVAILABLE. See Sound. - Cars and watches. Catalog items take a
group("Jazz","Podcasts/Daily"), and the catalog becomes a browse tree in CarPlay, Android Auto, Android Automotive OS and Wear OS, with "Recently played" and favorites (setCatalog(items, { browse }),setFavorites(ids)). A pick plays through the same path as a voice request and is reported assearchPlaywithrequest.origin: 'browse'. Long folders are split for car screens, andsearchCatalog/playFromSearchfilter by group. CarPlay and Android Auto are opt-in with the plugin'scaroption (rnapCar/RNAP_CARwithout Expo); Wear OS needs nothing. See Cars & watches. - Sleep timer and fades.
player.sleep({ after, fadeOut })pauses after a delay, fading out first;sleep(null)cancels it,status.sleepAtand asleepevent report it, anduseSleepTimer()counts down. Opt-in fades on play, pause and stop (new Player({ fade: { in, out } }),setFade()), andfadeTo(volume, seconds). The native engine runs them, so they keep working while JavaScript is frozen in the background. 25 new conformance scenarios, passed by all three engines. See Playback. - CLI: modules, size, info, migrate.
npx rnapis the documented name.rnap modules,rnap addandrnap removeturn the optional parts on and off (HLS, Cast, Siri, Assistant, the iOS background mode, the Sound API, cars) by writinggradle.properties,Podfile.properties.jsonor the Expo plugin's options, after showing the diff.rnap sizeshows what each costs in a release APK and a web page.rnap infoprints a report to paste into a bug report.rnap migraterewrites react-native-track-player, expo-audio and expo-av code to RNAP where the mapping is mechanical, and marks the rest. A write is atomic and refused if the file changed after its diff was shown.doctorreports module settings that disagree. See CLI.
Changed
- Web: less JavaScript on page load. The hls.js glue loads when hls.js is registered (
configureHls), and Google Cast's driver and Firefox's MP4 repackaging only when they are used: about 8 KB a page without them never loads. The Sound API is its own entry, so a page that only streams does not load it. - The API reference has a
playerand asoundsection; links to its old paths redirect.
Upgrading
- Catalog ids starting with
rnap:are rejected withINVALID_ARGUMENT, because the browse tree names its folders with that prefix. Rename such ids before upgrading. No other change is needed.