Skip to content

Conversation

@Dan-Flores
Copy link
Contributor

@Dan-Flores Dan-Flores commented Nov 13, 2025

This PR adds the codec_options dictionary arg to enable all remaining codec arguments.
The Python API accepts a Dict[str, str], converts it to a flattened List[str] to pass to custom_ops.py, which rebuilds the dict in unflattenCodecOptions().

Validation

To validate numeric options passed into codec_options, validateDoubleOption is refactored into tryToValidateCodecOption:

  • Check the type for each option
  • If numeric, attempt to convert provided value to a double
  • Validate value is in valid range
  • Do no validation for non-numeric options

Testing

The error handling done in tryToValidateCodecOption is documented in test_codec_options_errors.

Via local testing, I also checked that:

  • sortCodecOptions properly finds and sets format options correctly
  • Complex args such as {"-x264-params": "keyint=120:bframes=3"} are utilized

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 13, 2025
@Dan-Flores Dan-Flores marked this pull request as ready for review November 14, 2025 05:22
Copy link
Contributor

@NicolasHug NicolasHug left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Dan-Flores , looks great! Made a few comments below.

pixel_format: Optional[str] = None,
crf: Optional[Union[int, float]] = None,
preset: Optional[Union[str, int]] = None,
codec_options: Optional[Dict[str, str]] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make it Dict[str, Any]? I think there's value in allowing "some_param":16 instead of the un-pythonic "some_param: "16". We just need to call str() on all the values, which is OK.

a string: "fast", "medium", "slow"). Defaults to None
(which will use encoder's default).
codec_options (dict[str, str], optional): A dictionary of codec-specific
options to pass to the encoder, e.g. ``{"preset": "slow", "tune": "film"}``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have preset as a built-in parameter so it might be confusing to document it here.

Comment on lines 163 to 166
void sortCodecOptions(
const std::map<std::string, std::string>& codecOptions,
AVDictionary** codecDict,
AVDictionary** formatDict);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it could be a pure function in an anonymous namespace rather than a method?

from torchcodec import _core


def _flatten_codec_options(codec_options: Optional[Dict[str, str]]) -> Optional[list]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: write that at the bottom like the rest of the helpers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably don't need a function to do this in Python, I'll remove the function altogether.

"avcodec_open2 failed: Invalid argument",
),
],
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job on the parametrization above

crf: Optional[Union[int, float]] = None,
pixel_format: Optional[str] = None,
preset: Optional[str] = None,
codec_options: Optional[list[str]] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting to wonder if that's the right name for this parameter. IIUC, this can also be used to set the AVFormatContext parameters, right? So it's not just related to the codec itself?

Maybe this could be extra_options or something like that? CC @scotts @mollyxu

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, extra_options is probably better.

av_dict_set(formatDict, key.c_str(), value.c_str(), 0);
} else {
// Default to codec option (includes AVCodecContext + encoder-private)
// validateCodecOption(*avCodecContext_->codec, key.c_str(), value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove:

Suggested change
// validateCodecOption(*avCodecContext_->codec, key.c_str(), value);

Comment on lines 702 to 703
AVDictionary** codecDict,
AVDictionary** formatDict) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface is OK for now but we should consider changing it once we use RAII types for the dics (see my other follow-up suggestion).

const std::map<std::string, std::string>& codecOptions,
AVDictionary** codecDict,
AVDictionary** formatDict) {
// Search AVFormatContext's AVClass for options
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this comment, it doesn't add much value. Let's also document what this function is doing: it takes some options as input and sorts them into codec options and format options, which are returned into two separate dicts.

Copy link
Contributor

@NicolasHug NicolasHug left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @Dan-Flores !

Comment on lines 124 to 126
extra_options=[
x for k, v in (extra_options or {}).items() for x in (k, str(v))
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest something like that instead of the previous _flatten_codec_options but refrained as I thought it might be too crazy lol.

I like it, I'd just suggest the following, which is a tiiiiiny bit safer, and makes it more obvious that everything in the list is a string.

Suggested change
extra_options=[
x for k, v in (extra_options or {}).items() for x in (k, str(v))
],
extra_options=[
str(x) for k, v in (extra_options or {}).items() for x in (k, v)
],

Comment on lines 162 to 164
extra_options (dict[str, Any], optional): A dictionary of additional
encoder options to pass, e.g. ``{"preset": "slow", "tune": "film"}``.
Values will be converted to strings before passing to the encoder.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to align the docs (this one still have preset), same with the one below

Comment on lines 1095 to 1097
actual_codec_spec = self._get_video_metadata(dest, fields=["codec_name"]).get(
"codec_name"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here and everywhere, use the [] syntax instead of .get(). We usually get() only when we want to specify a fallback value, which I don't think is intended here.

Comment on lines 1177 to 1178
# Validate profile (case-insensitive, baseline is reported as "Constrained Baseline")
assert profile in metadata.get("profile", "").lower()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest the following, because using in is less strict and can sometimes be surprisingly easy to pass, like assert "" in "abc"

Suggested change
# Validate profile (case-insensitive, baseline is reported as "Constrained Baseline")
assert profile in metadata.get("profile", "").lower()
expected_profile = "constrained baseline" if profile == "baseline" else profile
assert metadata["profile"].lower() == expected_profile

@Dan-Flores Dan-Flores merged commit c69064f into meta-pytorch:main Nov 14, 2025
70 checks passed
@Dan-Flores Dan-Flores deleted the codec_options_encode_option branch November 14, 2025 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants