-
Notifications
You must be signed in to change notification settings - Fork 70
Add crf to VideoEncoder API #1031
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4b275f4
740a675
54a8294
8fcc181
d1e5bdf
222e74d
14e797b
b7e52fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| #include "torch/types.h" | ||
|
|
||
| extern "C" { | ||
| #include <libavutil/opt.h> | ||
| #include <libavutil/pixdesc.h> | ||
| } | ||
|
|
||
|
|
@@ -568,6 +569,43 @@ AVPixelFormat validatePixelFormat( | |
| } | ||
| TORCH_CHECK(false, errorMsg.str()); | ||
| } | ||
|
|
||
| void validateDoubleOption( | ||
| const AVCodec& avCodec, | ||
| const char* optionName, | ||
| double value) { | ||
| if (!avCodec.priv_class) { | ||
| return; | ||
| } | ||
| const AVOption* option = av_opt_find2( | ||
| // Convert obj arg from const AVClass* const* to non-const void* | ||
| // First cast to remove const, then cast to void* | ||
| const_cast<void*>(static_cast<const void*>(&avCodec.priv_class)), | ||
| optionName, | ||
| nullptr, | ||
| 0, | ||
| AV_OPT_SEARCH_FAKE_OBJ, | ||
| nullptr); | ||
| // If the option was not found, let FFmpeg handle it later | ||
| if (!option) { | ||
| return; | ||
| } | ||
| if (option->type == AV_OPT_TYPE_INT || option->type == AV_OPT_TYPE_INT64 || | ||
| option->type == AV_OPT_TYPE_FLOAT || option->type == AV_OPT_TYPE_DOUBLE) { | ||
| TORCH_CHECK( | ||
| value >= option->min && value <= option->max, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is comparing an
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment led me to realize that codecs can implement 'crf' as a |
||
| optionName, | ||
| "=", | ||
| value, | ||
| " is out of valid range [", | ||
| option->min, | ||
| ", ", | ||
| option->max, | ||
| "] for this codec. For more details, run 'ffmpeg -h encoder=", | ||
| avCodec.name, | ||
| "'"); | ||
| } | ||
| } | ||
| } // namespace | ||
|
|
||
| VideoEncoder::~VideoEncoder() { | ||
|
|
@@ -700,6 +738,7 @@ void VideoEncoder::initializeEncoder( | |
| // Apply videoStreamOptions | ||
| AVDictionary* options = nullptr; | ||
| if (videoStreamOptions.crf.has_value()) { | ||
| validateDoubleOption(*avCodec, "crf", videoStreamOptions.crf.value()); | ||
| av_dict_set( | ||
| &options, | ||
| "crf", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,7 @@ def to_file( | |
| dest: Union[str, Path], | ||
| *, | ||
| pixel_format: Optional[str] = None, | ||
| crf: Optional[Union[int, float]] = None, | ||
| ) -> None: | ||
| """Encode frames into a file. | ||
|
|
||
|
|
@@ -46,27 +47,35 @@ def to_file( | |
| container format. | ||
| pixel_format (str, optional): The pixel format for encoding (e.g., | ||
| "yuv420p", "yuv444p"). If not specified, uses codec's default format. | ||
| crf (int or float, optional): Constant Rate Factor for encoding quality. Lower values | ||
| mean better quality. Valid range depends on the encoder (commonly 0-51). | ||
| Defaults to None (which will use encoder's default). | ||
| """ | ||
| _core.encode_video_to_file( | ||
| frames=self._frames, | ||
| frame_rate=self._frame_rate, | ||
| filename=str(dest), | ||
| pixel_format=pixel_format, | ||
| crf=crf, | ||
| ) | ||
|
|
||
| def to_tensor( | ||
| self, | ||
| format: str, | ||
| *, | ||
| pixel_format: Optional[str] = None, | ||
| crf: Optional[Union[int, float]] = None, | ||
| ) -> Tensor: | ||
| """Encode frames into raw bytes, as a 1D uint8 Tensor. | ||
|
|
||
| Args: | ||
| format (str): The container format of the encoded frames, e.g. "mp4", "mov", | ||
| "mkv", "avi", "webm", "flv", or "gif" | ||
| "mkv", "avi", "webm", "flv", etc. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q - why remove "gif"? Do we not support it anymore?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not test explicitly for it anymore, but it still works. I mostly wanted to amend the docstring to make it seem less like a finalized, exhaustive list of supported formats. |
||
| pixel_format (str, optional): The pixel format to encode frames into (e.g., | ||
| "yuv420p", "yuv444p"). If not specified, uses codec's default format. | ||
| crf (int or float, optional): Constant Rate Factor for encoding quality. Lower values | ||
| mean better quality. Valid range depends on the encoder (commonly 0-51). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it ever valid to be less than 0?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe -1 is valid and is equivalent to leaving |
||
| Defaults to None (which will use encoder's default). | ||
|
|
||
| Returns: | ||
| Tensor: The raw encoded bytes as 4D uint8 Tensor. | ||
|
|
@@ -76,6 +85,7 @@ def to_tensor( | |
| frame_rate=self._frame_rate, | ||
| format=format, | ||
| pixel_format=pixel_format, | ||
| crf=crf, | ||
| ) | ||
|
|
||
| def to_file_like( | ||
|
|
@@ -84,6 +94,7 @@ def to_file_like( | |
| format: str, | ||
| *, | ||
| pixel_format: Optional[str] = None, | ||
| crf: Optional[Union[int, float]] = None, | ||
| ) -> None: | ||
| """Encode frames into a file-like object. | ||
|
|
||
|
|
@@ -94,14 +105,18 @@ def to_file_like( | |
| ``write(data: bytes) -> int`` and ``seek(offset: int, whence: | ||
| int = 0) -> int``. | ||
| format (str): The container format of the encoded frames, e.g. "mp4", "mov", | ||
| "mkv", "avi", "webm", "flv", or "gif". | ||
| "mkv", "avi", "webm", "flv", etc. | ||
| pixel_format (str, optional): The pixel format for encoding (e.g., | ||
| "yuv420p", "yuv444p"). If not specified, uses codec's default format. | ||
| crf (int or float, optional): Constant Rate Factor for encoding quality. Lower values | ||
| mean better quality. Valid range depends on the encoder (commonly 0-51). | ||
| Defaults to None (which will use encoder's default). | ||
| """ | ||
| _core.encode_video_to_file_like( | ||
| frames=self._frames, | ||
| frame_rate=self._frame_rate, | ||
| format=format, | ||
| file_like=file_like, | ||
| pixel_format=pixel_format, | ||
| crf=crf, | ||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we OK to use
priv_class? I.e. is it meant to be "private"?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the
avcodec.h,priv_classis defined in the section for public fields: https://www.ffmpeg.org/doxygen/2.0/libavcodec_2avcodec_8h_source.html