Skip to content

Commit e393910

Browse files
committed
feat(encoder): added auto selection of the most suitable output pix_fmt
Signed-off-by: k4yt3x <[email protected]>
1 parent e477123 commit e393910

File tree

5 files changed

+85
-12
lines changed

5 files changed

+85
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Automatic selection of the most suitable pixel format for the output video.
13+
1014
### Fixed
1115

1216
- Timestamp errors processing frames with PTS equal to 0 (#1222).

include/libvideo2x/avutils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ extern "C" {
77

88
int64_t get_video_frame_count(AVFormatContext *ifmt_ctx, int in_vstream_idx);
99

10+
enum AVPixelFormat
11+
get_encoder_default_pix_fmt(const AVCodec *encoder, AVPixelFormat target_pix_fmt);
12+
1013
#endif // AVUTILS_H

src/avutils.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
#include "avutils.h"
22

3+
extern "C" {
4+
#include <libavcodec/avcodec.h>
5+
#include <libavutil/pixdesc.h>
6+
}
7+
38
#include <spdlog/spdlog.h>
49

510
int64_t get_video_frame_count(AVFormatContext *ifmt_ctx, int in_vstream_idx) {
@@ -48,3 +53,68 @@ int64_t get_video_frame_count(AVFormatContext *ifmt_ctx, int in_vstream_idx) {
4853
// Estimate and return the total number of frames
4954
return static_cast<int64_t>(duration_secs * fps);
5055
}
56+
57+
enum AVPixelFormat
58+
get_encoder_default_pix_fmt(const AVCodec *encoder, AVPixelFormat target_pix_fmt) {
59+
int ret;
60+
char errbuf[AV_ERROR_MAX_STRING_SIZE];
61+
62+
// Retrieve the list of supported pixel formats
63+
const enum AVPixelFormat *supported_pix_fmts = nullptr;
64+
ret = avcodec_get_supported_config(
65+
nullptr, encoder, AV_CODEC_CONFIG_PIX_FORMAT, 0, (const void **)&supported_pix_fmts, nullptr
66+
);
67+
if (ret < 0) {
68+
av_strerror(ret, errbuf, sizeof(errbuf));
69+
spdlog::error("Failed to get supported pixel formats: {}", errbuf);
70+
return AV_PIX_FMT_NONE;
71+
}
72+
73+
if (supported_pix_fmts == nullptr) {
74+
if (target_pix_fmt == AV_PIX_FMT_NONE) {
75+
spdlog::warn("Encoder supports all pixel formats; defaulting to yuv420p");
76+
return AV_PIX_FMT_YUV420P;
77+
} else {
78+
spdlog::warn("Encoder supports all pixel formats; defaulting to the decoder's format");
79+
return target_pix_fmt;
80+
}
81+
}
82+
83+
// Determine if the target pixel format has an alpha channel
84+
const AVPixFmtDescriptor *desc = nullptr;
85+
int has_alpha = 0;
86+
if (target_pix_fmt != AV_PIX_FMT_NONE) {
87+
desc = av_pix_fmt_desc_get(target_pix_fmt);
88+
has_alpha = desc ? (desc->nb_components % 2 == 0) : 0;
89+
}
90+
91+
// Iterate over supported pixel formats to find the best match
92+
enum AVPixelFormat best_pix_fmt = AV_PIX_FMT_NONE;
93+
for (const enum AVPixelFormat *p = supported_pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
94+
if (target_pix_fmt != AV_PIX_FMT_NONE) {
95+
best_pix_fmt =
96+
av_find_best_pix_fmt_of_2(best_pix_fmt, *p, target_pix_fmt, has_alpha, nullptr);
97+
if (*p == target_pix_fmt) {
98+
best_pix_fmt = target_pix_fmt;
99+
break;
100+
}
101+
} else {
102+
best_pix_fmt = *p;
103+
break;
104+
}
105+
}
106+
if (best_pix_fmt == AV_PIX_FMT_NONE) {
107+
spdlog::error("No suitable pixel format found for encoder");
108+
}
109+
110+
if (target_pix_fmt != AV_PIX_FMT_NONE && best_pix_fmt != target_pix_fmt) {
111+
spdlog::warn(
112+
"Incompatible pixel format '%s' for encoder '%s'; auto-selecting format '%s'",
113+
av_get_pix_fmt_name(target_pix_fmt),
114+
encoder->name,
115+
av_get_pix_fmt_name(best_pix_fmt)
116+
);
117+
}
118+
119+
return best_pix_fmt;
120+
}

src/encoder.cpp

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@
55
#include <string.h>
66
#include <cstdint>
77

8+
extern "C" {
9+
#include <libavutil/pixdesc.h>
10+
}
11+
812
#include <spdlog/spdlog.h>
913

14+
#include "avutils.h"
1015
#include "conversions.h"
1116

12-
static enum AVPixelFormat get_encoder_default_pix_fmt(const AVCodec *encoder) {
13-
const enum AVPixelFormat *p = encoder->pix_fmts;
14-
if (!p) {
15-
spdlog::error("No pixel formats supported by encoder");
16-
return AV_PIX_FMT_NONE;
17-
}
18-
return *p;
19-
}
20-
2117
int init_encoder(
2218
AVBufferRef *hw_ctx,
2319
std::filesystem::path out_fpath,
@@ -86,12 +82,12 @@ int init_encoder(
8682
// Use the specified pixel format
8783
codec_ctx->pix_fmt = encoder_config->pix_fmt;
8884
} else {
89-
// Fall back to the default pixel format
90-
codec_ctx->pix_fmt = get_encoder_default_pix_fmt(encoder);
85+
codec_ctx->pix_fmt = get_encoder_default_pix_fmt(encoder, dec_ctx->pix_fmt);
9186
if (codec_ctx->pix_fmt == AV_PIX_FMT_NONE) {
9287
spdlog::error("Could not get the default pixel format for the encoder");
9388
return AVERROR(EINVAL);
9489
}
90+
spdlog::debug("Auto-selected pixel format: {}", av_get_pix_fmt_name(codec_ctx->pix_fmt));
9591
}
9692

9793
// Set the output video's time base

src/libvideo2x.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ extern "C" int process_video(
344344
cleanup();
345345
return -1;
346346
}
347-
spdlog::info("Output video dimensions: {}x{}", output_width, output_height);
347+
spdlog::debug("Output video dimensions: {}x{}", output_width, output_height);
348348

349349
// Initialize output encoder
350350
encoder_config->out_width = output_width;

0 commit comments

Comments
 (0)