首页 > 其他分享 >FFMPEG--使用Filter实现YUV图像翻转

FFMPEG--使用Filter实现YUV图像翻转

时间:2023-09-14 14:45:42浏览次数:35  
标签:FFMPEG -- graph frame ctx ret YUV filter out

工具:ffmpeg过滤器模块
相关过滤器,其创建过程如下:
创建一个过滤器节点,如overlay:avfilter_get_by_name(“overlay”);
创建一个过滤器上下文并将其添加到FilterGraph上,如:avfilter_graph_create_filter(&overlayFilter_ctx, overlayFilter, “overlay”,
“y=0:H/2”, NULL, filter_graph);
    // overlay filter:视频合成
    AVFilter *overlayFilter = avfilter_get_by_name("overlay");
    AVFilterContext *overlayFilter_ctx;
    ret = avfilter_graph_create_filter(&overlayFilter_ctx, overlayFilter, "overlay",
                                       "y=0:H/2", NULL, filter_graph);

buffer(源过滤器)
buffersink(输出过滤器)
split(分流)
参数配置如:“outputs=2”
crop(裁剪)
参数配置裁剪区域:“out_w=iw:out_h=ih/2:x=0:y=0”
vfilpt(垂直翻转)
无需参数
overlay(视频合成)
将通过滤镜的图像叠加到某个位置:“y=0:H/2”
代码如下,整体流程均已注释:
#include <stdio.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
//#include <libavfilter/avfiltergraph.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>


int main(int argc, char* argv)
{
    int ret = 0;

    // input yuv
    FILE* inFile = NULL;
    const char* inFileName = "F:\\Avenger4_1_YUV420P_480x320.yuv";
    //errno_t Errno = fopen_s(&inFile, inFileName, "rb+");
    inFile = fopen(inFileName, "rb");
    if (!inFile) {
        printf("Fail to open file\n");
        return -1;
    }

    int in_width = 480;
    int in_height = 320;

    // output yuv
    FILE* outFile = NULL;
    const char* outFileName = "F:\\out_crop_vfilter_480x320.yuv";
    fopen_s(&outFile, outFileName, "wb");
    if (!outFile) {
        printf("Fail to create file for output\n");
        return -1;
    }

    //1.注册过滤器
    avfilter_register_all();

    //2.创建一个过滤器图层,管理所有过滤器
    AVFilterGraph* filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        printf("Fail to create filter graph!\n");
        return -1;
    }

    //3.获取一个用于AVFilterGraph输入的过滤器
    // source filter,源头过滤器,过滤器参数 一般为图像格式信息
    char args[512];
    sprintf(args,
        "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
        in_width, in_height, AV_PIX_FMT_YUV420P, 1, 25, 1, 1);
    //bufferSrc->description = Buffer video frames, and make them accessible to the filterchain.
    AVFilter* bufferSrc = avfilter_get_by_name("buffer");   // AVFilterGraph的输入源

    AVFilterContext* bufferSrc_ctx;
    ret = avfilter_graph_create_filter(&bufferSrc_ctx, bufferSrc, "in", args, NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create filter bufferSrc\n");
        return -1;
    }

    //4.获取一个用于AVFilterGraph输出的过滤器
    // sink filter,
    AVBufferSinkParams *bufferSink_params;
    AVFilterContext* bufferSink_ctx;
    AVFilter* bufferSink = avfilter_get_by_name("buffersink");
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };

    bufferSink_params = av_buffersink_params_alloc();
    bufferSink_params->pixel_fmts = pix_fmts;
    ret = avfilter_graph_create_filter(&bufferSink_ctx, bufferSink, "out", NULL,
                                       bufferSink_params, filter_graph);
    if (ret < 0) {
        printf("Fail to create filter sink filter\n");
        return -1;
    }

    //5. split filter:用于分流,outputs=2表示输出两股流
    AVFilter *splitFilter = avfilter_get_by_name("split");
    AVFilterContext *splitFilter_ctx;
    ret = avfilter_graph_create_filter(&splitFilter_ctx, splitFilter, "split", "outputs=2",
                                       NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create split filter\n");
        return -1;
    }

    //6. crop filter:裁剪,out_w=iw:out_h=ih/2:x=0:y=0表示裁剪得到的区域
    AVFilter *cropFilter = avfilter_get_by_name("crop");
    AVFilterContext *cropFilter_ctx;
    ret = avfilter_graph_create_filter(&cropFilter_ctx, cropFilter, "crop",
                                       "out_w=iw:out_h=ih/2:x=0:y=0", NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create crop filter\n");
        return -1;
    }

    //7. vflip filter:垂直翻转,从上到下?==》最终合成的位置由overlay过滤器设置,经确认是将裁剪的区域翻转后拼接到图像下半部分,
    //图像上半部分不变(不变是因为split分割出了两路流,过滤器是操作的辅助流)
    //配合使用crop:out_w=iw:out_h=ih/2:x=0:y=ih/2进行验证
    AVFilter *vflipFilter = avfilter_get_by_name("vflip");
    AVFilterContext *vflipFilter_ctx;
    ret = avfilter_graph_create_filter(&vflipFilter_ctx, vflipFilter, "vflip", NULL, NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create vflip filter\n");
        return -1;
    }

    //8. overlay filter:视频合成
    AVFilter *overlayFilter = avfilter_get_by_name("overlay");
    AVFilterContext *overlayFilter_ctx;
    ret = avfilter_graph_create_filter(&overlayFilter_ctx, overlayFilter, "overlay",
                                       "y=0:H/2", NULL, filter_graph);
    if (ret < 0) {
        printf("Fail to create overlay filter\n");
        return -1;
    }
    //  split用于分流
    //  crop确定裁剪位置
    //  vflip仅翻转图像
    //  overlay确定视频叠加位置

    //                          [main]
    //input ----> split ---------------------> overlay --> output
    //              |                             ^
    //              |[tmp]                  [flip]|
    //              |                             |
    //              v                             |
    //              +-----> crop --> vflip -------+

    //9.过滤器连接
    // src filter to split filter
    ret = avfilter_link(bufferSrc_ctx, 0, splitFilter_ctx, 0);
    if (ret != 0) {
        printf("Fail to link src filter and split filter\n");
        return -1;
    }
    // split filter's first pad to overlay filter's main pad
    ret = avfilter_link(splitFilter_ctx, 0, overlayFilter_ctx, 0);
    if (ret != 0) {
        printf("Fail to link split filter and overlay filter main pad\n");
        return -1;
    }
    // split filter's second pad to crop filter
    ret = avfilter_link(splitFilter_ctx, 1, cropFilter_ctx, 0);
    if (ret != 0) {
        printf("Fail to link split filter's second pad and crop filter\n");
        return -1;
    }
    // crop filter to vflip filter
    ret = avfilter_link(cropFilter_ctx, 0, vflipFilter_ctx, 0);
    if (ret != 0) {
        printf("Fail to link crop filter and vflip filter\n");
        return -1;
    }
    // vflip filter to overlay filter's second pad
    ret = avfilter_link(vflipFilter_ctx, 0, overlayFilter_ctx, 1);
    if (ret != 0) {
        printf("Fail to link vflip filter and overlay filter's second pad\n");
        return -1;
    }
    // overlay filter to sink filter
    ret = avfilter_link(overlayFilter_ctx, 0, bufferSink_ctx, 0);
    if (ret != 0) {
        printf("Fail to link overlay filter and sink filter\n");
        return -1;
    }

    //10. check filter graph
    ret = avfilter_graph_config(filter_graph, NULL);
    if (ret < 0) {
        printf("Fail in filter graph\n");
        return -1;
    }

    //11.打印filtergraph具体情况
    char *graph_str = avfilter_graph_dump(filter_graph, NULL);
    FILE* graphFile = NULL;
    fopen_s(&graphFile, "graphFile.txt", "w");  // 打印filtergraph的具体情况
    fprintf(graphFile, "%s", graph_str);
    av_free(graph_str);

    //数据处理,按帧处理
    AVFrame *frame_in = av_frame_alloc();
    unsigned char *frame_buffer_in = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
    av_image_fill_arrays(frame_in->data, frame_in->linesize, frame_buffer_in,
        AV_PIX_FMT_YUV420P, in_width, in_height, 1);

    AVFrame *frame_out = av_frame_alloc();
    unsigned char *frame_buffer_out = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
    av_image_fill_arrays(frame_out->data, frame_out->linesize, frame_buffer_out,
        AV_PIX_FMT_YUV420P, in_width, in_height, 1);

    frame_in->width = in_width;
    frame_in->height = in_height;
    frame_in->format = AV_PIX_FMT_YUV420P;
    uint32_t frame_count = 0;
    while (1) {
        // 读取yuv数据
        if (fread(frame_buffer_in, 1, in_width*in_height * 3 / 2, inFile) != in_width*in_height * 3 / 2) {
            break;
        }
        //input Y,U,V
        frame_in->data[0] = frame_buffer_in;
        frame_in->data[1] = frame_buffer_in + in_width*in_height;
        frame_in->data[2] = frame_buffer_in + in_width*in_height * 5 / 4;

        if (av_buffersrc_add_frame(bufferSrc_ctx, frame_in) < 0) {
            printf("Error while add frame.\n");
            break;
        }
        // filter内部自己处理
        /* pull filtered pictures from the filtergraph */
        ret = av_buffersink_get_frame(bufferSink_ctx, frame_out);
        if (ret < 0)
            break;

        //output Y,U,V,yuv分量的偏移要为行距的长度才行,实际写的长度为实际的宽,图像宽是4的倍数的话linesize和width是相等的
        if (frame_out->format == AV_PIX_FMT_YUV420P) {
            for (int i = 0; i < frame_out->height; i++) {
                fwrite(frame_out->data[0] + frame_out->linesize[0] * i, 1, frame_out->width, outFile);
            }
            for (int i = 0; i < frame_out->height / 2; i++) {
                fwrite(frame_out->data[1] + frame_out->linesize[1] * i, 1, frame_out->width / 2, outFile);
            }
            for (int i = 0; i < frame_out->height / 2; i++) {
                fwrite(frame_out->data[2] + frame_out->linesize[2] * i, 1, frame_out->width / 2, outFile);
            }
        }
        ++frame_count;
        if(frame_count % 25 == 0)
            printf("Process %d frame!\n",frame_count);
        av_frame_unref(frame_out);
    }

    fclose(inFile);
    fclose(outFile);

    av_frame_free(&frame_in);
    av_frame_free(&frame_out);
    avfilter_graph_free(&filter_graph); // 内部去释放AVFilterContext产生的内存

    printf("Exe Run Over\n");
    return 0;
}



效果图如下:

标签:FFMPEG,--,graph,frame,ctx,ret,YUV,filter,out
From: https://www.cnblogs.com/kn-zheng/p/17702426.html

相关文章

  • python分片和断点续传oss
    分片上传和断点续传是常见于文件上传至云存储服务的操作,其中OSS(ObjectStorageService)是阿里云提供的云存储服务。Python可以用来实现分片上传和断点续传到阿里云OSS。下面是一个简单的示例,演示了如何使用Python和阿里云OSSSDK进行分片上传和断点续传。首先,确保你已......
  • 应用层通信协议设计
    一、应用层通信协议概述TCP/UDP是基于字节流的传输层通信协议,对于其的编程是基于IO流编程,所谓“流”,就是没有界限的一长串二进制数据。TCP/UDP作为传输层协议,并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分。所以在业务上一个完整的数据包在进行传......
  • XXL-JOB快速搭建
    1.XXL-JOB简介XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。它的有两个核心模块,一个模块叫做调度中心,另外一个模块叫做执行器,它把任务调度和任务执行分成两个部分。这样调度模块只......
  • MySQL Node.js mysql 事务
    MySQLNode.jsmysql事务在MySQL数据库中,事务(transaction)是一组原子性操作,要么全部执行成功,要么全部回滚回去。在多用户并发环境中,事务可以保护数据的一致性和完整性。MySQL提供了ACID属性,并且支持事务。Node.js中,可以通过使用mysql模块的连接来实现事务。什么是事务在MySQL数......
  • Mysql命令整理
    整理一下,时常用到,不定期更新:(1)连接数据库:/local/mysql/bin./mysql--port=3301-uroot-p123456--protocol=tcp(2)创建数据库:createdatabasedbname;(3)切换数据库:use dbname ;(4)赋予权限:GRANTALLONdbname.*TO......
  • 关于批量写入数据库
    今天上午写个程序要将几张表内的数据读出写入一张表(2w多数据),开始没有使用批处理(每条写入都自动commit),写入速度在分钟级(太慢了后来就没计时),后来添加了批处理(不自动commit),全部用时20多秒。可见批处理写入数据库要快很多。程序大致结构如下:EntityManagerem=JPA.em();em.setFlus......
  • 使用aapt dump命令获取Apk信息
    因工作需要,前一段时间继续又遇到了解析Apk信息的工作。由于这次需要读取android资源文件中的内容,因此使用了aapt工具,简单记录一下过程如下:  1.使用java.util.zip解包apk,使用AXMLPrinter反编译AndroidManifest.xml文件,解析XML文件获取:包名,版本号,最低sdk版本名,用户权限信息。......
  • C#中的异步编程和并发控制
    简介:欢迎来到C#语言入门指南的第十篇博客!在前几篇博客中,我们已经学习了C#的基本语法、面向对象编程、集合和异常处理等内容。今天,我们将进一步探讨两个重要的主题:异步编程和并发控制。这些主题对于处理多任务和提高程序性能至关重要。让我们深入了解吧!1.异步编程:异步编程是一种......
  • Bootstrap-01
    Bootstrap概念1.概念:一个前端开发的框架,Bootstrap,来自Twitter,是目前很受欢迎的前端框架。Bootstrap是基于HTML、CSS、JavaScript的,它简洁灵活,使得Web开发更加快捷。框架:一个半成品软件,开发人员可以在框架基础上,在进行开发,简化编码。好处:......
  • Java图片剪裁功能实现
    目前一些社交型互联网应用都有一些上传图片(例如头像,照片等)对预览图进行剪裁的功能。前一段时间在工作也遇到这个问题,总结一下基本实现步骤及代码(包含图片放大,缩小,设置品质,对指定点区域剪裁功能),使用JPEG格式图片测试通过,其它格式图片尚未验证。一、基本步骤:1.将图片文件的InputS......