slhck / ffmpeg-black-split

Split a video based on black periods
Other
12 stars 2 forks source link

More accurate cutting #1

Closed slhck closed 2 months ago

slhck commented 2 years ago

Currently, content periods may include a black frame at the end due to the way the cutting extends to the beginning of the next period. Without frame-accurate cutting, it will be somewhat challenging to find the exact end timestamp though. We could simply subtract 0.001 seconds?

Have to investigate further …

Unshackle8078 commented 2 months ago

This would be a nice enhancement (or fix). One which I recently cut into two had a little bit of the pre-black frame content in the post-black frame split file if that makes sense.

It's not clear in the period list whether millisecond resolution is supported (either with the detection or the cutting). The blackdetect filter itself definitely has millisecond resolution.

If for example -ss & -t are using 706 rather than 706.863, that could be a reason for accuracy loss.

I did a quick side by side running the same manual blackdetect command and split command and ended up with a more accurate split when using milliseconds.

The blackdetect filter for example outputs lines like this: [blackdetect @ xyz] black_start=705.872 black_end:706.372 black_duration:0.584

It would be great if all of these precision points are carried through.

slhck commented 2 months ago

So there is nothing truncating these identified periods to the nearest second. The issue is that a) frame accurate seeking is not always possible, especially when not re-encoding the output file, and b) the output may include one black frame, because the periods do not overlap, and so we would have to subtract one frame from the next section.

Out of curiosity, can you please show the exact ffmpeg commands and output you got, and the output from my tool with the verbose option turned on?

Unshackle8078 commented 2 months ago

So there is nothing truncating these identified periods to the nearest second. The issue is that a) frame accurate seeking is not always possible, especially when not re-encoding the output file, and b) the output may include one black frame, because the periods do not overlap, and so we would have to subtract one frame from the next section.

Understood however in this case I took the same source file and split using the same methods but ensured the millisecond was passed in. Including some black frames makes sense since they are in there and it isn't cutting.

Out of curiosity, can you please show the exact ffmpeg commands and output you got, and the output from my tool with the verbose option turned on?

Can verbose mode be enabled on the API method?

The two commands I ran: ffmpeg.exe -i '.\something.mkv' -vf "blackdetect=d=0.50:pix_th=0.10" -an -f null -

frame=    0 fps=0.0 q=-0.0 size=       0kB time=00:00:00.00 bitrate=N/A speed=   0x
frame=  490 fps=0.0 q=-0.0 size=N/A time=00:00:20.43 bitrate=N/A speed=38.8x
frame=  936 fps=844 q=-0.0 size=N/A time=00:00:39.03 bitrate=N/A speed=35.2x
[blackdetect @ ...] black_start:56.098 black_end:56.682 black_duration:0.584
frame= 1516 fps=942 q=-0.0 size=N/A time=00:01:03.22 bitrate=N/A speed=39.3x
frame= 2033 fps=964 q=-0.0 size=N/A time=00:01:24.79 bitrate=N/A speed=40.2x
frame= 2500 fps=939 q=-0.0 size=N/A time=00:01:44.27 bitrate=N/A speed=39.2x
frame= 3072 fps=946 q=-0.0 size=N/A time=00:02:08.12 bitrate=N/A speed=39.4x
frame= 3539 fps=944 q=-0.0 size=N/A time=00:02:27.60 bitrate=N/A speed=39.4x
frame= 4074 fps=958 q=-0.0 size=N/A time=00:02:49.91 bitrate=N/A speed=  40x
frame= 4520 fps=943 q=-0.0 size=N/A time=00:03:08.52 bitrate=N/A speed=39.3x
frame= 5017 fps=946 q=-0.0 size=N/A time=00:03:29.25 bitrate=N/A speed=39.5x
frame= 5501 fps=931 q=-0.0 size=N/A time=00:03:49.43 bitrate=N/A speed=38.8x
frame= 6105 fps=952 q=-0.0 size=N/A time=00:04:14.62 bitrate=N/A speed=39.7x
frame= 6538 fps=946 q=-0.0 size=N/A time=00:04:32.68 bitrate=N/A speed=39.5x
frame= 6975 fps=941 q=-0.0 size=N/A time=00:04:50.91 bitrate=N/A speed=39.2x
frame= 7480 fps=945 q=-0.0 size=N/A time=00:05:11.97 bitrate=N/A speed=39.4x
frame= 7891 fps=938 q=-0.0 size=N/A time=00:05:29.12 bitrate=N/A speed=39.1x
frame= 8376 fps=934 q=-0.0 size=N/A time=00:05:49.34 bitrate=N/A speed=  39x
frame= 8731 fps=910 q=-0.0 size=N/A time=00:06:04.15 bitrate=N/A speed=37.9x
frame= 9215 fps=905 q=-0.0 size=N/A time=00:06:24.34 bitrate=N/A speed=37.7x
frame= 9693 fps=907 q=-0.0 size=N/A time=00:06:44.27 bitrate=N/A speed=37.8x
frame=10189 fps=904 q=-0.0 size=N/A time=00:07:04.96 bitrate=N/A speed=37.7x
frame=10592 fps=900 q=-0.0 size=N/A time=00:07:21.77 bitrate=N/A speed=37.5x
frame=11065 fps=897 q=-0.0 size=N/A time=00:07:41.50 bitrate=N/A speed=37.4x
frame=11518 fps=894 q=-0.0 size=N/A time=00:08:00.39 bitrate=N/A speed=37.3x
frame=12095 fps=898 q=-0.0 size=N/A time=00:08:24.46 bitrate=N/A speed=37.4x
frame=12638 fps=902 q=-0.0 size=N/A time=00:08:47.10 bitrate=N/A speed=37.6x
frame=13133 fps=901 q=-0.0 size=N/A time=00:09:07.75 bitrate=N/A speed=37.6x
frame=13300 fps=874 q=-0.0 size=N/A time=00:09:14.72 bitrate=N/A speed=36.4x
frame=13900 fps=883 q=-0.0 size=N/A time=00:09:39.74 bitrate=N/A speed=36.8x
frame=14324 fps=882 q=-0.0 size=N/A time=00:09:57.43 bitrate=N/A speed=36.8x
frame=14894 fps=889 q=-0.0 size=N/A time=00:10:21.20 bitrate=N/A speed=37.1x
frame=15304 fps=887 q=-0.0 size=N/A time=00:10:38.30 bitrate=N/A speed=  37x
frame=15851 fps=892 q=-0.0 size=N/A time=00:11:01.11 bitrate=N/A speed=37.2x
frame=16445 fps=895 q=-0.0 size=N/A time=00:11:25.89 bitrate=N/A speed=37.3x
[blackdetect @ ...] black_start:705.872 black_end:706.372 black_duration:0.5 <<<<<< split
frame=17059 fps=904 q=-0.0 size=N/A time=00:11:51.50 bitrate=N/A speed=37.7x
frame=17345 fps=895 q=-0.0 size=N/A time=00:12:03.43 bitrate=N/A speed=37.3x
frame=17805 fps=895 q=-0.0 size=N/A time=00:12:22.61 bitrate=N/A speed=37.3x
frame=18335 fps=898 q=-0.0 size=N/A time=00:12:44.72 bitrate=N/A speed=37.5x
frame=18774 fps=898 q=-0.0 size=N/A time=00:13:02.99 bitrate=N/A speed=37.4x
frame=19185 fps=896 q=-0.0 size=N/A time=00:13:20.17 bitrate=N/A speed=37.4x
frame=19738 fps=898 q=-0.0 size=N/A time=00:13:43.23 bitrate=N/A speed=37.5x
frame=20298 fps=891 q=-0.0 size=N/A time=00:14:06.59 bitrate=N/A speed=37.2x
frame=20775 fps=893 q=-0.0 size=N/A time=00:14:26.49 bitrate=N/A speed=37.2x
frame=21367 fps=897 q=-0.0 size=N/A time=00:14:51.18 bitrate=N/A speed=37.4x
frame=21876 fps=894 q=-0.0 size=N/A time=00:15:12.41 bitrate=N/A speed=37.3x
frame=22431 fps=898 q=-0.0 size=N/A time=00:15:35.55 bitrate=N/A speed=37.5x
frame=22786 fps=891 q=-0.0 size=N/A time=00:15:50.36 bitrate=N/A speed=37.1x
frame=23338 fps=895 q=-0.0 size=N/A time=00:16:13.38 bitrate=N/A speed=37.3x
frame=23807 fps=895 q=-0.0 size=N/A time=00:16:32.95 bitrate=N/A speed=37.3x
frame=24237 fps=894 q=-0.0 size=N/A time=00:16:50.88 bitrate=N/A speed=37.3x
frame=24802 fps=898 q=-0.0 size=N/A time=00:17:14.45 bitrate=N/A speed=37.5x
frame=25150 fps=894 q=-0.0 size=N/A time=00:17:28.96 bitrate=N/A speed=37.3x
frame=25660 fps=896 q=-0.0 size=N/A time=00:17:50.23 bitrate=N/A speed=37.4x
frame=25793 fps=882 q=-0.0 size=N/A time=00:17:55.78 bitrate=N/A speed=36.8x
frame=26080 fps=877 q=-0.0 size=N/A time=00:18:07.75 bitrate=N/A speed=36.6x
frame=26630 fps=879 q=-0.0 size=N/A time=00:18:30.69 bitrate=N/A speed=36.7x
frame=27154 fps=881 q=-0.0 size=N/A time=00:18:52.54 bitrate=N/A speed=36.7x
frame=27526 fps=878 q=-0.0 size=N/A time=00:19:08.06 bitrate=N/A speed=36.6x
frame=27899 fps=872 q=-0.0 size=N/A time=00:19:23.62 bitrate=N/A speed=36.4x
frame=28338 fps=872 q=-0.0 size=N/A time=00:19:41.93 bitrate=N/A speed=36.4x
frame=28754 fps=872 q=-0.0 size=N/A time=00:19:59.28 bitrate=N/A speed=36.4x
frame=29248 fps=873 q=-0.0 size=N/A time=00:20:19.88 bitrate=N/A speed=36.4x
frame=29670 fps=873 q=-0.0 size=N/A time=00:20:37.48 bitrate=N/A speed=36.4x
frame=30212 fps=876 q=-0.0 size=N/A time=00:21:00.09 bitrate=N/A speed=36.5x
frame=30800 fps=878 q=-0.0 size=N/A time=00:21:24.61 bitrate=N/A speed=36.6x
frame=31285 fps=878 q=-0.0 size=N/A time=00:21:44.84 bitrate=N/A speed=36.6x
[blackdetect @ ...] black_start:1320.99 black_end:1321.49 black_duration:0.501
frame=31937 fps=884 q=-0.0 size=N/A time=00:22:12.03 bitrate=N/A speed=36.9x
frame=32404 fps=889 q=-0.0 Lsize=N/A time=00:22:31.47 bitrate=N/A speed=37.1x

ffmpeg.exe -ss 706.372 -i '.\something.mkv' -c copy -map 0 '.\output_split_2.mkv'

If splitting the first out it would be ffmpeg.exe -i '.\something.mkv' -t 705.872 -c copy -map 0 '.\output_split_1.mkv'

frame=15474 fps=336 q=-1.0 Lq=-1.0 size=  571541kB time=00:10:45.15 bitrate=7257.2kbits/s speed=  14x
video:329230kB audio:237057kB subtitle:74kB other streams:0kB global headers:0kB muxing overhead: 0.914479%

Output from this handy utility:

.\ffmpeg-black-split.exe 'something.mkv' -d 0.50 -t 0.2 -p -v -o output_location
DEBUG - Running ffmpeg command: ffmpeg -hide_banner -y -i 'something.mkv' -vf blackdetect=black_min_duration=0.5:picture_black_ratio_th=0.98:pixel_black_th=0.2 -an -f null -
DEBUG - Black and content periods detected:
{
  "black_periods": [
    {
      "start": 56.0,
      "end": 56.0,
      "duration": 0.0
    },
    {
      "start": 705.0,
      "end": 706.0,
      "duration": 0.0
    },
    {
      "start": 1320.0,
      "end": 1321.0,
      "duration": 0.0
    }
  ],
  "content_periods": [
    {
      "start": 0.0,
      "end": 56.0
    },
    {
      "start": 56.0,
      "end": 705.0
    },
    {
      "start": 706.0,
      "end": 1320.0
    },
    {
      "start": 1321.0,
      "end": null
    }
  ]
}
DEBUG - Running ffmpeg command: ffmpeg -hide_banner -y -ss 0.0 -i 'something.mkv' -t 56.0 -c copy -map 0 'something_0.0-56.0.mkv'
DEBUG - Running ffmpeg command: ffmpeg -hide_banner -y -ss 56.0 -i 'something.mkv' -t 649.0 -c copy -map 0 'something_56.0-705.0.mkv'
DEBUG - Running ffmpeg command: ffmpeg -hide_banner -y -ss 706.0 -i 'something.mkv' -t 614.0 -c copy -map 0 'something_706.0-1320.0.mkv'
DEBUG - Running ffmpeg command: ffmpeg -hide_banner -y -ss 1321.0 -i '.mkv' -c copy -map 0 'something_1321.0-.mkv'

This is the output with the API method:

Black Periods:
[
    {
        "start": 56.0,
        "end": 56.0,
        "duration": 0.0
    },
    {
        "start": 705.0,
        "end": 706.0,
        "duration": 0.0
    }, // should read: [blackdetect @ ...] black_start:705.872 black_end:706.372 black_duration:0.5
    {
        "start": 1320.0,
        "end": 1321.0,
        "duration": 0.0
    }
]
Filtered Periods:
[
    {
        "start": 705.0,
        "end": 706.0,
        "duration": 0.0
    }
]

Resulting files, second half of split file:

From this tool (0.4.0 or 0.5.0): Duration : 10 min 46 s (too long by a small amount) Time code of first frame : 00:11:44;00 (too early by a small amount)

Manually with ffmpeg: Duration : 10 min 45 s Time code of first frame : 00:11:45;10

Internally something is perhaps not parsing the whole values then if no truncation is occurring.

slhck commented 2 months ago

Thanks for these outputs. It does look like something is truncated. I will investigate!

Unshackle8078 commented 2 months ago

You've probably already got a handle on it but the source of this issue appears to be from https://github.com/slhck/ffmpeg-black-split/blob/0cbf28d1e6634541c6f474ecb4c375b9952fbf2c/ffmpeg_black_split/_black_split.py#L123

Where r"black_start:(\d+[\.\d+]?)" isn't picking up the trailing digits after the period. It appears the capture brackets ignore the modifiers like +.

Adjusting it to r"black_start:(\d+\.\d+)" for example seems to do the trick but may not be the best regex.

slhck commented 2 months ago

You're right, a fix is published in v0.5.1, see 78a541bc0cd32362bc8fa9bd2ad655ec8d75aee1