Oto dwa skrypty PowerShell do dzielenia długich filmów na mniejsze rozdziały według czarnych scen.
Zapisz je jako Detect_black.ps1 i Cut_black.ps1. Pobierz ffmpeg dla Windows i podaj skryptowi ścieżkę do pliku ffmpeg.exe i folderu wideo w sekcji opcji.
Oba skrypty nie dotykają istniejących plików wideo, pozostają nietknięte.
Otrzymasz jednak kilka nowych plików w tym samym miejscu, w którym znajdują się Twoje filmy wejściowe
- Plik dziennika na wideo z wyjściem konsoli dla obu używanych komend ffmpeg
- Plik CSV na wideo ze wszystkimi znacznikami czasu czarnych scen do ręcznego dostrajania
- Kilka nowych filmów w zależności od liczby wcześniej wykrytych czarnych scen
Pierwszy skrypt do uruchomienia: Detect_black.ps1
### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe" # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*" # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4") # Set which file extensions should be processed
$dur = 4 # Set the minimum detected black duration (in seconds)
$pic = 0.98 # Set the threshold for considering a picture as "black" (in percent)
$pix = 0.15 # Set the threshold for considering a pixel "black" (in luminance)
### Main Program ______________________________________________________________________________________________________
foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){
### Set path to logfile
$logfile = "$($video.FullName)_ffmpeg.log"
### analyse each video with ffmpeg and search for black scenes
& $ffmpeg -i $video -vf blackdetect=d=`"$dur`":pic_th=`"$pic`":pix_th=`"$pix`" -an -f null - 2> $logfile
### Use regex to extract timings from logfile
$report = @()
Select-String 'black_start:.*black_end:' $logfile | % {
$black = "" | Select start, end, cut
# extract start time of black scene
$start_s = $_.line -match '(?<=black_start:)\S*(?= black_end:)' | % {$matches[0]}
$start_ts = [timespan]::fromseconds($start_s)
$black.start = "{0:HH:mm:ss.fff}" -f ([datetime]$start_ts.Ticks)
# extract duration of black scene
$end_s = $_.line -match '(?<=black_end:)\S*(?= black_duration:)' | % {$matches[0]}
$end_ts = [timespan]::fromseconds($end_s)
$black.end = "{0:HH:mm:ss.fff}" -f ([datetime]$end_ts.Ticks)
# calculate cut point: black start time + black duration / 2
$cut_s = ([double]$start_s + [double]$end_s) / 2
$cut_ts = [timespan]::fromseconds($cut_s)
$black.cut = "{0:HH:mm:ss.fff}" -f ([datetime]$cut_ts.Ticks)
$report += $black
}
### Write start time, duration and the cut point for each black scene to a seperate CSV
$report | Export-Csv -path "$($video.FullName)_cutpoints.csv" –NoTypeInformation
}
Jak to działa
Pierwszy skrypt iteruje wszystkie pliki wideo, które pasują do określonego rozszerzenia i nie pasują do wzorca *_???.*
, ponieważ nazwano nowe rozdziały wideo <filename>_###.<ext>
i chcemy je wykluczyć.
Przeszukuje wszystkie czarne sceny i zapisuje znacznik czasu rozpoczęcia i czas trwania czarnej sceny do nowego pliku CSV o nazwie <video_name>_cutpoints.txt
To również oblicza punkty cięcia, jak pokazano: cutpoint = black_start + black_duration / 2
. Później wideo jest dzielone na segmenty według tych znaczników czasu.
Plik cutpoints.txt dla Twojego przykładowego wideo pokaże:
start end cut
00:03:56.908 00:04:02.247 00:03:59.578
00:08:02.525 00:08:10.233 00:08:06.379
Po uruchomieniu można ręcznie manipulować punktami cięcia. Po ponownym uruchomieniu skryptu wszystkie stare treści zostaną zastąpione. Zachowaj ostrożność podczas ręcznej edycji i zapisz swoją pracę w innym miejscu.
Dla przykładowego wideo komenda ffmpeg do wykrywania czarnych scen to
$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null
Istnieją 3 ważne liczby, które można edytować w sekcji opcji skryptu
d=4
oznacza, że wykrywane są tylko czarne sceny dłuższe niż 4 sekundy
pic_th=0.98
to próg uznawania obrazu za „czarny” (w procentach)
pix=0.15
ustawia próg uznawania piksela za „czarny” (w luminancji). Ponieważ masz stare filmy VHS, nie masz w nich całkowicie czarnych scen. Domyślna wartość 10 nie zadziała i musiałem nieznacznie zwiększyć próg
Jeśli coś pójdzie nie tak, sprawdź odpowiedni plik dziennika o nazwie <video_name>__ffmpeg.log
. Jeśli brakuje następujących linii, zwiększ liczby wymienione powyżej, aż wykryjesz wszystkie czarne sceny:
[blackdetect @ 0286ec80]
black_start:236.908 black_end:242.247 black_duration:5.33877
Drugi skrypt do uruchomienia: cut_black.ps1
### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe" # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*" # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4") # Set which file extensions should be processed
### Main Program ______________________________________________________________________________________________________
foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){
### Set path to logfile
$logfile = "$($video.FullName)_ffmpeg.log"
### Read in all cutpoints from *_cutpoints.csv; concat to string e.g "00:03:23.014,00:06:32.289,..."
$cuts = ( Import-Csv "$($video.FullName)_cutpoints.csv" | % {$_.cut} ) -join ","
### put together the correct new name, "%03d" is a generic number placeholder for ffmpeg
$output = $video.directory.Fullname + "\" + $video.basename + "_%03d" + $video.extension
### use ffmpeg to split current video in parts according to their cut points
& $ffmpeg -i $video -f segment -segment_times $cuts -c copy -map 0 $output 2> $logfile
}
Jak to działa
Drugi skrypt iteruje wszystkie pliki wideo w taki sam sposób, jak pierwszy skrypt. Odczytuje tylko wycięte znaczniki czasu z odpowiedniego cutpoints.txt
filmu.
Następnie zestawia odpowiednią nazwę pliku dla plików rozdziałów i nakazuje ffmpeg segmentację wideo. Obecnie filmy są dzielone na części bez ponownego kodowania (superszybkie i bezstratne). Z tego powodu mogą występować niedokładności 1-2s ze znacznikami czasu punktu przecięcia, ponieważ ffmpeg może wycinać tylko w klatkach kluczowych. Ponieważ po prostu kopiujemy i nie kodujemy ponownie, nie możemy samodzielnie wstawiać klatek kluczowych.
Poleceniem dla przykładowego wideo byłoby
$ffmpeg -i "Tape_10_3b.mp4" -f segment -segment_times "00:03:59.578,00:08:06.379" -c copy -map 0 "Tape_10_3b_(%03d).mp4"
Jeśli coś pójdzie nie tak, spójrz na odpowiedni plik ffmpeg.log
Bibliografia
Do zrobienia
Zapytaj OP, czy format CSV jest lepszy niż plik tekstowy jako plik punktu cięcia, abyś mógł edytować je w programie Excel nieco łatwiej
»Wdrożony
Zaimplementuj sposób formatowania znaczników czasu jako [hh]: [mm]: [ss], [milisekund] zamiast tylko sekund
»Zaimplementowano
Zaimplementuj polecenie ffmpeg, aby utworzyć pliki mozaikowe png dla każdego rozdziału
»Zaimplementowane
Sprawdź, czy -c copy
wystarczy scenariusz OP, czy też musimy w pełni ponownie zakodować.
Wygląda na to, że Ryan już to robi .