Tengo un video corto y estoy tratando de recortar todos los marcos en blanco o casi en blanco. Este no es un caso en el que me importen las pausas en un video que podría estar oscuro. Literalmente quiero eliminar todos los marcos negros cercanos.
Lo que no me di cuenta con ffmpeg al principio fue que los filtros 'blackframe' o 'blackdetect' es que en realidad no filtraban. Simplemente parecen mostrarle lo que posiblemente puede filtrar, con otro(s) comando(s).
¿Cuál es la mejor manera en 'Windows' para filtrar los marcos encontrados resultantes?
Entonces, con ffmpeg, en realidad estoy concat-(ting) el avance y el reverso del mismo archivo que estoy ejecutando algunos filtros en esos archivos. Ver mi comando actual:
ffmpeg.exe -y -i "2013-10-14_14-30-55.mov" -filter_complex "[0:v]transpose=3,split[tp][tp2];[tp]reverse[vr];[tp2][vr]concat=n=2:v=1:a=0[vbf];[vbf]blackframe=98:32[v]" -map "[v]" -vcodec wmv2 -q 10 -trellis 2 -r 16 "g:\gd5.wmv"
Luego obtengo esta salida: (Solo el final de la misma de todos modos...)
[Parsed_blackframe_4 @ 00000000003bf320] frame:650 pblack:99 pts:21821999 t:21.821999 type:B last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:651 pblack:99 pts:21855366 t:21.855366 type:P last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:652 pblack:99 pts:21888733 t:21.888733 type:B last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:653 pblack:99 pts:21922099 t:21.922099 type:B last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:654 pblack:99 pts:21955466 t:21.955466 type:I last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:655 pblack:99 pts:21988833 t:21.988833 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:656 pblack:99 pts:22022199 t:22.022199 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:657 pblack:99 pts:22055566 t:22.055566 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:658 pblack:99 pts:22088933 t:22.088933 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:659 pblack:99 pts:22122299 t:22.122299 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:660 pblack:99 pts:22155666 t:22.155666 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:661 pblack:99 pts:22189033 t:22.189033 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:662 pblack:99 pts:22222399 t:22.222399 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:663 pblack:99 pts:22255766 t:22.255766 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:664 pblack:99 pts:22289133 t:22.289133 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:665 pblack:99 pts:22322499 t:22.322499 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:666 pblack:99 pts:22355866 t:22.355866 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:667 pblack:99 pts:22389233 t:22.389233 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:668 pblack:99 pts:22422599 t:22.422599 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:669 pblack:99 pts:22455966 t:22.455966 type:I last_keyframe:669
frame= 362 fps= 14 q=10.0 Lsize= 9004kB time=00:00:22.62 bitrate=3260.2kbits/s dup=1 drop=309
video:8922kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.918756%
Entonces, ¿qué hago con esta información de marco negro en Windows? ¿O hay un método mejor con el filtro 'blackdetect'?
He aquí una forma de hacerlo con los filtros blackdetect
y .trim
Primero, una mejor manera de obtener la blackdetect
salida es a través ffprobe
de porque es capaz de escribir datos estructurados como XML
, JSON
o uno key=value
por línea.
Haces esto usando la inyección de metadatos:
ffprobe -f lavfi -i "movie=/path/to/input.mp4,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet TAG:lavfi.black_start=0 TAG:lavfi.black_end=5.42208 TAG:lavfi.black_start=73.4067
Como puede ver, devuelve la hora de inicio y finalización de cada sección negra. Utiliza la información devuelta a trim
los segmentos entre cada uno end
y el siguiente start
:
ffmpeg -i /path/to/input.mp4 -filter_complex "[0:v]trim=start=5.42208:end=73.4067,setpts=PTS-STARTPTS[v1];[0:a]atrim=start=5.42208:end=73.4067,asetpts=PTS-STARTPTS[a1]" -map [v1] -map [a1] output.mp4
Puede encadenar varios trim
filtros en el mismo comando. Vea esta respuesta para un ejemplo detallado.
Para automatizar esto en Windows, puede instalar y usar PowerShell u otro lenguaje de secuencias de comandos de su elección (PHP, Python, etc.).
He creado un script de Python que hará todo esto. Genera los comandos ffprobe
y ffmpeg
y los ejecuta Y genera un archivo sin clips negros
Repositorio de GitHub: https://github.com/FarisHijazi/deblack
Gist de GitHub (archivado): https://gist.github.com/FarisHijazi/eff7a7979440faa84a63657e085ec504
"""
@author: https://github.com/FarisHijazi
Use ffprobe to extract black frames and ffmpeg to trim them and output a new video
"""
# help from:
# https://video.stackexchange.com/questions/16564/how-to-trim-out-black-frames-with-ffmpeg-on-windows#new-answer?newreg=d534934be5774bd1938b535cd76608cd
# https://github.com/kkroening/ffmpeg-python/issues/184#issuecomment-493847192
import argparse
import os
import shlex
import subprocess
parser = argparse.ArgumentParser(__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('input', type=str, help='input video file')
parser.add_argument('--invert', action='store_true', help='remove nonblack instead of removing black')
args = parser.parse_args()
##FIXME: sadly you must chdir so that the ffprobe command will work
os.chdir(os.path.split(args.input)[0])
args.input = os.path.split(args.input)[1]
spl = args.input.split('.')
outpath = '.'.join(spl[:-1]) + '.' + ('invert' if args.invert else '') + 'out.' + spl[-1]
def delete_back2back(l):
from itertools import groupby
return [x[0] for x in groupby(l)]
def construct_ffmpeg_trim_cmd(timepairs, inpath, outpath):
cmd = f'ffmpeg -i "{inpath}" -y -filter_complex '
cmd += '"'
for i, (start, end) in enumerate(timepairs):
cmd += (f"[0:v]trim=start={start}:end={end},setpts=PTS-STARTPTS,format=yuv420p[{i}v]; " +
f"[0:a]atrim=start={start}:end={end},asetpts=PTS-STARTPTS[{i}a]; ")
for i, (start, end) in enumerate(timepairs):
cmd += f"[{i}v][{i}a]"
cmd += f'concat=n={len(timepairs)}:v=1:a=1[outv][outa]'
cmd += '"'
cmd += f' -map [outv] -map [outa] "{outpath}"'
return cmd
def get_blackdetect(inpath, invert=False):
ffprobe_cmd = f"ffprobe -f lavfi -i \"movie={inpath},blackdetect[out0]\" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet"
print('ffprobe_cmd:', ffprobe_cmd)
lines = subprocess.check_output(shlex.split(ffprobe_cmd)).decode('utf-8').split('\n')
times = [float(x.split('=')[1].strip()) for x in delete_back2back(lines) if x]
assert len(times), 'no black detected'
if not invert:
times = [0] + times[:-1]
timepairs = [(times[i], times[i + 1]) for i in range(0, len(times) // 2, 2)]
return timepairs
timepairs = get_blackdetect(args.input, invert=args.invert)
cmd = construct_ffmpeg_trim_cmd(timepairs, args.input, outpath)
print(cmd)
os.system(cmd)
Combinando múltiples respuestas juntas:
Roberto Koernke
ffprobe -f lavfi -i "movie=2013-10-14_14-30-55.mov,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet
pero no sale ninguna entrada. ¿Puede ser porque los marcos en cuestión no son lo suficientemente negros o lo estoy haciendo mal? No entiendo algunos de los comandos.aergista
blackdetect
, verifique sus argumentos y el ejemplo en ffmpeg.org/ffmpeg-filters.html#blackdetect . Asegúrese de tener también una versión recienteffmpeg
y no una instalación de distribución predeterminada.aergista
blackdetect
, mostrar soloblack_start
yblack_end
etiquetas, usar el formato-of default
de salida predeterminado sin envoltoriosnw=1
y suprimir todas las demás salidas-v quiet
Roberto Koernke
-v quiet
y obtengo esto: [Parsed_movie_0 @ 0056f560] Error al inicializar avformat_open_input 'C' [lavfi @ 0056e2a0] Error al inicializar el filtro 'película' con argumentos 'C:UsersDOCUME~12013-10-14_14-30-55. mov' movie=C:\Users\DOCUME~1\2013-10-14_14-30-55.mov,blackdetect=d=0:pix_th=51.80[out0]: No existe tal archivo o directorio`Roberto Koernke
-v quiet
estaba suprimiendo los mensajes de error.Roberto Koernke
Roberto Koernke
aergista
Roberto Koernke
Error initializing filter 'movie' with args
- La alternativa que voy a usar es copiar en el directorio local ffprobe/ffmpeg como un archivo temporal, procesarlo allí, y luego muevo la versión procesada donde la necesito. Gracias.Roberto Koernke