commaai/openpilot · error · FFmpegError
convert decoded frame: produced {rows} of {frame.height} row
Error message
convert decoded frame: produced {rows} of {frame.height} rows What it means
In FFmpegDecoder's frame-receive path, sws_scale() was asked to convert frame.height rows from the decoder's pixel format to the output format, but it returned fewer rows. That means the swscale context and the decoded frame disagree (wrong dimensions or pixel format), so the produced NV12/RGB buffer is incomplete and unusable. The decoder raises rather than return a partially converted frame.
Source
Thrown at openpilot/tools/camerastream/ffmpeg_decoder.py:227
"""Return one NV12 frame, or None if the decoder needs more input.
The returned buffer is reused on the next successful decode; callers must
use or copy it before calling decode again.
"""
result = _avcodec.avcodec_receive_frame(self._context, self._frame)
if result == -errno.EAGAIN:
return None
_check(result, "receive decoded frame")
try:
frame = self._frame.contents
self._prepare_output(frame)
rows = _swscale.sws_scale(
self._sws_context, frame.data, frame.linesize, 0, frame.height,
self._dst_data, self._dst_linesize,
)
if rows != frame.height:
raise FFmpegError(f"convert decoded frame: produced {rows} of {frame.height} rows")
return self._output
finally:
_avcodec.av_frame_unref(self._frame)
def decode(self, data) -> np.ndarray | None:
self._ensure_open()
if len(data) == 0:
return None
self._prepare_packet(data)
result = _avcodec.avcodec_send_packet(self._context, self._packet)
# The packet buffer is ours, not FFmpeg's. Clear the borrowed pointer so
# packet teardown can never attempt to release it.
self._packet.contents.data = None
self._packet.contents.size = 0
_check(result, "send packet to decoder")
return self._receive()
View on GitHub (pinned to 516ec1e682)
Solutions
- Recreate the swscale context when frame.width/height/format change: compare against the values used at context creation and call sws_getContext again
- Verify the sender/probe resolution matches what the decoder was opened with (avcodec_open2 with the correct width/height)
- Inspect frame.format at failure time - if it is an unexpected pix_fmt, negotiate the format upstream or add an intermediate conversion
Example fix
# before (stale context)
rows = _swscale.sws_scale(self._sws_context, frame.data, frame.linesize, 0, frame.height, ...)
# after (rebuild on change)
if (frame.width, frame.height, frame.format) != self._sws_spec:
_swscale.sws_freeContext(self._sws_context)
self._sws_context = _swscale.sws_getContext(
frame.width, frame.height, frame.format,
frame.width, frame.height, self._dst_fmt,
_swscale.SWS_BILINEAR, None, None, None)
self._sws_spec = (frame.width, frame.height, frame.format)
rows = _swscale.sws_scale(self._sws_context, frame.data, frame.linesize, 0, frame.height, self._dst_data, self._dst_linesize) Defensive patterns
Strategy: type-guard
Validate before calling
frame = self._frame.contents
if (frame.width, frame.height) != self._expected_dims:
raise RuntimeError(f"stream resolution changed to {frame.width}x{frame.height}; rebuild sws context") Type guard
def frame_matches_context(frame, sws_spec) -> bool:
"""True when the decoded frame's dims/format match the swscale context spec."""
return (frame.width, frame.height, frame.format) == sws_spec Try / catch
try:
return self._receive_frame_internal()
except FFmpegError as e:
if 'produced' in str(e) and 'rows' in str(e):
self._rebuild_sws_context(self._frame.contents) # recreate, then retry once
return self._receive_frame_internal()
raise Prevention
- Cache the (width, height, pix_fmt) triple used to create the sws context and rebuild whenever the frame's triple differs
- Never reuse a decoder across streams with different resolutions without draining and reopening the codec context
When it happens
Trigger: Receiving a decoded frame whose width/height/pixel format differs from what the sws_context was created for (stream resolution change mid-stream); calling _prepare_output/frame processing with a stale _sws_context after the codec renegotiated the frame size; an unusual source pix_fmt sws_scale cannot fully convert.
Common situations: Camera stream starts with different resolution than probed (e.g. adaptive encoder); switching cameras/formats on one decoder instance; ffplay/ffmpeg version differences in default output formats.
Related errors
- ffmpeg failed: {result.stderr.decode()}
- bundled firmware is {expected_product!r}, expected version {
- cannot recover from the ROM bootloader without a config back
- invalid config backup: {backup}
- final full-image verification failed
AI-assisted analysis of commaai/openpilot@516ec1e682 (2026-08-15).
Data as JSON: /api/errors/ee4568ac03e25279.
Report an issue: GitHub.