VideoHelp Forum
+ Reply to Thread
Results 1 to 10 of 10
Thread
  1. I need to trim a video without encoding into sections that later i can put back together with the concat demuxer of ffmpeg, in a way that when i join the video i have the same lenght of the original file. I understand that to cut precisely without encoding it is necessary to cut at keyframes, i'm using this command to get the keyframes:

    ffprobe -loglevel error -select_streams v:0 -show_entries packet=pts_time,flags -of csv=print_section=0 -input | awk -F',' '/K/ {print $1}'

    and they also correspond to the times that Avidemux reports for the keyframes. I'm using this command to trim the source:

    ffmpeg -ss 00:0:2.4607 -noaccurate_seek -avoid_negative_ts 1 -i input.mp4 -to 00:0:3.545 -c copy /content/out.mp4


    But it always ends up with different durations. I would expect that if i cut between keyframe at time 2.460792 and keyframe at time 3.545208 i would get a video of lenght that is the difference between those two times, but it is never the case.

    Any help? thanks
    Quote Quote  
  2. Member
    Join Date
    Feb 2004
    Location
    Virginia
    Search Comp PM
    I've found that the keyframe readings using Avidemux are 0.066 seconds larger than those when using VirtualDub2, at least on MP4 video files. The VirtualDub2 keyframe readings match exactly the ffprobe keyframe readings. If you use the keyframes from VirtualDub2 in your ffmpeg cutter command, you should get the correct length. However one weird note: You have to subtract exactly 0.1 second from all the keyframe readings to get the exact cut points even when starting at 0.0 which becomes -0.1 seconds. It's easy to find your keyframe cut point using 2 running copies of VirtualDub2 working on the same video. I've been cutting and reassembling my original multiple episode Doctor Who videos into single videos so I don't have to listen to all the theme music between the episodes. This means matching scenes between episodes which seems to work well. Oh, by the way I do have a life! I just like working on little projects like this. One last note: Using ffmpeg to cut your videos, keeps the embedded subtitles where using Avidemux or VirtualDub2 won't.

    Clarification: The above 0.066 seconds difference was measured on a video with a frame rate of 29.97. It's really a 2 frame difference ( 2 / 29.97 ) = 0.06673 seconds. For a 59.94 frame rate video, it's a difference of 0.03337 seconds. For a 23.976 frame rate video, it's a difference of 0.08345 seconds.
    Last edited by pjcrown; 15th Oct 2020 at 19:03.
    Quote Quote  
  3. Member
    Join Date
    Nov 2020
    Location
    mauritius
    Search Comp PM
    Best way is to use SolveigMM Video Splitter
    Quote Quote  
  4. When there are no subtitles Avidemux is excelent, always keep in mind that your cut needs to start on an I Frame and must end on an I or P frame, wo when in avidemux up and down keys go to I frames left and right keys to every frame, while navigating to an endpoint of your cut make sure that's it on a P frame.
    Quote Quote  
  5. Member Budman1's Avatar
    Join Date
    Jul 2012
    Location
    NORTHWEST ILLINOIS, USA
    Search Comp PM
    If you cut from key frame through the 'P' frame before the next Key frame, FFmpeg will cut correctly, If you include the next Key frame or more as the end point, FFMpeg will include, alter PTS, or exclude frames. Case in point Cut IBBBPBBBPBBBPIBB and you will get IBBBPBBBPBBBPIBP but the timings for the last couple of frames will not be the correct length of the frame duration.

    Also, If the audio and video length do not match, the total size of the video will be altered to match the longest one.

    If you use FFMpeg correctly it is a great tool:
    1. Cut from I frame THROUGH the last frame before the next key frame (Usually a 'P' frame for Closed GOP)
    2. Make sure the last frames PTS's are separated by equal amounts (25fps = .04, .04, .04)
    3. these can be checked using a batch file that list each frame:
    4. Using -vframes tends to be more accurate.

    Code:
    ffmpeg -ss 2.240000 -i "C:\Mixed GOP Black Yellow Test Video.mp4" -vframes 56 -c:v copy -c:a copy -y "C:\0_0_Mixed GOP Blak Yellow Test Video.mp4"
    Test Video:
    Image
    [Attachment 55860 - Click to enlarge]


    Example cutting 56 frames (28 +28):
    Image
    [Attachment 55857 - Click to enlarge]


    Original Frame times:
    Image
    [Attachment 55858 - Click to enlarge]


    End result:
    Image
    [Attachment 55859 - Click to enlarge]


    Notice the last frame does not include the 3rd segments start time of 4.48 (4.48-2.24=224) End segment is 2.20 for .04 but does not include 2.24 frame time

    Time always:
    General
    Complete name : C:\0_0_Mixed GOP Black Yellow Test Video.mp4
    Format : MPEG-4
    Format profile : Base Media
    Codec ID : isom (isom/iso2/avc1/mp41)
    File size : 28.7 KiB
    Duration : 2 s 240 ms
    Overall bit rate mode : Variable
    Overall bit rate : 105 kb/s
    Writing application : Lavf58.47.100
    Quote Quote  
  6. Member
    Join Date
    Dec 2005
    Location
    Canada
    Search Comp PM
    @Budman1

    3. these can be checked using a batch file that list each frame
    I can't find one - maybe because I'm not using the corrrect search pattern.
    Any suggestions??
    Quote Quote  
  7. Member Budman1's Avatar
    Join Date
    Jul 2012
    Location
    NORTHWEST ILLINOIS, USA
    Search Comp PM
    You need to have ffprobe in the same folder as the CMD file or use the pathed ffprobe on your computer.
    It creates a csv file and a simplified text file (like the 'original frame times image in my post). It takes a while to scan
    a large video but that can be shortened with multiple ffprobe statements to read the beginning few frames and the end frames.
    Try it on a short filed to ssee its performance

    This is the drag and drop CMD file I use. Create is as a CMD file and then drag/drop your video onto it.:

    Code:
    setlocal enabledelayedexpansion
    @echo off
    ::for %%a in (*.mp4,*.mpg,*.flv) Do (
    for %%a in ("%~nx1") Do (
    set /a count=0
    cd %%~dpa
    echo frame,media_type,stream_index,key_frame,pkt_pts,pkt_pts_time,pkt_dts,pkt_dts_time,best_effort_timestamp,^
    best_effort_timestamp_time,pkt_duration,pkt_duration_time,pkt_pos,pkt_size,^
    Width,Height,pix_fmt,sample_aspect_ratio,pict_type,coded_picture_number,display_picture_number,^
    interlaced_frame,top_field_first,repeat_pict,color_range,color_space,color_primaries,color_transfer,^
    chroma_location > "%%~na_ffprobe.csv"
    echo No.  pts_time  type > "%%~na_AllFrames.txt"
    ver > nul
    set /a Number=0
    ffprobe.exe -v quiet -select_streams v:0 -print_format csv -show_entries frame "%%~nxa"  >> "%%~na_ffprobe.csv"
    for /F "tokens=4,6,18,19 delims=," %%b in ('findstr "video" "%%~na_ffprobe.csv"') do (
    set "x=%%b"
    set "y=%%c"
    set "z=%%d"
    set "w=%%e"
    set "v=%%f"
    set "u=%%g"
    set sort=!y:~0,-7!
    ::if !sort! GEQ 0 if !sort! LEQ 10 echo !count!  !y:~0,13!  !x! !w:~0,1! >> "%%~na_AllFrames.txt"
    echo !count! !y:~0,13!  !x! !w:~0,1! >> "%%~na_AllFrames.txt"
    set /a count+=1
     set /a ekko=count%%100
     if !ekko! EQU 0 echo Frame !count! processed
    )
    )
    rem pause
    Quote Quote  
  8. Member Budman1's Avatar
    Join Date
    Jul 2012
    Location
    NORTHWEST ILLINOIS, USA
    Search Comp PM
    Shortened version asks for how many frames at the beginning to list (Minimum 5 due to FFprobe characteristics) and
    How long the file is in seconds. The way FFprobe seeks the end of a file is to seek to the last 'I' frame and since this
    may be 250 frames to the end for maximum GOP size, the count is hard coded to 250. Its not perfect like you can get
    in a windows GUI version but its a lot faster than scanning a long video.

    Code:
    setlocal enabledelayedexpansion
    @echo off
    ::for %%a in (*.mp4,*.mpg,*.flv) Do (
    set /P searchno=set number of frames to search 
    set /P duration=set length in seconds
    ::set /a searchno=10
    for %%a in ("%~nx1") Do (
    set /a count=0
    cd %%~dpa
    echo frame,media_type,stream_index,key_frame,pkt_pts,pkt_pts_time,pkt_dts,pkt_dts_time,best_effort_timestamp,^
    best_effort_timestamp_time,pkt_duration,pkt_duration_time,pkt_pos,pkt_size,^
    Width,Height,pix_fmt,sample_aspect_ratio,pict_type,coded_picture_number,display_picture_number,^
    interlaced_frame,top_field_first,repeat_pict,color_range,color_space,color_primaries,color_transfer,^
    chroma_location > "%%~na_ffprobe.csv"
    echo No.  pts_time  type > "%%~na_AllFrames.txt"
    ver > nul
    set /a Number=0
    ffprobe.exe  -v quiet -select_streams v:0 -print_format csv -show_entries frame -read_intervals %%+#!searchno! "%%~nxa"  >> "%%~na_ffprobe.csv"
    ffprobe.exe  -v quiet -select_streams v:0 -print_format csv -show_entries frame -read_intervals !duration!%%+#250 "%%~nxa"  >> "%%~na_ffprobe.csv"
    for /F "tokens=4,6,18,19 delims=," %%b in ('findstr "video" "%%~na_ffprobe.csv"') do (
    set "x=%%b"
    set "y=%%c"
    set "z=%%d"
    set "w=%%e"
    set "v=%%f"
    set "u=%%g"
    set sort=!y:~0,-7!
    ::if !sort! GEQ 0 if !sort! LEQ 10 echo !count!  !y:~0,13!  !x! !w:~0,1! >> "%%~na_AllFrames.txt"
    echo !count! !y:~0,13!  !x! !w:~0,1! >> "%%~na_AllFrames.txt"
    set /a count+=1
     set /a ekko=count%%100
     if !ekko! EQU 0 echo Frame !count! processed
    )
    )
    Quote Quote  
  9. Member
    Join Date
    Dec 2005
    Location
    Canada
    Search Comp PM
    @Budman1

    ...and thank you for sharing - they both work!
    Quote Quote  
  10. @Budman1 thanks a lot for that script, here's a revised .cmd version which avoids extra spaces:
    Code:
    setlocal enabledelayedexpansion
    @echo off
    ::for %%a in (*.mp4,*.mpg,*.flv) Do (
    for %%a in ("%~nx1") Do (
    set /a count=0
    cd %%~dpa
    echo frame,media_type,stream_index,key_frame,pkt_pts,pkt_pts_time,pkt_dts,pkt_dts_time,best_effort_timestamp,^
    best_effort_timestamp_time,pkt_duration,pkt_duration_time,pkt_pos,pkt_size,^
    Width,Height,pix_fmt,sample_aspect_ratio,pict_type,coded_picture_number,display_picture_number,^
    interlaced_frame,top_field_first,repeat_pict,color_range,color_space,color_primaries,color_transfer,^
    chroma_location> "%%~na_ffprobe.csv"
    echo No.  pts_time  type> "%%~na_AllFrames.txt"
    ver > nul
    set /a Number=0
    ffprobe.exe -v quiet -select_streams v:0 -print_format csv -show_entries frame "%%~nxa"  >> "%%~na_ffprobe.csv"
    for /F "tokens=4,6,18,19 delims=," %%b in ('findstr "video" "%%~na_ffprobe.csv"') do (
    set "x=%%b"
    set "y=%%c"
    set "z=%%d"
    set "w=%%e"
    set "v=%%f"
    set "u=%%g"
    set sort=!y:~0,-7!
    ::if !sort! GEQ 0 if !sort! LEQ 10 echo !count!  !y:~0,13!  !x! !w:~0,1!>> "%%~na_AllFrames.txt"
    echo !count! !y:~0,13! !x! !w:~0,1!>> "%%~na_AllFrames.txt"
    set /a count+=1
     set /a ekko=count%%100
     if !ekko! EQU 0 echo Frame !count! processed
    )
    )
    rem pause
    This is the shell version:
    Code:
    #!/bin/sh
    
    if [ $# -eq 0 ]; then
        printf "Usage: %s file [file...]\n" "$0"
        exit 1
    fi
    
    for a in "$@"; do
        # Check if the file exists
        if [ ! -f "$a" ]; then
            printf "File not found: %s\n" "$a"
            continue
        fi
    
        cd "$(dirname "$a")" || { printf "Failed to change directory to %s\n" "$(dirname "$a")"; continue; }
    
        base_name="$(basename "${a%.*}")"
    
        {
        printf "frame,media_type,stream_index,key_frame,pkt_pts,pkt_pts_time,pkt_dts,pkt_dts_time,best_effort_timestamp,"
        printf "best_effort_timestamp_time,pkt_duration,pkt_duration_time,pkt_pos,pkt_size,"
        printf "Width,Height,pix_fmt,sample_aspect_ratio,pict_type,coded_picture_number,display_picture_number,"
        printf "interlaced_frame,top_field_first,repeat_pict,color_range,color_space,color_primaries,color_transfer,"
        printf "chroma_location\r\n"
        } > "${base_name}_ffprobe.csv"
    
        ffprobe -v quiet -select_streams v:0 -print_format csv -show_entries frame "$a" | sed 's/$/\r/' >> "${base_name}_ffprobe.csv"
    
        count=0
    
        printf "No.  pts_time  type\r\n" > "${base_name}_AllFrames.txt"
    
        grep "video" "${base_name}_ffprobe.csv" | cut -d, -f4,6,18,19 | while IFS=, read -r b d f h j l; do
            printf "%s %s %s %s\r\n" "$count" "${d:0:13}" "$b" "${h:0:1}" >> "${base_name}_AllFrames.txt"
            count=$((count + 1))
            if [ $((count % 100)) -eq 0 ]; then
                printf "Frame %d processed\n" "$count"
            fi
        done
    done
    Quote Quote  



Similar Threads

Visit our sponsor! Try DVDFab and backup Blu-rays!