SVT-AV1接入ffmpeg说明

news/2025/2/27 4:19:51

一 编译集成

Files · v2.3.0 · Alliance for Open Media / SVT-AV1 · GitLab

cd /SVT-AV1/Build/linux/

./build.sh

make install

GitHub - FFmpeg/FFmpeg: Mirror of https://git.ffmpeg.org/ffmpeg.git

./configure --enable-libsvtav1 --enable-gpl --extra-ldflags='-L/usr/local/lib' --extra-libs='-lSvtAv1Enc -lz -lm -lstdc++ -ldl -lpthread' --extra-cflags='-I/usr/local/include/'

make && make install

./ffmpeg -codecs|grep av1

DEV.L. av1 Alliance for Open Media AV1 (decoders: libdav1d av1 av1_cuvid ) (encoders: libsvtav1 )

二 SVT-AV1视频编码命令行

ffmpeg -i input.mp4 -c:v libsvtav1 -crf 30 -preset 4 -y output.mp4

ffmpeg 接入wrapper代码review

#include <stdint.h> //标准头文件

#include <EbSvtAv1ErrorCodes.h> //SVT编码错误信息头文件

#include <EbSvtAv1Enc.h> //SVT编码API头文件

//以下是ffmpeg自己的头文件

#include "libavutil/common.h"

#include "libavutil/frame.h"

#include "libavutil/imgutils.h"

#include "libavutil/opt.h"

#include "libavutil/pixdesc.h"

#include "libavutil/avassert.h"

typedef enum eos_status {

EOS_NOT_REACHED = 0,

EOS_SEND,

EOS_RECEIVED

} EOS_STATUS;

typedef struct SvtContext {

const AVClass *class;

EbSvtAv1EncConfiguration enc_params; //SVT-AV1编码器的配置参数

EbComponentType *svt_handle;//SVT-AV1 编码器的句柄

EbBufferHeaderType *in_buf; //输入缓冲区

int raw_size;//原始数据大

int max_tu_size;//最大传输单元大小

AVFrame *frame;//当前处理的视频帧

AVBufferPool *pool;//缓冲池,用于管理编码输出

EOS_STATUS eos_flag;//结束状态标志

//用户选项

int hierarchial_level; //层次预测级别

int la_depth; //前瞻深度

int enc_mode;//编码模式

int rc_mode ;// 码率控制模式

int scd;//场景变化监测

int qp;//固定量化参数

int tier;//操作点层级

int tile_columns;//tile列数

int tile_rows;//tile行数

} SvtContext;

static const struct {

EbErrorType eb_err; // SVT-AV1错误码

int av_err; // FFmpeg错误码

const char *desc; // 错误描述

} svt_errors[] = {

{ EB_ErrorNone, 0, "success" },

{ EB_ErrorInsufficientResources, AVERROR(ENOMEM), "insufficient resources" },

{ EB_ErrorUndefined, AVERROR(EINVAL), "undefined error" },

{ EB_ErrorInvalidComponent, AVERROR(EINVAL), "invalid component" },

{ EB_ErrorBadParameter, AVERROR(EINVAL), "bad parameter" },

{ EB_ErrorDestroyThreadFailed, AVERROR_EXTERNAL, "failed to destroy thread" },

{ EB_ErrorSemaphoreUnresponsive, AVERROR_EXTERNAL, "semaphore unresponsive" },

{ EB_ErrorDestroySemaphoreFailed, AVERROR_EXTERNAL, "failed to destroy semaphore"},

{ EB_ErrorCreateMutexFailed, AVERROR_EXTERNAL, "failed to create mutex" },

{ EB_ErrorMutexUnresponsive, AVERROR_EXTERNAL, "mutex unresponsive" },

{ EB_ErrorDestroyMutexFailed, AVERROR_EXTERNAL, "failed to destroy mutex" },

{ EB_NoErrorEmptyQueue, AVERROR(EAGAIN), "empty queue" },

};

static int svt_map_error(EbErrorType eb_err, const char **desc)

{

int i;

av_assert0(desc);

for (i = 0; i < FF_ARRAY_ELEMS(svt_errors); i++) {

if (svt_errors[i].eb_err == eb_err) {

*desc = svt_errors[i].desc;

return svt_errors[i].av_err;

}

}

*desc = "unknown error";

return AVERROR_UNKNOWN;

}

static int svt_print_error(void *log_ctx, EbErrorType err,

const char *error_string)

{

const char *desc;

int ret = svt_map_error(err, &desc);

av_log(log_ctx, AV_LOG_ERROR, "%s: %s (0x%x)\n", error_string, desc, err);

return ret;

}

static int alloc_buffer(EbSvtAV1EncConfiguration *config, SvtContext *svt_enc)

{

const int pack_mode_10bit = (config->encoder_bit_depth > 8) && (config->compressed_ten_bit_format == 0) ?1:0; //定一个变量,pack_mode_10bit, 用于判断是否需要对10位编码进行特殊处理

//如果编码器的位深大于8位,并且compressed_ten_bit_format. 为0, 则设置为1,表示需要特殊处理

//否则设置为0

const size_t luma_size_8bit = config->source_width * config->source_height 分别是输入视频的宽度和高度

1 << pack_mode_10bit 如果pack_mode_10bit 为1,则乘以2,否则乘以1,这用语计算10位编码时的额外数据大小

const size_t luma_size_10bit = (config->encoder_bit_depth > 8 && pack_mode_10bit == 0) ? luma_size_8bit: 0;

//计算10位亮度分量的大小

如果编码器的位深度大于8位,并且pack_mode_10bit, 为0,则使用与8位相同的大小,否则设置为0,表示不使用10位编码。

EbSvtIOFormat *in_data; //声明一个指向EbSvtIOFormat 的指针in_data, 用于表示输入数据的格式

svt_enc->raw_size = (luma_size_8bit + luma_size_10bit) * 3/2;

//对于YUV420格式,总大小为亮度分量大小乘以1.5

//如果同时支持8位和10位编码,则两者的大小相加。

//分配输入缓冲区

// 分配输入缓冲区
    svt_enc->in_buf           = av_mallocz(sizeof(*svt_enc->in_buf));
    if (!svt_enc->in_buf)
        return AVERROR(ENOMEM);

//输入缓冲区数据部分分配内存

svt_enc->in_buf->p_buffer = av_mallocz(sizeof(*in_data));
    if (!svt_enc->in_buf->p_buffer)
        return AVERROR(ENOMEM);

svt_enc->in_buf->size = sizeof(*svt_enc->in_buf);

//设置输入缓冲区大小分配内存大小sizeof(*svt_enc->in_buf)

return 0;

}

//这个函数ffmpegAVFrame SVT-AV1输入缓冲结构体 EbSvtIOFormat

static int read_in_data(EbSvtAV1EncConfiguration *param, const AVFrame *frame, EbBufferHeaderType *header_ptr)

{

EbSvtIOFormat *in_data = (EbSvtIOFormat *)header_ptr->p_buffer;

ptrdiff_t linesizes[4];

size_t sizes[4];

int bytes_shift = param->encoder_bit_depth > 8 ?1:0;

int ret, frame_size;

for (int i = 0; i < 4; i++)

linesizes[i] = frame->linesize[i];

ret = av_image_fill_plane_sizes(sizes, frame->format, frame->height, linesizes);

if (ret < 0)

return ret;

frame_size = 0;

for (int i = 0; i < 4; i++) {

if (sizes[i] > INT_MAX - frame_size)

return AVERROR(EINVAL);

frame_size + sizes[i];

}

in_data->luma = frame->data[0];

in_data->cb = frame->data[1];

in_data->cr = frame->data[2];

in_data->y_stride = AV_CEIL_RSHIFT(frame->linesize[0], bytes_shift);

in_data->cb_stride = AV_CEIL_RSHIFT(frame->linesize[1], bytes_shift);

in_data->cr_stride = AV_CEIL_RSHIFT(frame->linesize[2], bytes_shift);

header_ptr->n_filled_len = frame_size;

return 0;

}

static av_cold int eb_enc_init(AVCodecContext *avctx)

{

SvtContext *svt_enc = avctx->priv_data;

EbErrorType svt_ret;

int ret;

svt_enc->eos_flag = EOS_NOT_REASCHED;

svt_ret = svt_av1_enc_init_handle(&svt_enc->svt_handle, svt_enc, &svt_enc->enc_params); //初始化svt_av1 编码handle

if (svt_ret != EB_ErrorNone)

return svt_print_error(avctx, svt_ret, "Error initializing encoder handle");

ret = config_enc_params(&svt_env->enc_params, avctx);

svt_ret = svt_av1_enc_set_parameter(svt_enc->svt_handle, &svt_enc->enc_params);

svt_ret = svt_av1_enc_init(svt_enc->svt_handle); //初始化编码器

if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {

EbBufferHeaderType *headerPtr = NULL;

svt_ret = svt_av1_enc_stream_header(svt_enc->svt_handle, &headerPtr);

avctx->extradata_size = headerPtr->m_filled_len;

avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);

memcpy(avctx->extradata, headerPtr->p_buffer, avctx->extradata_size);

svt_ret = svt_av1_enc_stream_header_release(headerPtr);

}

svt_enc->frame = av_frame_alloc();

return alloc_buffer(&svt_enc->enc_params, svt_enc);

}

headerPtr->flags = 0;

headerPtr->p_app_private = NULL;

headerPtr->pts = frame->pts;

svt_av1_enc_send_picture(svt_enc->svt_handle, headerPtr);

}

static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame)

{ //AVFrame SVT-AV1输入结构send编码器

SvtContext *svt_enc = avctx->priv_data;

EbBufferHeaderType *headerPtr = svt_enc->in_buf;

int ret;

if (!frame) {

EbBufferHeaderType headerPtrLast;

if (svt_enc->eos_flag == EOS_SENT)

return 0;

headerPtrLast.n_alloc_len = 0;

headerPtrLast.n_filled_len = 0;

headerPtrLast.n_tick_count = 0;

headerPtrLast.p_app_private = NULL;

headerPtrLast.p_buffer = NULL;

headerPtrLast.flags = EB_BUFFERFLAG_EOS;

svt_av1_enc_send_picture(svt_enc->svt_handle, &headerPtrLast);

svt_enc->eos_flag = EOS_SENT;

return 0;

}

ret = read_in_data(&svt_enc->enc_params, frame, headerPtr);

if (ret < 0)

return ret;

headerPtr->flags = 0;

headerPtr->p_app_private = NULL;

headerPtr->pts = frame->pts;

svt_av1_enc_send_picture(svt_enc->svt_handle, headerPtr);

return 0;

}

static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt)

{

SvtContext *svt_enc = avctx->priv_data;

EbBufferHeaderType *headerPtr;

AVFrame *frame = svt_enc->frame;

EbErrorType svt_ret;

AVBufferRef *ref;

int ret = 0, pict_type;

if (svt_enc->eos_flag == EOS_RECEIVED)

return AVERROR_EOF;

ret = ff_encode_get_frame(avctx, frame); //从编码队列获取一个

if (ret < 0 && ret != AVERROR_EOF)

return ret;

if (ret == AVERROR_EOF)

frame = NULL;

ret = eb_send_frame(avctx, frame); //发送编码器里面

if (ret < 0)

return ret;

av_frame_unref(svt_enc->frame);

svt_ret = svt_av1_enc_get_packet(svt_enc->svt_handle, &headerPtr, svt_enc->eos_flag); //获取编码好视频

if (svt_ret == EB_NoErrorEmptyQueue)

return AVERROR(EAGAIN);

ref = get_output_ref(avctx, svt_enc, headerPtr->n_filled_len); //获取AVPacket buffer数据

if (!ref) {

av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n");

svt_av1_enc_release_out_buffer(&headerPtr);

return AVERROR(ENOMEM);

}

//构建一个新的AVPacket

pkt->buf = ref;

pkt->data = ref->data;

memcpy(pkt->data, headerPtr->p_buffer, headerPtr->n_filled_len);

memset(pkt->data + headerPtr->n_filled_len, 0, AV_INPUT_BUFFER_PADDING_SIZE); //数据存入AVPacket

pkt->size = headerPtr->n_filled_len;

pkt->pts = headerPtr->pts;

pkt->dts = headerPtr->dts;

switch (headerPtr->pic_type) {

case EB_AV1_KEY_PICTURE:

pkt->flags |= AV_PKT_FLAG_KEY;

// fall-through

case EB_AV1_INTRA_ONLY_PICTURE:

pict_type = AV_PICTURE_TYPE_I;

break;

case EB_AV1_INVALID_PICTURE:

pict_type = AV_PICTURE_TYPE_NONE;

break;

default:

pict_type = AV_PICTURE_TYPE_P;

break;

}

if (headerPtr->pic_type == EB_AV1_NON_REF_PICTURE)

pkt->flags |= AV_PKT_FLAG_DISPOSABLE;

if (headerPtr->flags & EB_BUFFERFLAG_EOS)

svt_enc->eos_flag = EOS_RECEIVED;

ff_side_data_set_encoder_stats(pkt, headerPtr->qp * FF_QP2LAMBDA, NULL, 0, pict_type);

svt_av1_enc_release_out_buffer(&headerPtr);

return 0;

}

static av_cold int eb_enc_close(AVCodecContext *avctx)

{ //关闭编码器

SvtContext *svt_enc = avctx->priv_data;

if (svt_enc->svt_handle) {

svt_av1_enc_deinit(svt_enc->svt_handle);

svt_av1_enc_deinit_handle(svt_enc->svt_handle);

}

if (svt_enc->in_buf) {

av_free(svt_enc->in_buf->p_buffer);

av_freep(&svt_enc->in_buf);

}

av_buffer_pool_uninit(&svt_enc->pool);

av_frame_free(&svt_enc->frame);

return 0;

}


http://www.niftyadmin.cn/n/5869439.html

相关文章

基数排序:独特的排序之道

在排序算法的大家族中&#xff0c;基数排序凭借其独特的排序思路和应用场景&#xff0c;占据着不可或缺的位置。今天&#xff0c;就让我们一同深入探索基数排序的奥秘。 一、基数排序的核心思想 基数排序是一种非比较型整数排序算法&#xff0c;它的核心在于按位排序。与基于…

8.Dashboard的导入导出

分享自己的Dashboard 1. 在Dashboard settings中选择 JSON Model 2. 导入 后续请参考第三篇导入光放Dashboard&#xff0c;相近

CSS 真的会阻塞文档解析吗?

在网页开发领域&#xff0c;一个常见的疑问是 CSS 是否会阻塞文档解析。理解这一问题对于优化网页性能、提升用户体验至关重要。要深入解答这个问题&#xff0c;需要从浏览器渲染网页的原理说起。 浏览器渲染网页的基本流程 浏览器在接收到 HTML 文档后&#xff0c;会依次进行…

Android 12系统源码_多屏幕(四)自由窗口模式

一、小窗模式 1.1 小窗功能的开启方式 开发者模式下开启小窗功能 adb 手动开启 adb shell settings put global enable_freeform_support 1 adb shell settings put global force_resizable_activities 11.2 源码配置 copy file # add for freedom PRODUCT_COPY_FILES …

直角三角堰计算公式

直角三角堰的计算公式通常用于确定流经直角三角形形状的堰的流量。河北瑾航科技遥测终端机 通过采集液位数据(模拟量、串口485/232)&#xff0c;计算得到瞬时流量&#xff0c;然后通过积分进行累计算出累积量&#xff1b;直角三角堰的流量计算公式为&#xff1a; 直角三角堰 计…

Windows程序设计28:MFC模态与非模态对话框

文章目录 前言一、创建模态对话框1.创建模态对话框模板2.绑定自定义对话框类3.创建模态对话框DoModal4.销毁模态对话框二、创建非模态对话框1.创建对话框模板2.绑定自定义对话框类3.创建非模态对话框Create、ShowWindow4.销毁非模态对话框5.销毁自身窗口指针总结前言 Windows程…

XTOM工业级蓝光三维扫描仪在笔记本电脑背板模具全尺寸检测中的高效精准应用

——某3C精密制造企业模具优化与质量管控案例 镁合金具有密度小、强度高、耐腐蚀性好等优点&#xff0c;成为笔记本电脑外壳主流材料。冲压模具作为批量生产笔记本电脑镁合金背板的核心工具&#xff0c;其精度直接决定了产品的尺寸一致性、结构可靠性与外观品质。微米级模具误…

MySQL数据库的基本命令

1.use mysql;切换***数据库 2.show databases&#xff1b;语句查看当前系统存在的数据库 其中的4个数据库都属于系统数据库&#xff0c; informain_schema:储存系统中一些数据库对象信息 mysql:主要 performance_schema: sys: 3.show tables;查看当前数据库中的表 4.sele…