Confused by `VideoDecoderConfig.codedWidth`, does not reflect the actual coded width of the underlying coded frame · Issue #925 · w3c/webcodecs · GitHub
Skip to content

Confused by VideoDecoderConfig.codedWidth, does not reflect the actual coded width of the underlying coded frame #925

Open

Description

@Vanilagy

In all permutations I have tried ((Chrome, Firefox, Safari) x (Windows, Mac)), the codedWidth and codedHeight properties in the VideoDecoderConfig emitted by VideoEncoder contain the display dimensions of the frame, or in other words, the dimensions with which the encoder was initialized. In my work on Mediabunny, I have relied on this behavior extensively as a means to communicate video dimensions to muxers.

However, I kind of just realized... that behavior is wrong (probably)? The definition of VideoDecoderConfig.codedWidth is:

Width of the VideoFrame in pixels, potentially including non-visible padding, and prior to considering potential ratio adjustments.

Which is identical to the definition of VideoFrame.codedWidth.

So let's say we do this:

const decoder = new VideoDecoder({
	output: (frame) => {
		console.log("Actual coded width:", frame.codedWidth);
		console.log(frame);
	},
	error: console.error,
});

const encoder = new VideoEncoder({
	output: (chunk, meta) => {
		console.log("Reported coded width:", meta.decoderConfig.codedWidth);

		decoder.configure(meta.decoderConfig);
		decoder.decode(chunk);
		decoder.flush();
	},
	error: console.error,
});
encoder.configure({
	codec: 'avc1.64001f',
	width: 650,
	height: 650,
	bitrate: 1e6,
});

const canvas = new OffscreenCanvas(650, 650);
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';
ctx.fillRect(100, 100, 200, 200);

const frame = new VideoFrame(canvas, { timestamp: 0 });
encoder.encode(frame);

await encoder.flush();

We encode a 650x650 frame using AVC, which then, when decoded, has a codedWidth of 656. Makes total sense, since the dimensions have been rounded up to the nearest 16x16 macroblock, and 16 divides 656. But, the decoder config reports the codedWidth as 650.

So, same field, same definition in the spec, different values.

While I think it's useful that it contains the display dimensions, I would argue the behavior is not technically correct. Here's what I would expect:

  • codedWidth reflects the actual codedWidth you'd get by decoding the chunks (this makes it consistent, and actually provides new information that you didn't already know based on how you set up the encoder)
  • VideoDecoderConfig is extended with displayWidth, displayHeight, visibleRect, and codedRect, with definitions identical to those on VideoFrame.

What do you guys think? Is the current behavior of codedWidth faulty or intentional? Can this be fixed, or is it too late now since any change might break existing software that relies on the current behavior?

Edit:
My proposed ""change"" to codedWidth would mean that demuxers, when extracting VideoDecoderConfig for VideoDecoder, would need more complicated logic to derive the coded width, since they likely only know the display dimensions from the container metadata. Luckily, the fields are optional, so any demuxer could just omit it. But still, kinda annoying. Perhaps one should go the other route, retconning codedWidth to be defined in the spec as it is currently provided by browsers?

Activity

  1. dalecurtis commented on Feb 23, 2026

    @dalecurtis
    Contributor

    The true coded size isn't actually known until decoding since it can depend on a bunch of software defined factors; E.g., a UA may align by 16-bytes or 32-bytes depending on SIMD availability.

    I think it would be helpful to clarify in the spec text that codedSize on the config should actually be what VideoFrame calls visibleRect.size(). Which is separate from display size which is aspect ratio adjusted :|

  2. Vanilagy commented on Feb 24, 2026

    @Vanilagy
    Author

    That's good! Then it'd be clear.

    Question, if the coded size is so dependent on the underlying UA implementation, why is it exposed at all? How is it relevant to a user of WebCodecs?

  3. dalecurtis commented on Feb 24, 2026

    @dalecurtis
    Contributor

    When given to the decoder it's a hint to help select an appropriate decoder w/o having to do bitstream parsing. E.g., on Android some decoders may be limited to 1080p. On VideoFrame instances it may allow a zero copy access solution in the future if JS ever gets read-only memory. It's naturally generated as part of encoding.

  4. Vanilagy commented on Sep 24, 2026

    @Vanilagy
    Author

    @dalecurtis Would it be possible to push forward an effort to amend the spec here? The problem I'm having is that VideoDecoderConfig does not reliably give me the actual display dimensions of the frame. I think VideoDecoderConfig should be extended with displayWidth/displayHeight fields, of codedWidth and codedHeight should be spec'd as the width/height of the raw frame's visible rect (thus, before pixel aspect ratio corrections). This is confusing given the name, but doing anything else would be a breaking change I think.

    I use the coded fields in Mediabunny as an indicator of the display dimensions, but sometimes (and I haven't been able to reproduce this, and it may actually not be reproducible at all), Chromium fills the fields with the actual coded dimensions instead; making my muxer think the stream is actually 1920x1088 large. See Vanilagy/mediabunny#527

    I had Claude run a quick investigation and it said the current Chromium codebase cannot produce a value of 1088 for codedHeight when encoding 1080p; so this could actually be erroneous user code causing this.

  5. dalecurtis commented on Sep 24, 2026

    @dalecurtis
    Contributor

    I'd bet that's some hardware encoder bug if it's not reproducible elsewhere. IIUC, the user specified a 1920x1080 encoder size and encoder padded to 1088. Which isn't uncommon, but maybe the real bug is that it didn't write the correct SPS/PPS values for cropping. If you can get a sample bitstream from this device that would help figure it out. In this case, I don't think additional fields would help.

    I'm not opposed to adding more fields or more metadata on the output config though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions