c# split video according to different scenes

This feature is also my personal need.

Requirement: I downloaded several short videos of the same product introduction in dy, and each video is composed of multiple scenes. At this time, I need to extract a scene from each video to combine into a new video.

This requires the use of [Split video according to the scene].

Let’s take a look at the following animation to perceive the scene:

The scene is: a continuous video is a scene, and there are 6 scenes in the above animation.

Solution: The ffmpeg command calculates the start time stamp of the new scene, exports it to log.txt, c# parses the log.txt, extracts the pts_time and total length of each scene, and then loops to capture the video of each scene.

open whole. . . . .

1. Extract the pts_time and total length of each scene

Command: ffmpeg -i aa.mp4 -filter:v “select=’gt(scene,0.15)’,showinfo” -f null – 1> log.txt 2> & amp;1

The key data of the scene starting point in the log.txt file:

gt(scene,0.15), here is 0.15, my understanding is that the surface difference value between two frames is 0-1, 1 is completely different, I tested that 0.15 can detect each scene, you can according to your own needs fine-tuning, code

public static List<string> 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> & amp;1 -y", filename, flag, logfile);
            //Execute the ffmpeg command
            RunCmd(@cmd);
            duration = 0;
            if (File. Exists(logfile))
            {
                List<string> lst = new List<string>();
                string tmpstr;
                int n;
                foreach (string line in System.IO.File.ReadLines(logfile))
                {
                    // first find the total duration
                    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. Calculate the duration of each scene, and loop through the video of each scene

private void button2_Click(object sender, EventArgs e)
        {
            if (textbox. Text. Length < 1)
            {
                MessageBox.Show("Please select a file!!!");
                return;
            }
            count=0;

            logno("Query the time starting point of each scene of the video");
            int n = (int)numericUpDown1. Value;
            float f = n*1.0f / 100;
            double duration;
            List<string> list = FFMEPG.GetVideoSenueTime(textbox.Text, f,out duration);
            if (list == null || list. Count < 1)
            {
                log("Error, did not find the starting point of the scene");
                return;
            }
            log("Found" + list.Count + "segment scene...");
            logno("Start to export each scene video");
            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("Export file" + i + ".mp4, From: " + begin + ", To: " + end);
                else
                    log("Export file" + i + ".mp4 failed");
                begin = end;
            }
            if (duration > 0)
            {
                file = FFMEPG.CutFromTime(textbox.Text, begin.ToString(), (duration-begin).ToString(), i + ".mp4");
                if (file. Length > 0)
                    log("Export file" + i + ".mp4, From: " + begin + ", To: " + duration);
                else
                    log("Export file" + i + ".mp4 failed");
            }
        }

This is the operation button event. When the scene pts_time is obtained, the total video duration is returned. This is because pts_time only indicates the end of the previous scene and the starting point of this scene. When it is not the last scene, the two pts_time are subtracted to get Scene length, if it is the last pts_time, this method cannot be used, so subtract the last pts_time from the total returned duration to get the last scene length.

3. Intercept video scene function FFMPEG.CutFromTime

The length of the start and end points of the scene has been obtained. I will first intercept the video as usual, and the following command

ffmpeg -i aaa.mp4 -ss 0 -t 1.23333 -c:v copy output.mp4

The clipped video has found the transition point, but one extra frame was intercepted, and some of them were intercepted by two extra frames. When I queried Baidu, I got a lot of explanations. Finally, I found the ultimate answer and modified the command as follows:

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

The test is indeed normal, perfect segmentation.

code:

public static string CutFromTime(string OriginFile/*video source file*/, string startTime/*start time*/, string durationTime/*end time*/,string DstFile)
        {
            //Precise clip command
            //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. The last button perfectly decomposes the video into 8 segments according to the scene

Comparing the original video and the scene video in the video clip, the scene video playback is basically continuous, and frame loss is certain. . .

The complete project has been uploaded to my resources. . .