多个同类型编码视频切换时,只改变SourceFilter,不改变Filter Graph中其他Filter的C#实现

大概测试了一下,如果直接RenderFile播放的话,视频之间从上一个视频Stop到下一个视频Run之间 所需的时间为下面方法的至少3倍时间,

对于采用同一种视频编码的多个视频,可以采用只改变SourceFilter,而其他Filter不用改变的方式来切换视频。
实现方式如下,注意需要
DirectShowLibNET

DirectShowLibNET - A library to allow .NET applications to use DirectShow functions

 

public void ChangeFileSource(string newfilename)
        {
            // 先加入 新加入的文件名代表的SourceFilter
            IPin nextOutPin = null;
            int hr = 0;
            IBaseFilter sourceFilter = null;
            hr = m_FilterGraph.AddSourceFilter(newfilename, newfilename, out sourceFilter);
            DsError.ThrowExceptionForHR(hr);

            hr = sourceFilter.FindPin("Output", out nextOutPin);
            DsError.ThrowExceptionForHR(hr);
            
            //然后停止 当前FilterGraph的播放,
            hr = m_mediaCtrl.Stop();
            DsError.ThrowExceptionForHR(hr);

            //然后枚举所有Filter
            IEnumFilters enumFilters = null;
            hr = m_FilterGraph.EnumFilters(out enumFilters);

            int iFiltCount = 0;

            while (0 == enumFilters.Skip(1))
            {
                iFiltCount++;
            }
            enumFilters.Reset();

            int iPos = 0;
            IBaseFilter[] filters = new IBaseFilter[iFiltCount];
            while (iPos < iFiltCount)
            {
                int numFiltersToGet = 1;
                IBaseFilter[] filter = new IBaseFilter[numFiltersToGet];
                IntPtr fetched = IntPtr.Zero;
                hr = enumFilters.Next(1, filter, fetched);
                DsError.ThrowExceptionForHR(hr);
                filters[iPos] = filter[0];
                iPos++;
            }

            //此循环将新加入的SourceFilter加入到FilterGraph中,并将原SourceFilter移除并释放掉
            for (iPos = 0; iPos < iFiltCount; iPos++)
            {
                m_FilterGraph.RemoveFilter(filters[iPos]);
                if (filters[iPos] != m_currentSource)
                {
                    m_FilterGraph.AddFilter(filters[iPos], null);
                }
                else
                {
                    Marshal.ReleaseComObject(filters[iPos]);
                }
            }

            hr = m_FilterGraph.Render(nextOutPin);
            DsError.ThrowExceptionForHR(hr);
            m_currentSource = sourceFilter;
            sourceFilter = null;

            m_mediaCtrl.Run();

            if (enumFilters != null)
            {
                Marshal.ReleaseComObject(enumFilters);
                enumFilters = null;
            }
        }


 

### 回答1: 为视频加水印可以使用FFmpeg的`overlay`过滤器。以下是使用纯C代码实现的示例: ``` AVFilterContext *buffersrc_ctx; AVFilterContext *buffersink_ctx; AVFilterGraph *filter_graph; AVFilter *buffersrc = avfilter_get_by_name("buffer"); AVFilter *buffersink = avfilter_get_by_name("buffersink"); AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); const char *filter_descr = "overlay=10:10"; int ret; filter_graph = avfilter_graph_alloc(); if (!outputs || !inputs || !filter_graph) { ret = AVERROR(ENOMEM); goto end; } // 添加 buffer 过滤器到过滤器图 ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", "video_size=1920x1080:pix_fmt=0:time_base=1/25", NULL, filter_graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); goto end; } // 添加 buffersink 过滤器到过滤器图 ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); goto end; } // 设置 filter_graph 的输入输出链 outputs->name = av_strdup("in"); outputs->filter_ctx = buffersrc_ctx; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = buffersink_ctx; inputs->pad_idx = 0; inputs->next = NULL; // 添加 filterfilter_graph ret = avfilter_graph_parse_ptr(filter_graph, filter_descr, &inputs, &outputs, NULL); if (ret < 0) { goto end; } // 链接 filter ret = avfilter_graph_config(filter_graph, NULL); if (ret < 0) { goto end; } end: avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); avfilter_graph_free(&filter_graph); ``` 以上代码将在视频的左上角添加一个10像素的水印。可以通过修改`filter_descr`来调整水印的位置和大小。 ### 回答2: 在使用 Ffmpeg 为视频加水印,可以通过 Filter 部分的代码来实现。 首先,需要引入 Ffmpeg 头文件和相关的库文件: ```c extern "C" { #include <libavfilter/avfilter.h> #include <libavfilter/buffersink.h> #include <libavfilter/buffersrc.h> } #pragma comment(lib, "libavfilter.lib") #pragma comment(lib, "libavdevice.lib") #pragma comment(lib, "libavformat.lib") #pragma comment(lib, "libavcodec.lib") #pragma comment(lib, "libswscale.lib") ``` 然后,可以定义一个函数来实现添加水印的功能,如下所示: ```c void addWatermark(const char* inputFileName, const char* watermarkFileName, const char* outputFileName) { AVFilterContext* buffersink_ctx; AVFilterContext* buffersrc_ctx; AVFilterGraph* filter_graph; AVFormatContext* format_ctx; AVPacket packet; int ret; // 打开输入文件 ret = avformat_open_input(&format_ctx, inputFileName, 0, 0); if (ret < 0) { // 处理打开输入文件失败的情况 return; } // 查找流信息 ret = avformat_find_stream_info(format_ctx, 0); if (ret < 0) { // 处理查找流信息失败的情况 avformat_close_input(&format_ctx); return; } // 创建图像过滤器核心 filter_graph = avfilter_graph_alloc(); if (!filter_graph) { // 处理创建图像过滤器核心失败的情况 avformat_close_input(&format_ctx); return; } // 打开输入过滤器源(Source) const AVFilter* buffersrc = avfilter_get_by_name("buffer"); buffersrc_ctx = avfilter_graph_alloc_filter(filter_graph, buffersrc, "src"); if (!buffersrc_ctx) { // 处理打开输入过滤器源失败的情况 avfilter_graph_free(&filter_graph); avformat_close_input(&format_ctx); return; } // 设置输入过滤器源的参数 av_opt_set_int_list(buffersrc_ctx, "pix_fmts", fmts, -1, AV_OPT_SEARCH_CHILDREN); av_opt_set_int(buffersrc_ctx, "width", format_ctx->streams[0]->codecpar->width, AV_OPT_SEARCH_CHILDREN); av_opt_set_int(buffersrc_ctx, "height", format_ctx->streams[0]->codecpar->height, AV_OPT_SEARCH_CHILDREN); // 打开输出过滤器接收器(Sink) const AVFilter* buffersink = avfilter_get_by_name("buffersink"); buffersink_ctx = avfilter_graph_alloc_filter(filter_graph, buffersink, "sink"); if (!buffersink_ctx) { // 处理打开输出过滤器接收器失败的情况 avfilter_graph_free(&filter_graph); avformat_close_input(&format_ctx); return; } // 初始化图像过滤器核心 ret = avfilter_graph_parse_ptr(filter_graph, filter_desc, &buffersrc_ctx, &buffersink_ctx, NULL); if (ret < 0) { // 处理初始化图像过滤器核心失败的情况 avfilter_graph_free(&filter_graph); avformat_close_input(&format_ctx); return; } // 链接图像过滤器核心 ret = avfilter_graph_config(filter_graph, NULL); if (ret < 0) { // 处理链接图像过滤器核心失败的情况 avfilter_graph_free(&filter_graph); avformat_close_input(&format_ctx); return; } // 读取水印文件 AVFormatContext* watermark_format_ctx; ret = avformat_open_input(&watermark_format_ctx, watermarkFileName, 0, 0); if (ret < 0) { // 处理读取水印文件失败的情况 avfilter_graph_free(&filter_graph); avformat_close_input(&format_ctx); return; } // 从水印文件读取数据包 while (av_read_frame(watermark_format_ctx, &packet) >= 0) { // 对水印的数据包进行过滤 if (packet.stream_index == 0) { av_buffersrc_add_frame_flags(buffersrc_ctx, packet.data, AV_BUFFERSRC_FLAG_PUSH); } av_packet_unref(&packet); } // 处理输出过滤器接收器(Sink)的输出帧 while (1) { AVFrame* frame = av_frame_alloc(); ret = av_buffersink_get_frame(buffersink_ctx, frame); if (ret < 0) { // 处理处理输出过滤器接收器的输出帧失败的情况 av_frame_unref(frame); break; } // 对输出帧进行处理,例如进行水印贴图 // ... av_frame_unref(frame); } // 释放资源 avfilter_graph_free(&filter_graph); avformat_close_input(&format_ctx); avformat_close_input(&watermark_format_ctx); } ``` 以上是一个参考的基本代码示例,其需要替换部分代码来符合实际需求。此外,添加水印的具体处理方法,例如进行水印贴图,需要根据实际情况实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值