FFmpeg处理流程

news2025/3/14 6:06:36

结构体

AVFormatContext

作用:管理媒体文件的封装格式上下文,存储文件格式、流信息、I/O 操作等元数据。
关键字段

AVInputFormat *iformat;   // 输入格式(如MP4、FLV)
AVStream **streams;       // 音视频流数组
int nb_streams;           // 流数量
int64_t duration;         // 总时长(微秒)

初始化:avformat_alloc_context()avformat_open_input()

AVStream

作用:表示单个音视频流,包含编解码参数和时间基准。
关键字段:

AVCodecParameters *codecpar;  // 编解码参数(如分辨率、采样率)
AVRational time_base;         // 时间基(如1/30表示30fps)

初始化:avformat_new_stream()

AVCodec

AVCodecContext

作用:编解码器上下文,存储编解码参数(如码率、帧率、像素格式)。
关键字段:

enum AVCodecID codec_id;      // 编解码器ID(如H.264、AAC)
int width, height;            // 视频分辨率
enum AVPixelFormat pix_fmt;   // 像素格式(如YUV420P)
AVRational time_base;         // 编码器时间基

初始化:avcodec_alloc_context3(),avcodec_parameters_to_context()

AVPacket

作用:存储编码后的压缩数据(如H.264数据包)。
关键字段:

uint8_t *data;       // 压缩数据指针
int size;            // 数据大小
int64_t pts, dts;    // 显示和解码时间戳

初始化:av_packet_alloc()av_packet_unref()

AVFrame

作用:存储解码后的原始数据(如YUV像素数据或PCM音频样本)。
关键字段:

uint8_t *data[AV_NUM_DATA_POINTERS]; // 数据指针(如Y、U、V分量)
int linesize[AV_NUM_DATA_POINTERS];  // 每行字节数
int width, height;                    // 视频分辨率

初始化:av_frame_alloc()av_frame_free()

SwsContext

作用:图像格式转换上下文(如YUV转RGB)。
初始化:sws_getContext(),销毁:sws_freeContext()

SwrContext

作用:音频重采样上下文(如48kHz转44.1kHz)。
初始化:swr_alloc_set_opts(),销毁:swr_free()

API

avformat_open_input
avformat_find_stream_info
av_find_best_stream
avcodec_alloc_context3
avcodec_parameters_to_context
avcodec_open2
avcodec_find_encoder
av_opt_set_int

sws_getContext
avformat_alloc_output_context2
avformat_new_stream
avcodec_parameters_from_context
avio_open
avformat_write_header
av_frame_alloc
av_frame_get_buffer
av_packet_alloc

av_read_frame
avcodec_send_packet
avcodec_receive_frame
sws_scale
av_rescale_q
avcodec_send_frame
avcodec_receive_packet
av_packet_rescale_ts
av_interleaved_write_frame
av_packet_unref
av_write_trailer

例子

#include <iostream>
#include <memory>

// 使用 RAII 管理指针(可选,但推荐)
template<typename T, void(*Deleter)(T*)>
struct FFmpegResource {
    T* ptr = nullptr;
    FFmpegResource(T* p = nullptr) : ptr(p) {}
    ~FFmpegResource() { if (ptr) Deleter(ptr); }
};

using AVFormatContextPtr = FFmpegResource<AVFormatContext, avformat_close_input>;
using AVCodecContextPtr = FFmpegResource<AVCodecContext, avcodec_free_context>;
using SwsContextPtr = FFmpegResource<SwsContext, sws_freeContext>;
using AVFramePtr = FFmpegResource<AVFrame, av_frame_free>;
using AVPacketPtr = FFmpegResource<AVPacket, av_packet_free>;

int main() {
    AVFormatContext *srcCtx = nullptr;
    AVCodecContext *srcDecCtx = nullptr, *encCtx = nullptr;
    SwsContext *swsCtx = nullptr;
    AVFrame *decFrame = nullptr, *encFrame = nullptr;
    AVPacket *pkt = nullptr;
    AVFormatContext *outputCtx = nullptr;
    int ret = 0;

    // 错误处理标签
    #define CHECK_ERROR(cond, msg, cleanup_label) \
        if ((cond)) { \
            std::cerr << (msg) << ": " << av_err2str(ret) << std::endl; \
            goto cleanup_label; \
        }

    // ============ 打开输入文件 ============
    ret = avformat_open_input(&srcCtx, srcPath.toStdString().c_str(), nullptr, nullptr);
    CHECK_ERROR(ret < 0, "打开视频文件失败", cleanup);

    ret = avformat_find_stream_info(srcCtx, nullptr);
    CHECK_ERROR(ret < 0, "获取视频流信息失败", cleanup);

    // ============ 初始化视频解码器 ============
    const AVCodec *srcDec = nullptr;
    int streamIndex = av_find_best_stream(srcCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &srcDec, 0);
    CHECK_ERROR(streamIndex < 0, "查找视频流失败", cleanup);

    srcDecCtx = avcodec_alloc_context3(srcDec);
    CHECK_ERROR(!srcDecCtx, "分配解码器上下文失败", cleanup);

    ret = avcodec_parameters_to_context(srcDecCtx, srcCtx->streams[streamIndex]->codecpar);
    CHECK_ERROR(ret < 0, "拷贝解码器参数失败", cleanup);

    ret = avcodec_open2(srcDecCtx, srcDec, nullptr);
    CHECK_ERROR(ret < 0, "打开解码器失败", cleanup);

    // ============ 初始化视频编码器 ============
    const AVCodec *srcEnc = avcodec_find_encoder(srcCtx->streams[streamIndex]->codecpar->codec_id);
    CHECK_ERROR(!srcEnc, "查找编码器失败", cleanup);

    encCtx = avcodec_alloc_context3(srcEnc);
    CHECK_ERROR(!encCtx, "分配编码器上下文失败", cleanup);

    // 配置编码参数
    encCtx->width = width;
    encCtx->height = height;
    encCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    encCtx->time_base = {1, 30};
    encCtx->gop_size = 12;
//    encCtx->bit_rate = 4000000; 不设置码率

    encCtx->profile = FF_PROFILE_H264_HIGH;
    encCtx->level = 40;
    encCtx->max_b_frames = 2;
    encCtx->color_range = AVCOL_RANGE_MPEG; // 颜色范围(tv)
    encCtx->color_primaries = AVCOL_PRI_BT709; // 颜色标准
    encCtx->color_trc = AVCOL_TRC_BT709;  // 颜色传输特性
    encCtx->colorspace = AVCOL_SPC_BT709; // 颜色空间

    // 设置CRF模式与参数调整
    encCtx->flags |= AV_CODEC_FLAG_QSCALE;// 启用量化参数控制
    av_opt_set_int(encCtx->priv_data, "crf", 18, AV_OPT_SEARCH_CHILDREN);// 0-51,18为视觉无损

    av_opt_set(encCtx->priv_data, "preset", "veryslow", AV_OPT_SEARCH_CHILDREN);  // 牺牲时间换取质量
//    av_opt_set(encCtx->priv_data, "tune", "film", AV_OPT_SEARCH_CHILDREN);  // 电影类用film,动画用animation


    ret = avcodec_open2(encCtx, srcEnc, nullptr);
    CHECK_ERROR(ret < 0, "打开编码器失败", cleanup);

    // ============ 创建缩放上下文 ============
    swsCtx = sws_getContext(/* 参数保持原逻辑 */);
    CHECK_ERROR(!swsCtx, "创建缩放上下文失败", cleanup);

    // ============ 准备输出文件 ============
    ret = avformat_alloc_output_context2(&outputCtx, nullptr, nullptr, destPath.toStdString().c_str());
    CHECK_ERROR(ret < 0, "创建输出上下文失败", cleanup);

    AVStream *outStream = avformat_new_stream(outputCtx, nullptr);
    CHECK_ERROR(!outStream, "创建输出流失败", cleanup);

    ret = avcodec_parameters_from_context(outStream->codecpar, encCtx);
    CHECK_ERROR(ret < 0, "拷贝编码器参数到输出流失败", cleanup);

    // 显式设置输出流时间基与编码器一致 
    outStream->time_base = encCtx->time_base;

    if (!(outputCtx->oformat->flags & AVFMT_NOFILE)) {
        ret = avio_open(&outputCtx->pb, destPath.toStdString().c_str(), AVIO_FLAG_WRITE);
        CHECK_ERROR(ret < 0, "打开输出文件失败", cleanup);
    }

    ret = avformat_write_header(outputCtx, nullptr);
    CHECK_ERROR(ret < 0, "写入文件头失败", cleanup);

    // ============ 帧处理循环 ============
    decFrame = av_frame_alloc();
    encFrame = av_frame_alloc();
    pkt = av_packet_alloc();
    CHECK_ERROR(!decFrame || !encFrame || !pkt, "分配帧/包失败", cleanup);

    while (av_read_frame(srcCtx, pkt) >= 0) {
        if (pkt->stream_index != streamIndex) {
            av_packet_unref(pkt);
            continue;
        }

         // 解码
        if ((ret = avcodec_send_packet(srcDecCtx, pkt)) < 0) {
            cout << "读取包失败: " << av_err2str(ret) << endl;
        }
        while (ret >= 0) {
            ret = avcodec_receive_frame(srcDecCtx, decFrame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                cout << "读取帧失败: " << av_err2str(ret) << endl;
            }
            cout << "解码帧 pts: " << decFrame->pts << endl;

            // 缩放
            sws_scale(swsCtx, decFrame->data, decFrame->linesize,
                      0, srcDecCtx->height, encFrame->data, encFrame->linesize);
            encFrame->pts = av_rescale_q(decFrame->pts, srcCtx->streams[streamIndex]->time_base, encCtx->time_base);

            // 编码
            AVPacket *encPkt = av_packet_alloc();
            if ((ret = avcodec_send_frame(encCtx, encFrame)) < 0) {
                cout << "发送帧到编码器失败: " << av_err2str(ret) << endl;
            }
            while (ret >= 0) {
                ret = avcodec_receive_packet(encCtx, encPkt);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    cout << "编码器输出包失败: " << av_err2str(ret) << endl;
                }

                // 写入输出文件
                av_packet_rescale_ts(encPkt, encCtx->time_base, outStream->time_base);
                cout << "编码: " << encPkt->pts << endl;
                av_interleaved_write_frame(outputCtx, encPkt);
                av_packet_unref(encPkt);
            }

        }
    }

    // ============ 刷新编码器缓冲区  ============
    avcodec_send_frame(encCtx, nullptr); // 发送空帧刷新
    while (true) {
        AVPacket encPkt;
        av_init_packet(&encPkt);
        ret = avcodec_receive_packet(encCtx, &encPkt);
        if (ret == AVERROR_EOF || ret < 0) break;
        av_packet_rescale_ts(&encPkt, encCtx->time_base, outStream->time_base);
        av_interleaved_write_frame(outputCtx, &encPkt);
        av_packet_unref(&encPkt);
    }

    // ============ 写入文件尾 ============
    ret = av_write_trailer(outputCtx);
    CHECK_ERROR(ret < 0, "写入文件尾失败", cleanup);

// ============ 资源释放 ============
cleanup:
    avformat_close_input(&srcCtx);
    avcodec_free_context(&srcDecCtx);
    avcodec_free_context(&encCtx);
    sws_freeContext(swsCtx);
    av_frame_free(&decFrame);
    av_frame_free(&encFrame);
    av_packet_free(&pkt);
    if (outputCtx && !(outputCtx->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&outputCtx->pb);
    }
    avformat_free_context(outputCtx);
    return ret;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314685.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringMVC (一)基础

目录 SpringMVC 一 简单使用 1 新建模块选择指定参数 2 创建实现类 3 将项目启动 4 运行结果&#xff1a;在浏览器当中响应执行 二 RequestMapping 三 请求限定 SpringMVC SpringMVC是Spring的web模块&#xff0c;用来开发Web应用&#xff0c;SpringMVC应用最终作为B/…

windows第十二章 MFC控件常用消息

文章目录 控件反射消息机制文本框控件EN_CHANGE消息EN_UPDATE消息EN_SETFOCUS消息EN_KILLFOCUS消息EN_MAXTEXT消息EN_ERRSPACE消息EN_HSCROLL消息 按钮控件BN_CLICKED消息BN_DOUBLECLICKED消息BN_SETFOCUS消息BN_KILLFOCUS消息 单选按钮BN_CLICKED 消息 复选框BN_CLICKEDBN_DOU…

基于C语言的简单HTTP Web服务器实现

1. 概述 本案例使用C语言实现了一个简单的HTTP服务器&#xff0c;能够处理客户端的GET请求&#xff0c;并返回静态文件&#xff08;如HTML、图片等&#xff09;。在此案例中案例&#xff0c;我们主要使用的知识点有&#xff1a; Socket编程&#xff1a;基于TCP协议的Socket通信…

ZYNQ初识13(zynq_7020)hdmi和串口板载功能的验证

&#xff08;1&#xff09;另&#xff1a;首先需要确认供电模块&#xff0c;电压转换模块没有问题&#xff0c;测量后上电防止出现短路。通过vivado下载bit流文件检测JTAG下载口是否正常&#xff0c;如可正常检测&#xff0c;才可进行下一步验证。 &#xff08;2&#xff09;以…

ollama下载的DeepSeek的模型(Model)文件在哪里?(C盘下)

目录 一、下载大模型&#xff08;DeepSeek&#xff09; 2. 安装 Ollama 3. 检查安装是否成功 二、拉取大模型&#xff08;DeepSeek&#xff09; 1. 打开命令行 2. 下载模型 3. 测试下载 4. 等待下载完成 三.模型存放路径 这个位置&#xff01;&#xff01; 在人工智能…

docker的anythingllm和open-webui压缩包分享(国内镜像拉取,百度云压缩包分享)

文章目录 前言第一部分&#xff1a;镜像获取&#x1f680; 方式一&#xff1a;切换国内下载镜像✅1. 下载anythingllm✅ 2. 下载open-webui &#x1f680;方式二&#xff1a;下载我分享的百度云✅ anythingllm压缩包百度云链接❎ open-webui压缩包 第二部分&#xff1a;下载之后…

父组件中循环生成多个子组件时,有且只有最后一个子组件的watch对象生效问题及解决办法

提示&#xff1a;父组件中循环生成多个子组件时&#xff0c;有且只有最后一个子组件的watch对象生效问题及解决办法 文章目录 [TOC](文章目录) 前言一、问题二、解决方法——使用function函数代替箭头函数()>{}总结 前言 ‌‌‌‌‌问题&#xff1a;子组件用that解决watch无…

求递增子序列LIS的两种方法

文章目录 前言一、普通动态规划&#xff08;DP&#xff09;求解LIS1.DP思路2.DP的状态定义与转移方程3.DP的时间与空间复杂度4.DP代码实现5.DP的图文示例 二、贪心 二分查找求解LIS1.思路分析2.贪心 二分的时间与空间复杂度 三. 模板题讲解1.洛谷B3637 最长上升子序列1.dp写法…

【Linux篇】进程状态(僵尸进程,孤儿进程),优先级与调度机制

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 文章目录 1. 前文铺垫理解内核链表 2. 进程状态2.1 进程状态查看2.2 僵尸进程2.3 僵尸进程危害2.4 孤儿…

AI应用加速落地丨MaxKB正在被政府、公共事业、教育和医疗行业用户广泛采纳

2025年2月至3月上旬&#xff0c;伴随着各个行业接入并使用DeepSeek&#xff0c;MaxKB开源知识库问答系统正在被越来越多的行业用户所采纳&#xff0c;是人工智能行业落地的强应用。目前&#xff0c;MaxKB在政府、公共事业、教育和医疗四大行业已经拥有了众多典型案例&#xff0…

2024年第十五届蓝桥杯软件C/C++大学A组——五子棋对弈

蓝桥杯原题&#xff1a; 题目描述&#xff1a; “在五子棋的对弈中&#xff0c;友谊的小船说翻就翻&#xff1f; ” 不&#xff01;对小蓝和小桥来说&#xff0c;五子棋不仅是棋盘上的较量&#xff0c;更是心与心之间的沟通。这两位挚友秉承着 “ 友谊第一&#xff0c;比赛第二…

复试难度解析,西电先进材料与纳米科技学院学院考研录取情况

01、先进材料与纳米科技学院各个方向 02、24先进材料与纳米科技学院近三年复试分数线对比 PS&#xff1a;材料院24年院线学硕方向降低10分&#xff0c;专硕上涨15分&#xff1b;材料院在分数线相对于其他211、985院校对比来看&#xff0c;依然分数偏低&#xff0c;推荐大家关注…

Deepseek Chatgpt Kimi 推荐的深度学习书单

朋友让推荐一些深度学习的书&#xff0c;让 Deepseek、Chatgpt、Kimi 分别生成了一份书单并做了对比&#xff0c;记录一下以备以后用到。 Chatgpt 推荐的深度学习书 1. chatgpt 推荐的书目截图 1.2 Chatgpt 推荐的深度学习书目文字版 如果你想学习 Deep Learning&#xff0…

高频面试题(含笔试高频算法整理)基本总结回顾25

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言&#xff0c…

mac安装mysql之后报错zsh: command not found: mysql !

在Mac上安装MySQL后&#xff0c;如果终端中找不到mysql命令&#xff0c;通常是 因为MySQL的命令行工具&#xff08;如mysql客户端&#xff09;没有被正确地添加到你的环境变量中。 检查 MySQL 是否已安装 ps -ef|grep mysql查看到路径在 /usr/local/mysql/bin 查看 .bash_pro…

蓝桥杯备考:set容器用法(lower_bound)---营业额统计

如图所示&#xff0c;这道题的暴力解法就是枚举每天的营业额&#xff0c;让该营业额和前面的天的营业额依次相减取最小值这样的话我们的时间复杂度就是N平方&#xff0c;我们是很有可能超时的 所以我们选择用set容器的二分查找功能 我们每次遍历到一个数的时候&#xff0c;前…

VSCode集成C语言开发环境

下载MinGW https://sourceforge.net/projects/mingw/ 点击download按钮下载exe文件到本地 点击exe文件安装 选择基础包和c编译版 vscode安装部分跳过 安装code runner和c/c插件 **(1) 创建 C 文件** 新建一个测试文件&#xff08;例如 hello.c&#xff09;&#xf…

Python----数据可视化(pyecharts二:绘图一:条形图,直方图,折线图,散点图,箱图,饼图,热力图)

1、条形图 from pyecharts.charts import Bar from pyecharts.faker import Faker from pyecharts import options as opts # 绘制柱状图 bar (Bar() # 创建柱状图.add_yaxis("商家A", Faker.values(),colorFaker.rand_color()) # 添加数据.add_yaxis("商家B&…

Training-free Neural Architecture Search for RNNs and Transformers(预览版本)

摘要 神经架构搜索 (NAS) 允许自动创建新的有效神经网络架构&#xff0c;为手动设计复杂架构的繁琐过程提供了替代方案。然而&#xff0c;传统的 NAS 算法速度慢&#xff0c;需要大量的计算能力。最近的研究调查了图像分类架构的无训练 NAS 指标&#xff0c;大大加快了搜索算…

计算机考研C语言

C语言程序设计从入门到精通【2025完整版】考研复试 嵌入式 计算机二级 软考 专升本也适用_哔哩哔哩_bilibili 1、第一个C程序 helloC #include <stdio.h>int main(){printf("hehe");return 0;}每个C语言程序不管有多少行代码&#xff0c;都是从main函数开始执…