自动将大型 .mov 视频文件拆分为黑帧(场景变化)的较小文件?

Rya*_*yan 12 video ffmpeg video-editing

我有 65 个名为 tape_01.mov、tape_02.mov、...、tape_65.mov 的 .mov 视频文件。每个大约 2.5 小时,占用许多 GB 空间。它们是 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) . 我希望这一步的压缩设置很容易配置。创建 .mp4 文件后,原始 .mov 文件应该仍然存在。
  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 中运行的 shell 脚本。

nix*_*xda 12

这里有两个 PowerShell 脚本,用于将长视频按黑色场景拆分成更小的章节。

将它们另存为 Detect_black.ps1 和 Cut_black.ps1。下载适用于 Windows 的 ffmpeg并在选项部分下告诉脚本您的 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
}
Run Code Online (Sandbox Code Playgroud)

它是如何工作的

第一个脚本遍历所有匹配指定扩展名但不匹配 pattern 的视频文件*_???.*,因为新的视频章节被命名<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
Run Code Online (Sandbox Code Playgroud)

运行后,您可以根据需要手动操作切割点。如果您再次运行该脚本,所有旧内容都会被覆盖。手动编辑并将您的工作保存在其他地方时要小心。

对于示例视频,检测黑色场景的 ffmpeg 命令是

$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null
Run Code Online (Sandbox Code Playgroud)

在脚本的选项部分有 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
Run Code Online (Sandbox Code Playgroud)

要运行的第二个脚本: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        
}
Run Code Online (Sandbox Code Playgroud)

它是如何工作的

第二个脚本以与第一个脚本相同的方式遍历所有视频文件。它仅从相应的视频中读取剪切时间戳cutpoints.txt

接下来,它为章节文件组合一个合适的文件名,并告诉 ffmpeg 对视频进行分段。目前,视频切片无需重新编码(超快和无损)。因此,切割点时间戳可能存在 1-2 秒的不准确,因为 ffmpeg 只能在关键帧处进行切割。由于我们只是复制而不重新编码,因此我们无法自行插入 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"
Run Code Online (Sandbox Code Playgroud)

如果出现问题,请查看相应的ffmpeg.log



参考

  • 适用于 Windows 的FFmpeg 下载
  • Segment dokumentation:segment_times 和 segment_frames 是选项


去做

  • 询问 OP CSV 格式是否比文本文件作为剪切点文件更好,以便您可以更轻松地使用 Excel 编辑它们
    » 已实现
  • 实现一种将时间戳格式化为 [hh]:[mm]:[ss],[milliseconds] 而不仅仅是秒的方法
    » 已实现
  • 实现一个 ffmpeg 命令,为每一章创建 mosaik png 文件
    » 已实现
  • 详细说明-c copy对于 OP 的场景是否足够,或者我们需要完全重新编码。
    似乎瑞安已经在上面了


tbe*_*nz9 -2

我希望我是错的,但我认为你的要求是不可能的。重新编码视频时可能有可能,但作为“简单的无损切片”我不知道有什么方法。

许多视频编辑程序都具有自动分析功能或类似功能,可以在黑帧上分割视频,但这需要对视频进行重新编码。

祝你好运!