SVT-AV1接入ffmpeg说明
一 编译集成
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;
}
//下面这个函数,把ffmpeg的AVFrame 转为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;
}
相关文章:
SVT-AV1接入ffmpeg说明
一 编译集成 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/loca…...
基于 C++ Qt 的 Fluent Design 组件库 QFluentWidgets
简介 QFluentWidgets 是一个基于 Qt 的 Fluent Designer 组件库,内置超过 150 个开箱即用的 Fluent Designer 组件,支持亮暗主题无缝切换和自定义主题色。 编译示例 以 Qt5 为例(Qt6 也支持),将 libQFluentWidgets.d…...
OpenCV(6):图像边缘检测
图像边缘检测是计算机视觉和图像处理中的一项基本任务,它用于识别图像中亮度变化明显的区域,这些区域通常对应于物体的边界。是 OpenCV 中常用的边缘检测函数及其说明: 函数算法说明适用场景cv2.Canny()Canny 边缘检测多阶段算法,检测效果较…...
多模态人物视频驱动技术回顾与业务应用
一种新的商品表现形态,内容几乎存在于手淘用户动线全流程,例如信息流种草内容、搜索消费决策内容、详情页种草内容等。通过低成本、高时效的AIGC内容生成能力,能够从供给端缓解内容生产成本高的问题,通过源源不断的低成本供给倒推…...
星海智算+ DeepSeek-R1:技术突破与行业应用的协同革新
一、前言 在当今数字化时代,人工智能(AI)正以前所未有的速度改变着商业和社会的方方面面。最近爆火的DeepSeek-R1系列模型,以其强大的推理能力和在中文的推理、代码和数学任务高效的性能得到了全球用户的热议。该模型不仅在多项专…...
选择排序:简单高效的选择
大家好,今天我们来聊聊选择排序(Selection Sort)算法。这是一个非常简单的排序算法,适合用来学习排序的基本思路和操作。选择排序在许多排序算法中以其直观和易于实现的特点著称,虽然它的效率不如其他高效算法…...
考研/保研复试英语问答题库(华工建院)
华南理工大学建筑学院保研/考研 英语复试题库,由华工保研er和学硕笔试第一同学一起整理,覆盖面广,助力考研/保研上岸!需要👇载可到文章末尾见小🍠。 以下是主要内容: Part0 复试英语的方法论 Pa…...
ARM Cortex-M处理器中的MSP和PSP
在ARM Cortex-M系列处理器中,MSP(主堆栈指针)和PSP(进程堆栈指针)是两种不同的堆栈指针,主要用于实现堆栈隔离和提升系统可靠性。以下是它们的核心区别和应用场景: 1. 基本定义 MSP(…...
《Keras 3 使用 NeRF 进行 3D 体积渲染》:此文为AI自动翻译
《Keras 3 使用 NeRF 进行 3D 体积渲染》 作者: Aritra Roy Gosthipaty, Ritwik Raha 创建日期: 2021/08/09 最后修改时间: 2023/11/13 描述: 体积渲染的最小实现,如 NeRF 中所示。 (i) 此示例使用 Keras 3 在 Colab 中查看 GitHub 源 介绍 在此示例中,我们展示了…...
Pytorch实现之浑浊水下图像增强
简介 简介:这也是一篇非常适合GAN小白们上手的架构文章!提出了一种基于GAN的水下图像增强网络。这种网络与其他架构类似,生成器是卷积+激活函数+归一化+残差结构的组成,鉴别器是卷积+激活函数+归一化以及全连接层。损失函数是常用的均方误差、感知损失和对抗损失三部分。 …...
【redis】数据类型之Bitfields
Redis的Bitfields(位域)与Bitmaps一样,在Redis中并不是一种独立的数据类型,而是一种基于字符串的数据结构,用于处理位级别的操作。允许用户将一个Redis字符串视作由一系列二进制位组成的数组,并对这些位进行…...
Python入门 — 类
面向对象编程中,编写表示现实世界中的事物和情景的类(class),并基于这些类来创建对象(object)。根据类来创建对象称为实例化,这样就可以使用类的实例(instance) 一、创建…...
R-INLA实现绿地与狐狸寄生虫数据空间建模:含BYM、SPDE模型及PC先验应用可视化...
全文链接:https://tecdat.cn/?p40720 本论文旨在为对空间建模感兴趣的研究人员客户提供使用R-INLA进行空间数据建模的基础教程。通过对区域数据和地统计(标记点)数据的分析,介绍了如何拟合简单模型、构建和运行更复杂的空间模型&…...
Linux云计算SRE-第十五周
1.总结Dockerfile的指令和Docker的网络模式 一、Dockerfile 核心指令详解 1、基础构建指令 指令 功能描述 关键特性 FROM 指定基础镜像(必须为首条指令) - 支持多阶段构建:FROM node AS builder - scratch 表示空镜像 RUN 在镜像构建…...
2014年下半年试题一:论软件需求管理
论文库链接:系统架构设计师论文 论文题目 软件需求管理是一个对系统需求变更了解和控制的过程。需求管理过程与需求开发过程相互关联,初始需求导出的同时就要形成需求管理规划,一旦启动了软件开发过程需求管理活动就紧密相伴。 需求管理过程中…...
podman加速器配置,harbor镜像仓库部署
Docker加速器 registries加速器 [rootlocalhost ~]# cat /etc/redhat-release CentOS Stream release 8 [rootlocalhost ~]# cd /etc/containers/ [rootlocalhost containers]# ls certs.d policy.json registries.conf.d storage.conf oci registries.conf re…...
信息学奥赛一本通 1522:网络 | OpenJudge 百练 1144:Network
【题目链接】 ybt 1522:网络 OpenJudge 百练 1144:Network 【题目考点】 1. 图论:割点 【解题思路】 每个交换机是一个顶点,如果两地点之间有电话线连接,那么两顶点之间有一条无向边,该图是无向图。 初始时任何地…...
本地部署DeepSeek的硬件配置建议
本地部署DeepSeek的硬件配置需求因模型参数规模和部署工具不同而有所差异,以下是综合多个来源的详细要求: 1. 基础配置(适用于7B参数模型) 内存:最低8GB,推荐16GB及以上;若使用Ollama工具&…...
Redis面试题----Redis 的持久化机制是什么?各自的优缺点?
Redis 提供了两种主要的持久化机制,分别是 RDB(Redis Database)和 AOF(Append Only File),下面将详细介绍它们的原理、优缺点。 RDB(Redis Database) 原理 RDB 持久化是将 Redis 在某个时间点上的数据集快照以二进制文件的形式保存到磁盘上。可以通过手动执行 SAVE …...
C#实现本地AI聊天功能(Deepseek R1及其他模型)。
前言 1、C#实现本地AI聊天功能 WPFOllamaSharpe实现本地聊天功能,可以选择使用Deepseek 及其他模型。 2、此程序默认你已经安装好了Ollama。 在运行前需要线安装好Ollama,如何安装请自行搜索 Ollama下载地址: https://ollama.org.cn Ollama模型下载地址…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...
