9

У меня есть 65. MOV видео файлов с именами tape_01.mov, tape_02.mov, ..., tape_65.mov. Каждый длится около 2,5 часов и занимает много гигабайт. Они являются цифровыми копиями того, что было лентами VHS ("домашние фильмы" моей семьи).

Каждый 2,5-часовой видеофайл .mov содержит много "глав" (в том смысле, что иногда сцена заканчивается переходом в черный экран, а затем появляется новая сцена).

Моя главная цель - разбить 65 больших файлов на более мелкие файлы по главам. И я хочу, чтобы это было сделано автоматически через обнаружение черных рамок между "сценами".

Могу ли я использовать ffmpeg (или что-нибудь еще) для передачи 65 исходных имен файлов, чтобы он автоматически перебирал каждое видео и разбивал его на части, называя получившиеся фрагменты tape_01-ch_01.mov, tape_01-ch_02.mov, tape_01-ch_03.mov, так далее? И я хочу, чтобы это делалось без перекодирования (я хочу, чтобы это был простой фрагмент без потерь).

Как я могу это сделать?

Вот шаги, которые я хочу:

  1. Выполните итерацию по папке файлов .mov (с именем tape_01.mov, tape_02.mov, ..., tape_65.mov), чтобы экспортировать их в виде файлов mp4 (с именем tape_01.mp4, tape_02.mp4, ..., tape_65.mp4) , Я хочу, чтобы настройки сжатия были простыми в настройке для этого шага. Исходные файлы .mov должны существовать после создания файлов .mp4.
  2. Перебирайте файлы mp4: для каждого файла mp4 создайте текстовый файл, который указывает время начала и продолжительность черных сегментов между "сценами". Каждый mp4 может иметь 0 или более этих черных перерывов сцены. Время начала и продолжительность должны быть точными до доли секунды. Формат ЧЧ: ММ: СС недостаточно точен.
  3. Затем я смогу вручную дважды проверить эти текстовые файлы, чтобы увидеть, правильно ли они идентифицируют изменения сцены (черные сегменты). Я могу настроить время, если я хочу.
  4. Другой сценарий считывает каждый файл mp4 и его текстовый файл и разбивает (очень быстро и без потерь) mp4 на столько частей, сколько необходимо, основываясь на строках текстового файла (время указано там). Расколы должны происходить в середине каждого черного сегмента. Вот пример файла mp4 (со звуком, удаленным в целях конфиденциальности), в котором изменения сцены переходят в черный. Исходные файлы mp4 должны оставаться нетронутыми, так как создаются меньшие файлы mp4. Файлы меньшего размера mp4 должны иметь схему именования tape_01_001.mp4, tape_01_002.mp4, tape_01_003.mp4 и т.д.
  5. Бонус! Было бы замечательно, если бы другой сценарий мог затем перебрать небольшие файлы mp4 и сгенерировать одно изображение .png для каждого, представляющего собой мозаику снимков экрана, как этот человек пытается: Значимые миниатюры для видео с использованием FFmpeg

Я хотел бы, чтобы эти сценарии были сценариями оболочки, которые я могу запускать в Mac, Ubuntu и Windows Git Bash.

4 ответа4

10

Вот два сценария PowerShell для разделения длинных видеороликов на более мелкие главы черными сценами.

Сохраните их как Detect_black.ps1 и Cut_black.ps1. Загрузите ffmpeg для Windows и сообщите сценарию путь к файлу ffmpeg.exe и папке с видео в разделе параметров.

Оба сценария не затрагивают существующие видеофайлы, они остаются нетронутыми.
Тем не менее, вы получите несколько новых файлов в том же месте, где находятся ваши входные видео

  • Файл журнала для каждого видео с консольным выводом для обеих используемых команд ffmpeg
  • Файл CSV для каждого видео со всеми временными метками черных сцен для ручной тонкой настройки
  • Пара новых видео в зависимости от того, сколько черных сцен было обнаружено ранее


Первый скрипт для запуска: 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
}

Как это работает

Первый скрипт перебирает все видеофайлы, которые соответствуют указанному расширению и не соответствуют шаблону *_???.* , Так как новые видео главы были названы <filename>_###.<ext> и мы хотим исключить их.

Он ищет все черные сцены и записывает метку времени начала и длительность черной сцены в новый CSV-файл с именем <video_name>_cutpoints.txt

Он также вычисляет точки среза, как показано: cutpoint = black_start + black_duration / 2 . Позже, видео становится сегментированным в эти временные метки.

Файл cutpoints.txt для вашего примера видео будет отображать:

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

После прогона вы можете при желании манипулировать точками среза вручную. Если вы снова запустите скрипт, весь старый контент будет перезаписан. Будьте осторожны при ручном редактировании и сохраняйте свою работу в другом месте.

Для примера видео команда ffmpeg для обнаружения черных сцен

$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null

В разделе опций скрипта есть 3 важных номера, которые можно редактировать.

  • d=4 означает, что обнаружены только черные сцены длительностью более 4 секунд
  • pic_th=0.98 - это порог для того, чтобы считать изображение "черным" (в процентах).
  • pix=0.15 устанавливает порог для рассмотрения пикселя как "черного" (по яркости). Поскольку у вас есть старые видео VHS, в ваших видео нет полностью черных сцен. Значение по умолчанию 10 не будет работать, и мне пришлось немного увеличить порог

Если что-то пойдет не так, проверьте соответствующий файл журнала с именем <video_name>__ffmpeg.log . Если следующие строки отсутствуют, увеличивайте числа, упомянутые выше, пока не обнаружите все черные сцены:

[blackdetect @ 0286ec80]
 black_start:236.908 black_end:242.247 black_duration:5.33877



Второй скрипт для запуска: 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        
}

Как это работает

Второй скрипт перебирает все видеофайлы так же, как первый скрипт. Он считывает только вырезанные временные метки из соответствующих cutpoints.txt видео.

Затем он собирает подходящее имя файла для файлов главы и сообщает ffmpeg о сегментации видео. В настоящее время видео нарезано без перекодирования (сверхбыстрое и без потерь). Из-за этого может быть неточность 1-2 с с временными метками точки вырезания, потому что ffmpeg может вырезать только в key_frames. Поскольку мы просто копируем и не перекодируем, мы не можем вставить key_frames самостоятельно.

Команда для примера видео будет

$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"

Если что-то пойдет не так, взгляните на соответствующий файл ffmpeg.log



Рекомендации



Сделать

  • Спросите у OP, лучше ли формат CSV, чем текстовый файл в качестве файла точки обрезки, чтобы вы могли редактировать их в Excel немного проще
    »Реализовано
  • Реализуйте способ форматировать метки времени как [чч]:[мм]:[сс], [миллисекунды], а не только секунды
    »Реализовано
  • Реализуйте команду ffmpeg для создания png-файлов мозаики для каждой главы
    »Реализовано
  • Уточните, если -c copy достаточно для сценария OP или нам нужно полностью перекодировать.
    Похоже, Райан уже на нем.
3

Вот бонус: этот третий скрипт PowerShell создает миниатюру мозаичного изображения

Вы получите одно изображение на каждую главу видео, которое выглядит как пример ниже.

### 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
$x = 5                         # Set how many images per x-axis
$y = 4                         # Set how many images per y-axis
$w = 384                       # Set thumbnail width in px (full image width: 384px x 5 = 1920px,)

### Main Program ______________________________________________________________________________________________________

foreach ($video in dir $folder -include "*_???.mp4" -r){

  ### get video length in frames, an ingenious method
  $log = & $ffmpeg -i $video -vcodec copy -an -f null $video 2>&1   
  $frames = $log | Select-String '(?<=frame=.*)\S+(?=.*fps=)' | % { $_.Matches } | % { $_.Value }  
  $frame = [Math]::floor($frames / ($x * $y))

  ### put together the correct new picture name
  $output = $video.directory.Fullname + "\" + $video.basename + ".jpg"

  ### use ffmpeg to create one mosaic png per video file
  ### Basic explanation for -vf options:  http://trac.ffmpeg.org/wiki/FilteringGuide
#1  & $ffmpeg -y -i $video -vf "select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#2  & $ffmpeg -y -i $video -vf "yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output 
    & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output       

#4  & $ffmpeg -y -i $video -vf "select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  
#5  & $ffmpeg -y -i $video -vf "yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  
#6  & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output  

#7  & $ffmpeg -y -i $video -vf "thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#8  & $ffmpeg -y -i $video -vf "yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
#9  & $ffmpeg -y -i $video -vf "mpdecimate,yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output
}

Основная идея состоит в том, чтобы получить непрерывный поток изображений по всему видео. Мы делаем это с помощью опции выбора ffmpeg.

Сначала мы извлекаем общее количество кадров с помощью оригинального метода (например, 2000) и делим его на число миниатюр по умолчанию (например, 5 x 4 = 20). Таким образом, мы хотим генерировать одно изображение каждые 100 кадров с 2000 / 20 = 100

Результирующая команда ffmpeg для создания миниатюры может выглядеть так

ffmpeg -y -i input.mp4 -vf "mpdecimate,yadif,select=not(mod(n\,100)),scale=384:-1,tile=5x4" -frames:v 1 output.png

В приведенном выше коде вы видите 9 различных комбинаций -vf состоящих из

  • select=not(mod(n\,XXX)) где XXX - вычисленная частота кадров
  • thumbnail которая автоматически выбирает наиболее представительные кадры
  • select='gt(scene\,XXX) + -vsync vfr где XXX - пороговое значение, с которым вы должны играть
  • mpdecimate - удаляет почти дублированные кадры. Хорошо против черных сцен
  • yadif - деинтерлейсировать входное изображение. Не знаю почему, но это работает

Версия 3 - лучший выбор на мой взгляд. Все остальные закомментированы, но вы все еще можете попробовать их. Мне удалось удалить большинство размытых миниатюр с помощью mpdecimate , yadif и select=not(mod(n\,XXX)) . Да уж!

Для вашего примера видео я получаю эти превью


нажмите, чтобы увеличить


нажмите, чтобы увеличить

Я загрузил все эскизы, созданные этими версиями. Посмотрите на них для полного сравнения.

0

Цените оба сценария, отличный способ разделить видео.

Тем не менее, у меня были некоторые проблемы с видео, которые не показывают правильное время после этого, и я не мог перейти к определенному времени. Одним из решений было демультиплексирование, а затем мультиплексирование потоков с помощью Mp4Box.

Другой, более простой способ для меня - использовать mkvmerge для разделения. Поэтому оба сценария должны были быть изменены. Для Detect_black.ps1 к опции фильтра нужно было добавить только «* .mkv», но это не обязательно, если вы начинаете только с файлами mp4.

### Options        
$mkvmerge = ".\mkvmerge.exe"        # Set path to mkvmerge.exe
$folder = ".\*"                     # Set path to your video folder; '\*' must be appended
$filter = @("*.mov", "*.mp4", "*.mkv")        # 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 mkvmerge
  $output = $video.directory.Fullname + "\" + $video.basename + "-%03d" + ".mkv"

  ### use mkvmerge to split current video in parts according to their cut points and convert to mkv
  & $mkvmerge -o $output --split timecodes:$cuts $video
}

Функция та же, но проблем с видео пока нет. Спасибо за вдохновение.

-2

Я надеюсь, что ошибаюсь, но я не думаю, что ваш вопрос возможен. Это может быть возможно при перекодировании видео, но как "простой фрагмент без потерь" я не знаю ни одного способа.

Многие программы редактирования видео будут иметь функцию автоматического анализа или что-то подобное, что может разделить ваши видео на черные рамки, но это потребует перекодирования видео.

Удачи!

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .