c#根据场景不同来拆分视频

这个功能也是我个人需求。

需求:我在dy下载了同一个产品介绍的几个短视频,每个视频又是由多个场景组成,这个时候我需要每个视频里抽取一个场景,来组合成一个新视频。

这个就需要用到【根据场景拆分视频】。

大家先看看如下动图,感知场景:

场景即:连续连贯的一段影像为一个场景,上面动图有6个场景。

解决方案:ffmpeg命令计算出新场景起点时候戳,导出到log.txt中,c#解析log.txt,提取到各个场景的pts_time和总长,然后循环截取各个场景视频。

开整。。。。。

一、提取各个场景pts_time和总长度

命令:ffmpeg -i aa.mp4 -filter:v "select='gt(scene,0.15)',showinfo" -f null - 1> log.txt 2>&1

log.txt文件中的场景起始点关键数据:

 gt(scene,0.15),这里这个0.15,我的理解是两帧之面差异值0-1,1为完全不同,我测试下来0.15就可以检测出各个场景了,你自己可以根据自己的需要来微调,上代码

public static List GetVideoSenueTime(string filename,float flag,out double duration)
        {
            string logfile = "log.txt";
            string cmd = string.Format(" -i {0} "
                + "-filter:v \"select='gt(scene,{1})',showinfo\" -f null - 1>{2} 2>&1 -y", filename, flag, logfile);
            //执行ffmpeg命令
            RunCmd(@cmd);
            duration = 0;
            if (File.Exists(logfile))
            {
                List lst = new List();
                string tmpstr;
                int n;                
                foreach (string line in System.IO.File.ReadLines(logfile))
                {
                    //先找总时长
                    if (duration == 0)
                    {
                        n = line.IndexOf("Duration: ");
                        if (n < 0) continue;
                        tmpstr = GetTimeStr(line, n + 10,",");
                        if (tmpstr.Length > 0)
                        {
                            string[] ary = tmpstr.Split('.');
                            if (ary.Length == 2)
                            {
                                TimeSpan t = TimeSpan.Parse(ary[0]);     
                                duration = t.Seconds + double.Parse("0." + ary[1]);
                            }
                            else duration = -1;                            
                        }
                        continue;
                    }
                    n=line.IndexOf("pts_time:");
                    if (n < 0) continue;
                    tmpstr = GetTimeStr(line,n+9," ");
                    if(tmpstr.Length>0)lst.Add(tmpstr);
                }
                return lst; 
            }
            return null;
        }

2,计算每个场景时长,循环截取各个场景的视频

private void button2_Click(object sender, EventArgs e)
        {
            if (textbox.Text.Length < 1)
            {
                MessageBox.Show("请选择文件!!!");
                return;
            }
            count=0;

            logno("查询视频各个场景时间起点");
            int n = (int)numericUpDown1.Value;
            float f = n*1.0f / 100;
            double duration;
            List list = FFMEPG.GetVideoSenueTime(textbox.Text, f,out duration);
            if (list == null || list.Count < 1)
            {
                log("出错,没有找点场景起点");
                return;
            }
            log("找到"+list.Count+"段场景...");
            logno("开始导出各个场景视频");            
            string file;
            double begin = 0;
            double end;
            double d;
            int i = 0;
            for (; i < list.Count; i++)
            {
                end = double.Parse(list[i]);
                d = end - begin;
                file = FFMEPG.CutFromTime(textbox.Text, begin.ToString(), d.ToString(), i + ".mp4");
                if (file.Length > 0)
                    log("导出文件" + i + ".mp4,From:" + begin + ",To:" + end);
                else
                    log("导出文件" + i + ".mp4 失败");
                begin = end;
            }
            if (duration > 0)
            {
                file = FFMEPG.CutFromTime(textbox.Text, begin.ToString(), (duration-begin).ToString(), i + ".mp4");
                if (file.Length > 0)
                    log("导出文件" + i + ".mp4,From:" + begin + ",To:" + duration);
                else
                    log("导出文件" + i + ".mp4 失败");
            }
        }

这是操作按钮事件,在获取场景pts_time的时候,返回了视频总时长,这个是因为pts_time只是表示上一个场景终点,本场景起点,当不是最后一个场景时,两个pts_time相减就得出了场景长度,如果是最后一个pts_time,就无法用这种方法,所以返回的总时长减去最后的pts_time,就得到了最后一个场景长度。

3、截取视频场景函数FFMPEG.CutFromTime

获取到场景起止点长度都已经搞点,我先照常的截取视频来操作,如下命令

ffmpeg -i aaa.mp4 -ss 0 -t 1.23333 -c:v copy output.mp4c#根据场景不同来拆分视频_第1张图片

剪出的来视频,是找到了交接点,但是多截取了一帧,有些多截取了两帧,查询百度时,得到了很多解释,最后,找到终极答案,修改命令如下:

ffmpeg -i aaa.mp4 -max_muxing_queue_size 1024 -ss 0 -t 1.069 -strict -2 -keyint_min 8 -g 8 -sc_threshold 0 output.mp4

测试也确实正常,完美分割。

c#根据场景不同来拆分视频_第2张图片

代码:

public static string CutFromTime(string OriginFile/*视频源文件*/, string startTime/*开始时间*/, string durationTime/*结束时间*/,string DstFile)
        {
            //精确剪辑命令
            //ffmpeg -i aaa.mp4 -max_muxing_queue_size 1024 -ss 0 -t 1.069 -strict -2 -keyint_min 8 -g 8 -sc_threshold 0 output.mp4
            string strCmd = " -ss " + startTime
                + " -i " + OriginFile
                + " -t " + durationTime
                + " -max_muxing_queue_size 1024"
                + " -strict -2 -keyint_min 8 -g 8 -sc_threshold 0"
                + " " + DstFile + " -y ";

            RunCmd(strCmd);

            if (System.IO.File.Exists(DstFile))
            {
                return DstFile;
            }
            return "";
        }

 4、最后一键完美将视频按场景分解成8个片断

c#根据场景不同来拆分视频_第3张图片

在剪映里原视频与场景视频拼合对比,场景视频播放基本是连续的,丢帧那是肯定的。。。

c#根据场景不同来拆分视频_第4张图片

 源码下载:https://download.csdn.net/download/xchenbb/87610784

你可能感兴趣的:(【c#,音视频】,音视频,c#,ffmpeg)