首页 > 其他分享 >ffmpeg实现画中画

ffmpeg实现画中画

时间:2023-07-13 17:58:00浏览次数:47  
标签:ffmpeg 实现 graph 画中画 ret filter msg pFrame out

本篇博客相比上一篇《 ffmpeg滤镜学习一,movie+overlay滤镜实现视频加水印、画中画》更深入一些,本次的实现,可以控制子画面出现的时间段、子画面播放时间等,这篇文章主要参考了大师兄悟空公众号下的文章《使用 FFmpeg 实现画中画效果(一)》,下面看一下具体实现:

首先提出5个问题:

  1. 子画面展示位置?

  2. 子画面从主画面的哪个时间点开始播放?

  3. 子画面从子画面的哪个时间点开始播放?

  4. 子画面是按照时间段显示还是一直显示?

  5. 如果子画面和主画面不等长怎么办?

要解决这5个问题,主要使用overlay滤镜,如下:

ffmpeg -h filter=overlay
....

首先通过x、y参数可以解决子画面显示位置的问题。

shortest参数可以解决主画面、子画面时间不等的问题。

enable参数可以解决2、4两个问题,第三个问题需要使用一个新的滤镜setpts,主画面与子画面的视频偏移可以通过setpts滤镜设置,如下:

ffmpeg -h filter=setpts

设置画布:

const char *filter_descr = "movie=out1.mp4[in2];[in2]setpts=PTS[out2];[0:v][out2]overlay=x=20:y=120:enable='between(t,2,15)':shortest=1";

下代码吧:

/*
 * 实现对现有视频增加水印,可以是图片、也可以是视频,若为视频,类似画中画
 */
#include "myffmpeg/util.h"
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
    int open_input_file(AVFormatContext *fmt, AVCodecContext **codecctx, AVCodec *codec, const char *filename, int index)
    {
        int ret = 0;
        char msg[500];
        *codecctx = avcodec_alloc_context3(codec);
        ret = avcodec_parameters_to_context(*codecctx, fmt->streams[index]->codecpar);
        if (ret < 0)
        {
            sprintf(msg, "avcodec_parameters_to_context error,ret:%d\n", ret);
            lp_log(msg);
            return -1;
        }
 
        // open 解码器
        ret = avcodec_open2(*codecctx, codec, NULL);
        if (ret < 0)
        {
            sprintf(msg, "avcodec_open2 error,ret:%d\n", ret);
            lp_log(msg);
            return -2;
        }
        printf("pix:%d\n", (*codecctx)->pix_fmt);
        return ret;
    }
 
    int init_filter(AVFilterContext **buffersrc_ctx, AVFilterContext **buffersink_ctx, AVFilterGraph **filter_graph, AVStream *stream, AVCodecContext *codecctx, const char *filter_desc)
    {
        int ret = -1;
        char args[512];
        char msg[500];
        const AVFilter *buffersrc = avfilter_get_by_name("buffer");
        const AVFilter *buffersink = avfilter_get_by_name("buffersink");
 
        AVFilterInOut *input = avfilter_inout_alloc();
        AVFilterInOut *output = avfilter_inout_alloc();
 
        AVRational time_base = stream->time_base;
        enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
 
        if (!output || !input || !filter_graph)
        {
            ret = -1;
            sprintf(msg, "avfilter_graph_alloc/avfilter_inout_alloc error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
        snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", codecctx->width, codecctx->height, codecctx->pix_fmt, stream->time_base.num, stream->time_base.den, codecctx->sample_aspect_ratio.num, codecctx->sample_aspect_ratio.den);
        ret = avfilter_graph_create_filter(buffersrc_ctx, buffersrc, "in", args, NULL, *filter_graph);
        if (ret < 0)
        {
            sprintf(msg, "avfilter_graph_create_filter buffersrc error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
 
        ret = avfilter_graph_create_filter(buffersink_ctx, buffersink, "out", NULL, NULL, *filter_graph);
        if (ret < 0)
        {
            sprintf(msg, "avfilter_graph_create_filter buffersink error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
        ret = av_opt_set_int_list(*buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
        if (ret < 0)
        {
            sprintf(msg, "av_opt_set_int_list error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
        /*
     * The buffer source output must be connected to the input pad of
     * the first filter described by filters_descr; since the first
     * filter input label is not specified, it is set to "in" by
     * default.
     */
        output->name = av_strdup("in");
        output->filter_ctx = *buffersrc_ctx;
        output->pad_idx = 0;
        output->next = NULL;
 
        /*
     * The buffer sink input must be connected to the output pad of
     * the last filter described by filters_descr; since the last
     * filter output label is not specified, it is set to "out" by
     * default.
     */
        input->name = av_strdup("out");
        input->filter_ctx = *buffersink_ctx;
        input->pad_idx = 0;
        input->next = NULL;
 
        if ((ret = avfilter_graph_parse_ptr(*filter_graph, filter_desc, &input, &output, NULL)) < 0)
        {
            sprintf(msg, "avfilter_graph_parse_ptr error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
 
        if ((ret = avfilter_graph_config(*filter_graph, NULL)) < 0)
        {
            sprintf(msg, "avfilter_graph_config error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
    end:
        avfilter_inout_free(&input);
        avfilter_inout_free(&output);
        return ret;
    }
 
    int my_filter(const char *name)
    {
        int ret;
        char msg[500];
        // const char *filter_descr = "movie=my_logo.png[wm];[in][wm]overlay=10:10[out]";
        // const char *filter_descr = "scale=640:360,transpose=cclock";
        const char *filter_descr = "movie=out1.mp4[in2];[in2]setpts=PTS[out2];[in][out2]overlay=x=20:y=120:enable='between(t,2,15)':shortest=1";
        AVFormatContext *pFormatCtx = NULL;
        AVCodecContext *pCodecCtx;
        AVFilterContext *buffersink_ctx;
        AVFilterContext *buffersrc_ctx;
        AVFilterGraph *filter_graph;
        AVCodec *codec;
        int video_stream_index = -1;
 
        AVPacket packet;
        AVFrame *pFrame;
        AVFrame *pFrame_out;
        filter_graph = avfilter_graph_alloc();
        FILE *fp_yuv = fopen("test.yuv", "wb+");
        ret = avformat_open_input(&pFormatCtx, name, NULL, NULL);
        if (ret < 0)
        {
            sprintf(msg, "avformat_open_input error,ret:%d\n", ret);
            lp_log(msg);
            ret = -1;
            goto end;
        }
 
        ret = avformat_find_stream_info(pFormatCtx, NULL);
        if (ret < 0)
        {
            sprintf(msg, "avformat_find_stream_info error,ret:%d\n", ret);
            lp_log(msg);
            ret = -2;
            goto end;
        }
 
        ret = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
        if (ret < 0)
        {
            sprintf(msg, "av_find_best_stream error,ret:%d\n", ret);
            lp_log(msg);
            ret = -3;
            goto end;
        }
        // 获取到视频流索引
        video_stream_index = ret;
 
        av_dump_format(pFormatCtx, 0, name, 0);
        if ((ret = open_input_file(pFormatCtx, &pCodecCtx, codec, name, video_stream_index)) < 0)
        {
            ret = -4;
            sprintf(msg, "open_input_file error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
 
        if ((ret = init_filter(&buffersrc_ctx, &buffersink_ctx, &filter_graph, pFormatCtx->streams[video_stream_index], pCodecCtx, filter_descr)) < 0)
        {
            ret = -5;
            sprintf(msg, "init_filter error,ret:%d\n", ret);
            lp_log(msg);
            goto end;
        }
        pFrame = av_frame_alloc();
        pFrame_out = av_frame_alloc();
        while (1)
        {
            if ((ret = av_read_frame(pFormatCtx, &packet)) < 0)
                break;
 
            if (packet.stream_index == video_stream_index)
            {
                ret = avcodec_send_packet(pCodecCtx, &packet);
                if (ret < 0)
                {
                    sprintf(msg, "avcodec_send_packet error,ret:%d\n", ret);
                    lp_log(msg);
                    break;
                }
 
                while (ret >= 0)
                {
                    ret = avcodec_receive_frame(pCodecCtx, pFrame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    {
                        break;
                    }
                    else if (ret < 0)
                    {
                        sprintf(msg, "avcodec_receive_frame error,ret:%d\n", ret);
                        lp_log(msg);
                        goto end;
                    }
 
                    pFrame->pts = pFrame->best_effort_timestamp;
 
                    /* push the decoded frame into the filtergraph */
                    ret = av_buffersrc_add_frame_flags(buffersrc_ctx, pFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
                    if (ret < 0)
                    {
                        sprintf(msg, "av_buffersrc_add_frame_flags error,ret:%d\n", ret);
                        lp_log(msg);
                        break;
                    }
 
                    /* pull filtered frames from the filtergraph */
                    while (1)
                    {
                        ret = av_buffersink_get_frame(buffersink_ctx, pFrame_out);
                        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                            break;
                        if (ret < 0)
                        {
                            ret = -6;
                            goto end;
                        }
                        if (pFrame_out->format == AV_PIX_FMT_YUV420P)
                        {
                            //Y, U, V
                            for (int i = 0; i < pFrame_out->height; i++)
                            {
                                fwrite(pFrame_out->data[0] + pFrame_out->linesize[0] * i, 1, pFrame_out->width, fp_yuv);
                            }
                            for (int i = 0; i < pFrame_out->height / 2; i++)
                            {
                                fwrite(pFrame_out->data[1] + pFrame_out->linesize[1] * i, 1, pFrame_out->width / 2, fp_yuv);
                            }
                            for (int i = 0; i < pFrame_out->height / 2; i++)
                            {
                                fwrite(pFrame_out->data[2] + pFrame_out->linesize[2] * i, 1, pFrame_out->width / 2, fp_yuv);
                            }
                        }
                        av_frame_unref(pFrame_out);
                    }
                    av_frame_unref(pFrame);
                }
            }
            av_packet_unref(&packet);
        }
    end:
        avcodec_free_context(&pCodecCtx);
        fclose(fp_yuv);
    }
}

标签:ffmpeg,实现,graph,画中画,ret,filter,msg,pFrame,out
From: https://www.cnblogs.com/kn-zheng/p/17551670.html

相关文章

  • ffmpeg画中画效果
    1.画中画效果overlay滤镜(覆盖、叠加)overlay的使用语法:ffmpeg  -i input1 -i input2  -filter_complex  overlay=x:y output这里不使用-vf简单滤镜,而是使用-filter_complex复合滤镜,因为是有多个输入源。但是如果通过链接标签,可以t结合movie视频源使用-vf滤......
  • FFmpeg命令行实现画中画
    哈喽,小伙伴们,欢迎回来,上一讲小编带大家学习了如何使用FFmpeg命令行为视频添加字幕,不知道大家掌握的效果怎么样呢?本期我们又要开始新的征程了,使用FFmpeg命令行实现画中画。画中画,因为有两个"画"字,故我们需要两个视频素材。当我们准备好两个视频素材以后,接下来我们就可以开始了:一、......
  • kubernetes 实现 list-watch 的底层原理
    我们都知道,controller-manager,scheduler,kubelet会向apiserver监听感兴趣的对象,当监听对象的内容或状态发生变化后,对应的事件会立即推送到监听者。借由这套事件通知机制,kubernetes才能良好地运转。那么这套事件通知机制是如何实现并驱动的呢?1.etcd在k8s中,apiserver是......
  • 达梦split函数的实现,pipe row的用法
    本文转载自:https://www.yii666.com/article/516427.html 为了让PL/SQL函数返回数据的多个行,必须通过返回一个REFCURSOR或一个数据集合来完成。REFCURSOR的这种情况局限于可以从查询中选择的数据,而整个集合在可以返回前,必须进行具体化。达梦和Oracle9i通过引入的管道化......
  • 供应链产能受限型选址模型——Python实现
    选址问题是运筹学中非常经典的问题。选址问题是指在确定选址对象,选址目标区,成本函数以及存在何种约束条件的前提下,以总物流成本最低或总服务最优或社会效益最大化为总目标,以确定物流系统中物流节点的数量、位置,从而合理规划物流网络结构。设施选址问题(FacilityLocationProblem)自......
  • 【Netty】「优化进阶」(二)浅谈 LengthFieldBasedFrameDecoder:如何实现可靠的消息分割?
    前言本篇博文是《从0到1学习Netty》中进阶系列的第二篇博文,主要内容是通过不同的应用案例来了解LengthFieldBasedFrameDecoder是如何处理不同的消息,实现自动分割,往期系列文章请访问博主的Netty专栏,博文中的所有代码全部收集在博主的GitHub仓库中;介绍LengthFieldBasedFrameDe......
  • Springboot实现注解判断权限
    Springboot实现注解判断权限今天记录一下使用springboot的注解来给方法加权限避免了每个方法都需要大量的权限判断超级好用√@目录Springboot实现注解判断权限1.创建权限注解2.定义一个权限的枚举类3.创建拦截器AOP校验权限poincut表达式介绍4.使用注解1.创建权限注解首先......
  • AQS实现原理
    在java.util.concurrent包中,我们经常会使用ReentrantLock,CyclicBarrier等工具类,但是我们往往对其内部的实现原理却并不知晓。本篇文章主要对上述工具类的核心实现AQS进行剖析,分析原理可以让我们学习到大神的代码设计思维。文章将从一下几个方面分析:1.AQS是什么?AbstractQueuedS......
  • 如何实现python直方图的具体操作步骤
    Python直方图直方图是数据可视化中常用的一种图形表示方式,它可以将数据按照一定的范围分成若干个区间,并统计每个区间内数据的个数。Python提供了多种库和函数来绘制直方图,使得数据分析和数据挖掘更加方便和直观。matplotlib库绘制直方图在Python中,最常用的绘图库之一就是matplot......
  • vue - 点击按钮上传文件功能的实现
    methods:{//点击调用上传方法asynchandleUpload(row){try{letfileList=awaitthis.getFile("",true);//参数1:选取文件类型如.pdf、.png、.doc文件,参数2、是否多选console.log(fileList);//上传文件可在此处进行}catch......