如何在 C++ 中将调色板生成器和调色板使用过滤器与 FFmpeg 一起使用?

2024-04-11

我想在 QT 项目中创建一个动画 gif。 当我直接将 QImage 缩放到 AV_PIX_FMT_RGB8 时,输出看起来很糟糕,带有闪烁的伪影,而当我缩放到 AV_PIX_FMT_YUV420P 之间时,输出会抖动,这看起来并没有好多少。

我发现 ffmpeg 能够使用名为 Palettegen 的过滤器生成调色板文件,然后使用该调色板将电影转换为 gif。

是否有任何示例 c++ 文件可供我用于我的项目,或者是否有人知道谁在代码中使用这些过滤器?


过滤器可以如下描述

format=pix_fmts=rgb32,fps=10,scale=320:240:flags=lanczos,split [o1] [o2];[o1] palettegen [p]; [o2] fifo [o3];[o3] [p] paletteuse

你可以做一些fps or scale使用上面的过滤器进行自定义。

然后我们应该在 ffmpeg 中应用这个过滤图。

一个重要的注意事项是,作为过滤器palettegen需要整个流,我们需要将整个流添加到缓冲区源,并将流的末尾添加到缓冲区源,然后我们可以从缓冲区接收器获取帧并混合到输出 gif 文件。

这是一个完整的示例,在测试中更改微定义并确保使用最新版本的 ffmpeg。

#define INPUT_PATH "/Users/gif/Downloads/VUE_1503566813005.mp4"
#define OUTPUT_PATH "/Users/gif/Downloads/VUE.gif"

视频2gif.cpp

#include <iostream>

#ifdef __cplusplus
extern "C" {
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavutil/opt.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}
#endif

#define INPUT_PATH "/Users/gif/Downloads/VUE_1503566813005.mp4"
#define OUTPUT_PATH "/Users/gif/Downloads/VUE.gif"

const char *filter_descr = "format=pix_fmts=rgb32,fps=10,scale=320:240:flags=lanczos,split [o1] [o2];[o1] palettegen [p]; [o2] fifo [o3];[o3] [p] paletteuse";

static AVFormatContext* ofmt_ctx;
static AVCodecContext* o_codec_ctx;

static AVFilterGraph* filter_graph;
static AVFilterContext* buffersrc_ctx;
static AVFilterContext* buffersink_ctx;

static int init_filters(const char* filter_desc,
                        AVFormatContext* ifmt_ctx,
                        int stream_index,
                        AVCodecContext* dec_ctx)
{
    char args[512];
    int ret = 0;
    AVFilter* buffersrc = avfilter_get_by_name("buffer");
    AVFilter* buffersink = avfilter_get_by_name("buffersink");
    AVFilterInOut* inputs = avfilter_inout_alloc();
    AVFilterInOut* outputs = avfilter_inout_alloc();
    AVRational time_base = ifmt_ctx->streams[stream_index]->time_base;

    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE };

    filter_graph = avfilter_graph_alloc();
    if (!outputs || !inputs || !filter_graph) {
        ret = AVERROR(ENOMEM);
        return ret;
    }

    snprintf(args, sizeof(args),
             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
             dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
             time_base.num, time_base.den,
             dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);

    ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, nullptr, filter_graph);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "Cannot create buffer source\n");
        return ret;
    }

    ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", nullptr, nullptr, filter_graph);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "Cannot create buffer sink\n");
        return ret;
    }

    av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "can not set output pixel format\n");
        return ret;
    }

    outputs->name = av_strdup("in");
    outputs->filter_ctx = buffersrc_ctx;
    outputs->pad_idx = 0;
    outputs->next = nullptr;

    inputs->name = av_strdup("out");
    inputs->filter_ctx = buffersink_ctx;
    inputs->pad_idx = 0;
    inputs->next = nullptr;

    if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_desc, &inputs, &outputs, nullptr)) < 0) {
        av_log(nullptr, AV_LOG_ERROR, "parse filter graph error\n");
        return ret;
    }

    if ((ret = avfilter_graph_config(filter_graph, nullptr)) < 0) {
        av_log(nullptr, AV_LOG_ERROR, "config graph error\n");
    }

    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);

    return 0;
}

static int init_muxer()
{
    int ret = 0;
    AVOutputFormat* o_fmt = av_guess_format("gif", OUTPUT_PATH, "video/gif");
    ret = avformat_alloc_output_context2(&ofmt_ctx, o_fmt, "gif", OUTPUT_PATH);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "%s allocate output format\n", av_err2str(ret));
        return -1;
    }

    AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_GIF);
    if (!codec) {
        return -1;
    }

    AVStream* stream = avformat_new_stream(ofmt_ctx, codec);

    AVCodecParameters* codec_paramters = stream->codecpar;
    codec_paramters->codec_tag = 0;
    codec_paramters->codec_id = codec->id;
    codec_paramters->codec_type = AVMEDIA_TYPE_VIDEO;
    codec_paramters->width = 320;
    codec_paramters->height = 240;
    codec_paramters->format = AV_PIX_FMT_PAL8;

    o_codec_ctx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(o_codec_ctx, codec_paramters);

    o_codec_ctx->time_base = { 1, 10 };

    if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
        o_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

    ret = avcodec_open2(o_codec_ctx, codec, NULL);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "%s open output codec\n", av_err2str(ret));
        return ret;
    }

    ret = avio_open(&ofmt_ctx->pb, OUTPUT_PATH, AVIO_FLAG_WRITE);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "%s avio open error\n", av_err2str(ret));
        return ret;
    }

    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        av_log(nullptr, AV_LOG_ERROR, "%s write header\n", av_err2str(ret));
        return ret;
    }

    av_dump_format(ofmt_ctx, -1, OUTPUT_PATH, 1);

    return 0;
}

static void destroy_muxer()
{
    avformat_free_context(ofmt_ctx);
    avcodec_close(o_codec_ctx);
    avcodec_free_context(&o_codec_ctx);
}

static void destroy_filter()
{
    avfilter_free(buffersrc_ctx);
    avfilter_free(buffersink_ctx);
    avfilter_graph_free(&filter_graph);
}

static void muxing_one_frame(AVFrame* frame)
{
    int ret = avcodec_send_frame(o_codec_ctx, frame);
    AVPacket *pkt = av_packet_alloc();
    av_init_packet(pkt);

    while (ret >= 0) {
        ret = avcodec_receive_packet(o_codec_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            break;
        }

        av_write_frame(ofmt_ctx, pkt);
    }

    av_packet_unref(pkt);
}

int main(int argc, const char * argv[]) {
    av_register_all();
    avcodec_register_all();
    avfilter_register_all();

    int ret = 0;
    AVFormatContext* fmt_ctx = avformat_alloc_context();
    AVCodecContext* codec_ctx = nullptr;
    AVCodec* codec = nullptr;
    int video_index = -1;

    AVPacket *pkt = av_packet_alloc();
    av_init_packet(pkt);
    pkt->size = 0;
    pkt->data = nullptr;

    ret = avformat_open_input(&fmt_ctx, INPUT_PATH, NULL, NULL);
    if (ret != 0) {
        std::cerr << "error open input" << std::endl;
        goto die;
    }

    avformat_find_stream_info(fmt_ctx, NULL);
    av_dump_format(fmt_ctx, -1, INPUT_PATH, 0);

    ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
    if (ret == AVERROR_STREAM_NOT_FOUND) {
        std::cerr << "error no video stream found" << std::endl;
        goto die;
    }

    for (unsigned i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_index = i;

            if (ret == AVERROR_DECODER_NOT_FOUND) {
                codec = avcodec_find_decoder(fmt_ctx->streams[i]->codecpar->codec_id);
            }

            break;
        }
    }

    if (!codec) {
        std::cerr << "could not find the decoder" << std::endl;
        goto die;
    }

    codec_ctx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_index]->codecpar);
    ret = avcodec_open2(codec_ctx, codec, NULL);
    if (ret < 0) {
        std::cerr << "open codec error" << std::endl;
        goto die;
    }

    if (init_muxer() < 0) {
        av_log(nullptr, AV_LOG_ERROR, "could not init muxer\n");
        goto die;
    }

    if ((ret = init_filters(filter_descr, fmt_ctx, video_index, codec_ctx)) < 0) {
        av_log(nullptr, AV_LOG_ERROR, "could not init filters %s\n", av_err2str(ret));
        goto die;
    }

    // it's time to decode
    while (av_read_frame(fmt_ctx, pkt) == 0) {
        if (pkt->stream_index == video_index) {

            ret = avcodec_send_packet(codec_ctx, pkt);

            while (ret >= 0) {
                AVFrame* frame = av_frame_alloc();
                ret = avcodec_receive_frame(codec_ctx, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                }

                if (ret >= 0) {
                    // processing one frame
                    frame->pts = frame->best_effort_timestamp;

                    // palettegen need a whole stream, just add frame to buffer and don't get frame
                    ret = av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
                    if (ret < 0) {
                        av_log(nullptr, AV_LOG_ERROR, "error add frame to buffer source %s\n", av_err2str(ret));
                    }
                }
                av_frame_free(&frame);
            }
        }
    }

    // end of buffer
    if ((ret = av_buffersrc_add_frame_flags(buffersrc_ctx, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF)) >= 0) {
        do {
            AVFrame* filter_frame = av_frame_alloc();
            ret = av_buffersink_get_frame(buffersink_ctx, filter_frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                // av_log(nullptr, AV_LOG_ERROR, "error get frame from buffer sink %s\n", av_err2str(ret));
                av_frame_unref(filter_frame);
                break;
            }

            // write the filter frame to output file
            muxing_one_frame(filter_frame);
            av_log(nullptr, AV_LOG_INFO, "muxing one frame\n");

            av_frame_unref(filter_frame);
        } while (ret >= 0);
    } else {
        av_log(nullptr, AV_LOG_ERROR, "error add frame to buffer source %s\n", av_err2str(ret));
    }

    av_packet_free(&pkt);

    av_write_trailer(ofmt_ctx);
    destroy_muxer();
    destroy_filter();
die:
    avformat_free_context(fmt_ctx);
    avcodec_close(codec_ctx);
    avcodec_free_context(&codec_ctx);

    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 C++ 中将调色板生成器和调色板使用过滤器与 FFmpeg 一起使用? 的相关文章

  • 无法使用已与其底层 RCW 分离的 COM 对象。在 oledb 中

    我收到此错误 但我不知道我做错了什么 下面的代码在backrgroundworker中 将异常详细信息复制到剪贴板 System Runtime InteropServices InvalidComObjectException 未处理 通
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • C++ 子字符串返回错误结果

    我有这个字符串 std string date 20121020 我正在做 std cout lt lt Date lt lt date lt lt n std cout lt lt Year lt lt date substr 0 4 l
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 如何返回 json 结果并将 unicode 字符转义为 \u1234

    我正在实现一个返回 json 结果的方法 例如 public JsonResult MethodName Guid key var result ApiHelper GetData key Data is stored in db as v
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 如何使我的表单标题栏遵循 Windows 深色主题?

    我已经下载了Windows 10更新包括黑暗主题 文件资源管理器等都是深色主题 但是当我创建自己的 C 表单应用程序时 标题栏是亮白色的 如何使我自己的桌面应用程序遵循我在 Windows 中设置的深色主题 你需要调用DwmSetWindo
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • C++ fmt 库,仅使用格式说明符格式化单个参数

    使用 C fmt 库 并给定一个裸格式说明符 有没有办法使用它来格式化单个参数 example std string str magic format 2f 1 23 current method template
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif

随机推荐

  • 如何确定 akka 中生成的 actor 数量?

    我最近开始研究 Akka 2 0 框架 并且能够运行一些代码 生成执行简单 Oracle 数据库调用 执行简单计算等的 Actor 但是在生产中什么也没有 我想知道的是 是否有一般的经验法则或最佳实践来确定为某些类型的任务生成多少个参与者
  • 无法在同一页面设置PHP cookie

    我在同一页面上设置 cookie 时遇到问题 我在我的网站上使用了 cookie 它工作正常 我倾向于将 php 设置为单独的文件 现在 我在同一页面上设置 cookie 但它似乎不起作用 expire time 5 setcookie r
  • 如何使用 JSP 页面中的参数运行 java 类?

    我之前编写了一个从 bash 文件执行的 java 类 现在我需要使用 Javascript HTML 按钮允许它从 JSP 页面执行 我想知道如何做到这一点 首先 我的课程是这样的 public class Sync public sta
  • WCF .svc 文件在 IIS7.5 .NET 4.0 中用作纯文本 - 不在默认网站下

    最终更新 事实证明 由于 DotNetNuke 模块的压缩问题 这是一个转移注意力的问题 我必须添加到 PageBlaster DNN 模块 所以这不再是问题 我正在将 WCF svc 文件部署到我的实时网站 http www pokerd
  • 无法打开输入文件:localhost:8080

    在 Windows 上 要从命令提示符运行 PHP Web 服务器 我键入 php s localhost 80800 t public 我收到此错误 无法打开输入文件 localhost 8080 And yet cmd php test
  • 在 SQL 中使用累积需求时在聚合级别上优化表概览

    我试图找到最好的方法来获得累积显示的总体概述的概述 即通过扣除不满足每个步骤中的累积要求的观察结果 这是表脚本和示例数据 CREATE TABLE Table A id INTEGER NOT NULL PRIMARY KEY totalA
  • Spark 中的迭代缓存与检查点

    我有一个在 Spark 上运行的迭代应用程序 我将其简化为以下代码 var anRDD org apache spark rdd RDD Int sc parallelize 0 to 1000 var c Long Int MaxValu
  • 在 Python App Engine API 中写入和读取 blobstore 文件以存储时间戳

    我正在使用 Google App Engine 的 python API 我想做的是从服务器加载文件列表 如果它已经过去了超过一个时间间隔 例如 1 小时 为此 我尝试将执行的最后一个操作小时存储到文件中 并在下次读取它以了解与下一个请求的
  • 自定义字体和 XML 布局 (Android)

    我正在尝试在 Android 中使用 XML 文件定义 GUI 布局 据我所知 没有办法指定您的小部件应该在 XML 文件中使用自定义字体 例如您放置在 asset font 中的字体 并且您只能使用系统安装的字体 我知道 在 Java 代
  • ggplot2 中的并排水平图例

    我想让我的 ggplot 图例并排出现在图下方 变量名称位于符号上方 就像它们在this https kohske wordpress com 2010 12 25 various position adjustments of legen
  • Html 锚文本格式

    这可能是一个基本问题 但我使用的是 HTML 锚点 锚点的文本作为多行段落 我有 br 锚文本部分中当前无法识别的标签 所有文本在输出中都在一行中 锚标签代码如下 li a href title View all Printed Docum
  • 为什么 arr 和 &arr 相同?

    我已经编程c c 很多年了 但是今天的偶然发现让我有些好奇 为什么下面的代码中两个输出产生相同的结果 arr当然是地址arr 0 即指向arr 0 我本来期望 arr是该指针的地址 但它具有相同的值arr int arr 3 cout lt
  • 如何使用 Visual Studio 命令行 + SCons 构建 Windows XP 应用程序?

    使用 Visual Studio 2015 IDE 构建 Windows XP 应用程序一点也不难 您只需要使用正确的平台工具集 v120 xp 或 v140 xp 并确保在目标计算机上安装正确的可再发行 Visual Studio 运行时
  • 绘制可调整大小(不相交)的多边形

    我到处寻找但找不到答案 我 需要通过鼠标交互绘制可调整大小的多边形 但我 不希望出现不规则 重叠或相交的多边形 结尾 这是绘制可调整大小的多边形的简单示例http www wolfpil de polygon html http www w
  • Pom.xml项目构建错误

    我已经在 Eclipse 中导入了 Maven 项目 但在 pom xml 中出现以下错误 项目构建错误 dependencyManagement dependency dependency systemPath 为 jdk tools j
  • 如何在uWSGI中配置允许的GET参数字符数?

    我正在使用 nginx uWSGI 运行 Django 刚刚从本地开发转移到临时服务器后 我发现我们发出的特定 GET 请求有一个很长的查询字符串 尝试调试 似乎 uWSGI 不接受这个基于长度的查询字符串 我可以传递短字符串并且它可以工作
  • 为什么 $1 在 hashref 赋值中指向与多个正则表达式匹配的相同值?

    对于没有故事的问题 请跳到行后 我正在把一串由一些字母和数字组成的字符串 这两种情况都可能出现 分成散列引用中的两个字段 仅当该字段存在时它们才应该出现 该字符串可能如下所示 D d 例如ZR17 R15 19 22 我不想简单地将其放入这
  • Oracle - 特定用户的审计跟踪

    正如主题所述 我正在寻找一种方法来跟踪特定用户的活动 可能有也可能没有 SYSDBA 或 SYSOPER 权限 例如 人力资源部 我想知道他的登录详细信息是什么 他更改了哪些对象 它们的原始值是什么 执行的 SQL 语句 执行了哪些过程 函
  • 升级 Zend Framework - 步骤和指南

    我维护的网站目前使用的是 Zend Framework 1 8 0 该版本已经有 2 年多的历史了 我想升级到 1 11 11 但在寻找如何顺利完成此操作的基本指南方面遇到了一些困难 就像用最新文件覆盖library Zend文件夹一样简单
  • 如何在 C++ 中将调色板生成器和调色板使用过滤器与 FFmpeg 一起使用?

    我想在 QT 项目中创建一个动画 gif 当我直接将 QImage 缩放到 AV PIX FMT RGB8 时 输出看起来很糟糕 带有闪烁的伪影 而当我缩放到 AV PIX FMT YUV420P 之间时 输出会抖动 这看起来并没有好多少