如何在 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 一起使用? 的相关文章

  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • 没有强命名的代码签名是否会让您的应用程序容易被滥用?

    尝试了解authenticode代码签名和强命名 我是否正确地认为 如果我对引用一些 dll 非强命名 的 exe 进行代码签名 恶意用户就可以替换我的 DLL 并以看似由我签名但正在运行的方式分发应用程序他们的代码 假设这是真的 那么您似
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • 用于登录 .NET 的堆栈跟踪

    我编写了一个 logger exceptionfactory 模块 它使用 System Diagnostics StackTrace 从调用方法及其声明类型中获取属性 但我注意到 如果我在 Visual Studio 之外以发布模式运行代
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 关于 C++ 转换:参数 1 从“[some_class]”到“[some_class]&”没有已知的转换

    我正在研究 C 并且遇到了一个错误 我不知道确切的原因 我已经找到了解决方案 但仍然想知道原因 class Base public void something Base b int main Base b b something Base
  • 带动态元素的 WPF 启动屏幕。如何?

    我是 WPF 新手 我需要一些帮助 我有一个加载缓慢的 WPF 应用程序 因此我显示启动屏幕作为权宜之计 但是 我希望能够在每次运行时更改屏幕 并在文本区域中显示不同的引言 这是一个生产力应用程序 所以我将使用非愚蠢但激励性的引言 当然 如
  • WCF 中 SOAP 消息的数字签名

    我在 4 0 中有一个 WCF 服务 我需要向 SOAP 响应添加数字签名 我不太确定实际上应该如何完成 我相信响应应该类似于下面的链接中显示的内容 https spaces internet2 edu display ISWG Signe
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 这些作业之间是否存在顺序点?

    以下代码中的两个赋值之间是否存在序列点 f f x 1 1 x 2 不 没有 在这种情况下 标准确实是含糊不清的 如果你想确认这一点 gcc 有这个非常酷的选项 Wsequence point在这种情况下 它会警告您该操作可能未定义
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 测试用例执行完成后,无论是否通过,如何将测试用例结果保存在变量中?

    我正在使用 NUNIT 在 Visual Studio 中使用 Selenium WebDriver 测试用例的代码是 我想在执行测试用例后立即在变量中记录测试用例通过或失败的情况 我怎样才能实现这一点 NUnit 假设您使用 NUnit
  • IEnumreable 动态和 lambda

    我想在 a 上使用 lambda 表达式IEnumerable

随机推荐

  • 如何确定 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 之间时 输出会抖动 这看起来并没有好多少