Interface: NetworkReconnectOptions
Defined in: packages/player/src/player.ts:302
FFmpeg's own HTTP reconnection, wired through mpv's stream-lavf-o.
Remarks
This is the primary recovery layer, and it is entirely native. It runs inside libavformat's read loop, on mpv's demuxer thread, with no JavaScript and no timers anywhere near it — which is the only kind of retry that works with the screen off (ARCHITECTURE, "Platform truths": JS timers freeze in the background). PlayerOptions.retry is the second layer and covers the failures this one cannot see, namely an entry that never opened at all.
What is set, and why each one
Verified against the shipped binary (strings on
libmpv.so, v1.1.9-rnmedia.7, FFmpeg n8.1.2 / Lavf62.12.102) and
against that exact FFmpeg tree's libavformat/http.c:
| AVOption | value | what it buys |
|---|---|---|
reconnect | 1 | reconnects a premature end of a sized response — http.c:1871, guarded by is_premature = filesize > 0 && off < filesize. This is the truncated-download case |
reconnect_on_network_error | 1 | retries the connect on any non-HTTP-status failure — DNS, refused TCP, TLS — http.c:437 via http_should_reconnect()'s default: arm. This is the "the radio came back" case |
reconnect_streamed | 1 | lifts http.c:1868's hard break for non-seekable streams, which is what makes any mid-read retry legal on a live stream at all |
reconnect_delay_max | maxDelaySeconds | the give-up bound. FFmpeg's backoff is delay = 1 + 2 * delay from 0, so 5 means attempts at 0 s, 1 s and 3 s and then a stop (the next delay, 7, exceeds it) — about four seconds of trying |
FFmpeg's own defaults for all four are off / 120 s
(http.c:195-201), which is why this is opt-out rather than opt-in.
What it does not cover, stated plainly
reconnect_at_eofis deliberately NOT set. It is the option that would make a live stream reconnect when the server simply closes the connection — and it is unsafe as a global default, becausehttp.c:1871does not guard it onis_streamed. On an ordinary sized file, reaching the natural end of the response isAVERROR_EOF, so enabling it turns every clean track end into a reconnect storm that runs for maxDelaySeconds and then returnsAVERROR(EIO)— i.e. it would convert "the song finished" into "the song failed", destroying the verytrackEnded-vs-errordistinction this library is built around. An app whose queue is only live streams can still opt in, by passing the whole list raw (see below).- HTTP status codes. A
404or a503is not retried; FFmpeg gates that onreconnect_on_http_error, which takes a status list and is a policy decision an app must make for itself. Pass it raw if you want it. - A source that never opened. If the very first connect exhausts its
attempts, mpv fails the load and the entry ends with a typed
error. That is PlayerOptions.retry's job, not this one's. - Non-HTTP protocols. These are
libavformat/http.coptions.file://,rtsp://,srt://and friends ignore them (mpv's manual: "Unknown or misspelled options are silently ignored").
Cost
A hard connect failure now takes up to ~maxDelaySeconds before mpv reports
it, instead of failing at once. That is the trade: bounded, and paid only on
a failure path.
Overriding
A raw mpvOptions['stream-lavf-o'] replaces this whole list — mpv's
key/value list options are set, not merged. So opting into one extra key
means writing all of them:
await Player.create({
mpvOptions: {
'stream-lavf-o':
'reconnect=1,reconnect_on_network_error=1,reconnect_streamed=1,' +
'reconnect_delay_max=5,reconnect_at_eof=1', // live-only app
},
})
Properties
enabled?
readonly optional enabled?: boolean;
Defined in: packages/player/src/player.ts:309
Turn the whole thing off, leaving FFmpeg's own defaults (every reconnect option disabled).
Default Value
true
maxDelaySeconds?
readonly optional maxDelaySeconds?: number;
Defined in: packages/player/src/player.ts:321
FFmpeg's reconnect_delay_max, in seconds: retrying stops once the next
backoff step would exceed it.
Must be an integer in 0 … 4294 — FFmpeg's own domain
(AV_OPT_TYPE_INT, M_RANGE(0, UINT_MAX/1000/1000), http.c:200).
Anything else throws an invalid-state PlayerError rather than
being clamped.