mirror of
https://mirror.skon.top/https://github.com/FFmpeg/FFmpeg
synced 2026-04-21 13:21:55 +08:00
avcodec/libsvtav1: enable 2-pass encoding
This patch enables two-pass encoding for libsvtav1 by implementing support for AV_CODEC_FLAG_PASS1 and AV_CODEC_FLAG_PASS2. Previously, users requiring two-pass encoding with SVT-AV1 had to use the standalone SvtAv1EncApp tool. This patch allows 2-pass encoding directly through FFmpeg. Based on patch by Fredrik Lundkvist, with review feedback from James Almer and Andreas Rheinhardt. See: https://ffmpeg.org/pipermail/ffmpeg-devel/2024-May/327452.html Changes: - Use AV_BASE64_DECODE_SIZE macro for buffer size calculation - Allocate own buffer for rc_stats_buffer (non-ownership pointer) - Error handling with buffer cleanup Signed-off-by: Werner Robitza <werner.robitza@gmail.com>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <EbSvtAv1ErrorCodes.h>
|
||||
#include <EbSvtAv1Enc.h>
|
||||
#include <EbSvtAv1Metadata.h>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "libavutil/common.h"
|
||||
#include "libavutil/frame.h"
|
||||
#include "libavutil/imgutils.h"
|
||||
#include "libavutil/base64.h"
|
||||
#include "libavutil/intreadwrite.h"
|
||||
#include "libavutil/mastering_display_metadata.h"
|
||||
#include "libavutil/mem.h"
|
||||
@@ -65,6 +67,8 @@ typedef struct SvtContext {
|
||||
|
||||
DOVIContext dovi;
|
||||
|
||||
uint8_t *stats_buf;
|
||||
|
||||
// User options.
|
||||
AVDictionary *svtav1_opts;
|
||||
int enc_mode;
|
||||
@@ -341,6 +345,42 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param,
|
||||
return AVERROR(ENOSYS);
|
||||
}
|
||||
#endif
|
||||
if (avctx->flags & AV_CODEC_FLAG_PASS2) {
|
||||
int stats_sz;
|
||||
|
||||
if (!avctx->stats_in) {
|
||||
av_log(avctx, AV_LOG_ERROR, "No stats file for second pass\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
stats_sz = AV_BASE64_DECODE_SIZE(strlen(avctx->stats_in));
|
||||
if (stats_sz <= 0) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Invalid stats file size\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
svt_enc->stats_buf = av_malloc(stats_sz);
|
||||
if (!svt_enc->stats_buf) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Failed to allocate stats buffer\n");
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
stats_sz = av_base64_decode(svt_enc->stats_buf, avctx->stats_in, stats_sz);
|
||||
if (stats_sz < 0) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Failed to decode stats file\n");
|
||||
av_freep(&svt_enc->stats_buf);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
param->rc_stats_buffer.buf = svt_enc->stats_buf;
|
||||
param->rc_stats_buffer.sz = stats_sz;
|
||||
param->pass = 2;
|
||||
|
||||
av_log(avctx, AV_LOG_VERBOSE, "Using %d bytes of 2-pass stats\n", stats_sz);
|
||||
} else if (avctx->flags & AV_CODEC_FLAG_PASS1) {
|
||||
param->pass = 1;
|
||||
av_log(avctx, AV_LOG_VERBOSE, "Starting first pass\n");
|
||||
}
|
||||
|
||||
param->source_width = avctx->width;
|
||||
param->source_height = avctx->height;
|
||||
@@ -618,9 +658,45 @@ static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt)
|
||||
|
||||
#if SVT_AV1_CHECK_VERSION(2, 0, 0)
|
||||
if (headerPtr->flags & EB_BUFFERFLAG_EOS) {
|
||||
svt_enc->eos_flag = EOS_RECEIVED;
|
||||
svt_av1_enc_release_out_buffer(&headerPtr);
|
||||
return AVERROR_EOF;
|
||||
if (avctx->flags & AV_CODEC_FLAG_PASS1) {
|
||||
SvtAv1FixedBuf first_pass_stats = { 0 };
|
||||
EbErrorType svt_ret_stats;
|
||||
int b64_size;
|
||||
|
||||
svt_ret_stats = svt_av1_enc_get_stream_info(
|
||||
svt_enc->svt_handle,
|
||||
SVT_AV1_STREAM_INFO_FIRST_PASS_STATS_OUT,
|
||||
&first_pass_stats);
|
||||
|
||||
if (svt_ret_stats != EB_ErrorNone) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"Failed to get first pass stats\n");
|
||||
svt_av1_enc_release_out_buffer(&headerPtr);
|
||||
return AVERROR_EXTERNAL;
|
||||
}
|
||||
|
||||
if (first_pass_stats.sz > 0 && first_pass_stats.buf) {
|
||||
b64_size = AV_BASE64_SIZE(first_pass_stats.sz);
|
||||
avctx->stats_out = av_malloc(b64_size);
|
||||
if (!avctx->stats_out) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"Failed to allocate stats output buffer\n");
|
||||
svt_av1_enc_release_out_buffer(&headerPtr);
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
av_base64_encode(avctx->stats_out, b64_size,
|
||||
first_pass_stats.buf, first_pass_stats.sz);
|
||||
|
||||
av_log(avctx, AV_LOG_VERBOSE,
|
||||
"First pass stats: %"PRIu64" bytes, encoded to %d bytes\n",
|
||||
first_pass_stats.sz, b64_size);
|
||||
}
|
||||
}
|
||||
|
||||
svt_enc->eos_flag = EOS_RECEIVED;
|
||||
svt_av1_enc_release_out_buffer(&headerPtr);
|
||||
return AVERROR_EOF;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -688,6 +764,7 @@ static av_cold int eb_enc_close(AVCodecContext *avctx)
|
||||
av_buffer_pool_uninit(&svt_enc->pool);
|
||||
av_frame_free(&svt_enc->frame);
|
||||
ff_dovi_ctx_unref(&svt_enc->dovi);
|
||||
av_freep(&svt_enc->stats_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user