Автоматически разделять большие видеофайлы .mov на более мелкие файлы на черных кадрах (смена сцены)?

У меня есть 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 или более из этих черных разрывов сцены. Время начала и продолжительность должны быть точными до доли секунды. HH: MM: формат SS недостаточно точен.
  3. Затем я смогу вручную дважды проверить эти текстовые файлы, чтобы определить, правильно ли они определяют изменения сцены (черные сегменты). Я могу настроить тайминги, если захочу.
  4. Другой скрипт будет читать каждый файл mp4 и его txt-файл и разделять (в супербыстро и без потерь) 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.

8 голосов | спросил Ryan 24 TueEurope/Moscow2013-12-24T09:14:57+04:00Europe/Moscow12bEurope/MoscowTue, 24 Dec 2013 09:14:57 +0400 2013, 09:14:57

4 ответа


10

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

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

введите описание изображения здесь>> </p>

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

<ul>
<li> Файл журнала на видео с выходом консоли для обеих команд ffmpeg </li>
<li> Файл CSV на видео со всеми временными отметками черных сцен для ручной точной настройки </li>
<li> Несколько новых видеороликов в зависимости от того, сколько черных сцен обнаружено ранее. </li>
</ul>
<p> <img src = примера видео будет показывать:

 ### 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
}

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

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

*_???.*

Есть 3 важных номера, которые редактируются в разделе параметров скрипта

  • <filename>_###.<ext> означает, что обнаружены только черные сцены более 4 секунд
  • <video_name>_cutpoints.txt является порогом для рассмотрения изображения как «черного» (в процентах)
  • cutpoint = black_start + black_duration / 2 устанавливает пороговое значение для того, чтобы считать пиксель «черным» (в яркости). Поскольку у вас есть старые видео VHS, у вас нет абсолютно черных сцен в ваших видео. Значение по умолчанию 10 не будет работать, и мне пришлось немного увеличить порог

Если что-то пойдет не так, проверьте соответствующий файл журнала с именем 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 -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null



Второй сценарий для запуска: cut_black.ps1

d=4

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

Второй скрипт выполняет итерацию по всем видеофайлам таким же образом, как и первый скрипт. Он читает только временные метки cut из соответствующего pic_th=0.98 видео.

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

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

pix=0.15

Если что-то пойдет не так, посмотрите на соответствующий ffmpeg.log



Ссылки



Todo

  • Задайте OP, если формат CSV лучше, чем текстовый файл, как файл точки пересечения, поэтому вы можете немного упростить их редактирование с помощью Excel
    »Выполнено
  • Реализовать способ форматирования временных меток как [hh]: [mm]: [ss], [миллисекунды], а не только секунды
    »Выполнено
  • Внедрить команду ffmpeg для создания файлов mosaik png для каждой главы
    »Выполнено
  • Разработайте, если <video_name>__ffmpeg.log достаточно для сценария OP или нам нужно полностью перекодировать.
    Похоже, как Райан уже на нем .
ответил nixda 26 ThuEurope/Moscow2013-12-26T00:36:27+04:00Europe/Moscow12bEurope/MoscowThu, 26 Dec 2013 00:36:27 +0400 2013, 00:36:27
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
}

Основная идея - получить непрерывный поток изображений по всему видео. Мы делаем это с помощью опции select опции 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)). Да!

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

 введите описание изображения здесь
Нажмите, чтобы увеличить

 введите описание изображения здесь
Нажмите, чтобы увеличить

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

ответил nixda 8 MaramSat, 08 Mar 2014 02:01:51 +04002014-03-08T02:01:51+04:0002 2014, 02:01:51
0

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

Тем не менее, у меня были некоторые проблемы с видеороликами, которые не показывали правильное время после этого, и я не смог перейти к определенному времени. Одним из решений было demux, а затем mux-потоки с использованием 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
}

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

ответил Andy Gebauer 10 Maypm15 2015, 19:47:26
-2

Надеюсь, я ошибаюсь, но я не думаю, что ваше задание возможно. Возможно, это будет возможно при повторном кодировании видео, но как «простой кусок без потерь», я ничего не знаю.

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

Удачи!

ответил tbenz9 25 WedEurope/Moscow2013-12-25T00:57:34+04:00Europe/Moscow12bEurope/MoscowWed, 25 Dec 2013 00:57:34 +0400 2013, 00:57:34

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132