A transcode is reported with no bitrate until ffmpeg names one #174

Closed
opened 2026-08-21 18:46:12 +02:00 by Claude · 0 comments
Collaborator

A running transcode is reported with no bitrate at all until ffmpeg names one,
where upstream falls back to the bitrate the job was asked to produce.

Seen in the player statistics of a transcoding HLS playback. Upstream's
Transcoding Info carries Bitrate 16.6 Mbps beside the codecs; ours carries
the codecs, the channel count and nothing else:

upstream   Video codec  H264 / Audio codec  AAC / Audio channels  2 / Bitrate  16.6 Mbps
ours       Target codecs  H264,  AAC,  2 Ch

The bundle draws that value only when TranscodingInfo.Bitrate is truthy, and
ours was not there.

crates/ephyra-http/src/map/session.rs::transcoding fills it from the encoder's
own account of itself and from nothing else:

bitrate: progress.and_then(|progress| progress.bit_rate),

Upstream reports the same field from the progress line or from the job:
TranscodeManager.ReportTranscodingProgress ends in

Bitrate = bitRate ?? state.TotalOutputBitrate,

and EncodingJobInfo.TotalOutputBitrate is (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0) — settled before ffmpeg starts, so a session is never
without one.

What it costs. ffmpeg writes bitrate=N/A until it has written enough to
average over, so every transcode reads as bitrate-less for its first seconds —
and a job whose progress never carries one reads that way for its whole life.
The dashboard's Now Playing tile and the player statistics both draw from this
field, and a session that is plainly transcoding at some rate shows none.

Where to start

streaming::transcode::Target is what the route already has to hand and it
carries what the job was asked for — the codecs, the size, the channels. It does
not carry the two output bitrates, so they have to be put on it where the rest of
the target is settled; the sum is upstream's TotalOutputBitrate, and the
progress line's own figure still wins where there is one.

How to know it is done

A session whose progress carries no bitrate is reported with the target's, and
one whose progress carries a bitrate is reported with that. Both fail today —
the first reports nothing, and there is no second to break.

House rules

CLAUDE.md is the short version and these are the parts that bite: tests run
under cargo nextest; upstream's source is the checkout in upstream/ at the
pin
; generated files are regenerated rather than edited; a comment says what
the code does, not how it got there.

A test that cannot fail is worse than none.

A running transcode is reported with no bitrate at all until ffmpeg names one, where upstream falls back to the bitrate the job was asked to produce. Seen in the player statistics of a transcoding HLS playback. Upstream's *Transcoding Info* carries `Bitrate 16.6 Mbps` beside the codecs; ours carries the codecs, the channel count and nothing else: upstream Video codec H264 / Audio codec AAC / Audio channels 2 / Bitrate 16.6 Mbps ours Target codecs H264, AAC, 2 Ch The bundle draws that value only when `TranscodingInfo.Bitrate` is truthy, and ours was not there. `crates/ephyra-http/src/map/session.rs::transcoding` fills it from the encoder's own account of itself and from nothing else: bitrate: progress.and_then(|progress| progress.bit_rate), Upstream reports the same field from the progress line **or** from the job: `TranscodeManager.ReportTranscodingProgress` ends in Bitrate = bitRate ?? state.TotalOutputBitrate, and `EncodingJobInfo.TotalOutputBitrate` is `(OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0)` — settled before ffmpeg starts, so a session is never without one. **What it costs.** ffmpeg writes `bitrate=N/A` until it has written enough to average over, so every transcode reads as bitrate-less for its first seconds — and a job whose progress never carries one reads that way for its whole life. The dashboard's *Now Playing* tile and the player statistics both draw from this field, and a session that is plainly transcoding at some rate shows none. ## Where to start `streaming::transcode::Target` is what the route already has to hand and it carries what the job was asked for — the codecs, the size, the channels. It does not carry the two output bitrates, so they have to be put on it where the rest of the target is settled; the sum is upstream's `TotalOutputBitrate`, and the progress line's own figure still wins where there is one. ## How to know it is done A session whose progress carries no bitrate is reported with the target's, and one whose progress carries a bitrate is reported with that. Both fail today — the first reports nothing, and there is no second to break. ### House rules `CLAUDE.md` is the short version and these are the parts that bite: tests run under `cargo nextest`; upstream's source is the checkout in `upstream/` **at the pin**; generated files are regenerated rather than edited; a comment says what the code does, not how it got there. A test that cannot fail is worse than none.
Sign in to join this conversation.
No description provided.