Modifiez facilement des vidéos de performance avec ffmpeg en utilisant Ruby

1.Tout d'abord

En tant que tendance récente des lecteurs de musique, il existe une tendance selon laquelle les gens ne peuvent pas se rassembler au même endroit et jouer ensemble, ils prennent donc une vidéo pour chacun et la modifient plus tard pour en faire une vidéo et la publier. Je n'ai pas beaucoup de connaissances en musique et en gros je ne la joue pas moi-même. Cependant, il y a des gens impliqués dans la musique autour de moi, et on me demande souvent de monter des vidéos. Pour soutenir cela, j'ai utilisé Ruby pour exécuter ffmpeg et créé un environnement dans lequel le montage vidéo peut être effectué de manière semi-automatique.

ruby_movie_ffmpeg.png

2. Flux de traitement

Désormais, même les personnes sans connaissances peuvent facilement filmer des vidéos de performance à l'aide de smartphones. … Mais cela signifie également que tout le monde envoie des vidéos dans différents formats, tailles vidéo et fréquences d'images. Je pense qu'il existe un meilleur moyen d'éviter la détérioration de la vidéo, mais elle est traitée par une méthode étape par étape comme suit.

3. Source

Puisqu'il est supposé s'exécuter dans un environnement Windows, ceux qui y font référence doivent faire attention à l'utilisation du caractère .

movie_on_playing.rb


control_file = ARGV[0]
cfile = File.open(control_file, "r")

def system_log( exe, workd )
  fho = File.open("#{workd}\\log.txt", "a")
  fho.write("#{exe}\n")
  fho.close
  res = system( exe )
  unless(res)
    puts "# Error! ffmpeg failed. See #{workd}\\log.txt to confirm command."
    exit
  end
end

out_width  = 0
out_height = 0
movie      = []
resize     = []
volume     = []
fadein     = []
delay      = []
delay_f    = []
to_time    = []
blackf     = []
crop_xy    = []
crop_size  = []
skip       = []
row_div    = []
row_flag   = true
ri = 0

for s1 in cfile
  next if s1 =~ /^#/
  a1 = s1.chomp.split(" ")
  if(a1[0] == "ffmpeg")
    ffmpeg = a1[1]
    next
  end
  if(a1[0] == "work")
    workd = a1[1]
    next
  end
  if(a1[0] == "out")
    out_file_name = a1[1]
    system("title #{out_file_name}")
    next
  end
  if(a1[0] == "movie")
    movie << a1[1]
    next
  end
  if(a1[0] == "resize")
    resize << a1[1]
    next
  end
  if(a1[0] == "volume")
    volume << a1[1]
    next
  end
  if(a1[0] == "fadein")
    fadein << a1[1]
    next
  end
  if(a1[0] == "delay")
    delay << a1[1]
    delay_f << a1[2] if(a1[1].to_f > 0)
    next
  end
  if(a1[0] == "time")
    to_time << a1[1]
    next
  end
  if(a1[0] == "crop")
    crop_xy << a1[1]
    crop_size << a1[2]
    if(row_div.size == 0)
      out_width = out_width + a1[2].split("x")[0].to_i
    end
    if row_flag
      out_height = out_height + a1[2].split("x")[1].to_i
      row_flag = false
    end
    next
  end
  if(a1[0] == "skip")
    skip << a1[1]
    next
  end
  if(a1[0] == "row_div")
    row_div << movie.size
    row_flag = true
    next
  end
end

cfile.close

Dir.mkdir(workd) unless File.exist?(workd)
efile_mp4 = []
efile_mp3 = []
ovl_vx = 0
ovl_vy = 0
ovl_a = []

fho = File.open("#{workd}\\log.txt", "a")
fho.write("\n#{Time.now}\n")
fho.close

out_width_e = out_width
out_height_e = out_height
fi = 0
w_reset = false
row_div << 9999

for i in 0...movie.size
  filen = movie[i].split("\\")[-1].split(".")[0]
  filee = movie[i].split("\\")[-1].split(".")[1]
  file_rsz = "#{filen}_#{resize[i]}"
  rx = resize[i].split("x")[0]
  ry = resize[i].split("x")[1]
  ####################
  #### resize part ###
  ####################
  
  if(delay[i].to_f > 0.0)
    fade_time = 0
  else
    fade_time = -1 * delay[i].to_f
  end
  if(delay[i].to_f > 0)
    if(fadein[i].to_i > 0)
      fade_in = "fade=in:0:#{fadein[i]},"
    else
      fade_in = ""
    end
    system_log( "#{ffmpeg} -y -i \"#{movie[i]}\" -vf \"#{fade_in}scale=#{rx}:#{ry}\" \"#{workd}\\#{file_rsz}.#{filee}\"", workd ) if(skip[i] == "off")
  else
    system_log( "#{ffmpeg} -y -i \"#{movie[i]}\" -vf \"scale=#{rx}:#{ry}\" \"#{workd}\\#{file_rsz}.#{filee}\"", workd ) if(skip[i] == "off")
  end
  ######################
  ### black mov part ###
  ######################
  if(delay[i].to_f > 0.0)
    dname = delay[i].gsub(".","p")
    bfile = "black_#{resize[i]}_#{dname}.#{filee}"
    if(delay_f[fi] == nil)
      frate = "30000/1001"
    else
      frate = delay_f[fi]
    end
    system_log( "#{ffmpeg} -y -f lavfi -i \"color=c=black:s=#{resize[i]}:r=#{frate}:d=#{delay[i]}\" -f lavfi -i \"aevalsrc=0|0:c=stereo:s=44100:d=#{delay[i]}\" \"#{workd}\\#{bfile}\"", workd ) if(skip[i] == "off")
    blackf << bfile
    fi = fi + 1
  else
    blackf << "black_0"
  end
  #######################
  ### concat/cut part ###
  #######################
  if(blackf[i] != "black_0")
    # add plus delay
    con_filen = "#{workd}\\concat_#{i}.txt"
    con_file = File.open(con_filen, "w")
    con_file.write("file #{workd}/#{blackf[i]}\n")
    con_file.write("file #{workd}/#{file_rsz}.#{filee}\n")
    con_file.close
    file_cn = "#{file_rsz}_con"
    system_log( "#{ffmpeg} -y -safe 0 -f concat -i \"#{con_filen}\" -c:v copy -c:a copy -map 0:v -map 0:a \"#{workd}\\#{file_cn}.#{filee}\"", workd ) if(skip[i] == "off")
  elsif(delay[i].to_f < 0)
    # add minus delay
    cut_time = -1 * delay[i].to_f
    file_ct  = "#{filen}_cut"
    system_log( "#{ffmpeg} -y -i \"#{workd}\\#{file_rsz}.#{filee}\" -ss #{cut_time} \"#{workd}\\#{file_ct}.#{filee}\"", workd ) if(skip[i] == "off")
    file_cn = file_ct
  else
    # not add delay
    file_cn = file_rsz
  end
  ###########################
  ### fade, crop, to part ###
  ###########################

  crop_x = crop_xy[i].split("x")[0]
  crop_y = crop_xy[i].split("x")[1]
  crop_w = crop_size[i].split("x")[0]
  crop_h = crop_size[i].split("x")[1]
  cropt = "crop=#{crop_w}:#{crop_h}:#{crop_x}:#{crop_y}"
  
  if(w_reset)
    out_width_e = out_width
    out_height_e = out_height_e - crop_h.to_i
    w_reset = false
  end

  if( out_width_e - crop_w.to_i > 0 || out_height_e - crop_h.to_i > 0)
    padt = ",pad=#{out_width_e}:#{out_height_e}:0:0"
  else
    padt = ""
  end
  if(to_time[i].to_f > 0)
    tot = "-to #{to_time[i].to_f + delay[i].to_f}"
  else
    tot = ""
  end
  file_crp = "#{file_rsz}_cropped"
  if(delay[i].to_f > 0)
    system_log( "#{ffmpeg} -y -i \"#{workd}\\#{file_cn}.#{filee}\" -vf \"#{cropt}#{padt}\" #{tot} \"#{workd}\\#{file_crp}.#{filee}\"", workd ) if(skip[i] == "off")
  else
    if(fadein[i].to_i > 0)
      fade_in = "fade=in:0:#{fadein[i]},"
    else
      fade_in = ""
    end
    system_log( "#{ffmpeg} -y -i \"#{workd}\\#{file_cn}.#{filee}\" -vf \"#{fade_in}#{cropt}#{padt}\" #{tot} \"#{workd}\\#{file_crp}.#{filee}\"", workd ) if(skip[i] == "off")
  end
  
  out_width_e = out_width_e - crop_w.to_i
  if(row_div.size > 0)
    if(i == row_div[ri] - 1)
      ovl_vx = 0
      ovl_vy = ovl_vy + crop_h.to_i
      ri = ri + 1
      w_reset = true
    else
      ovl_vx = ovl_vx + crop_w.to_i
    end
  else
    ovl_vx = ovl_vx + crop_w.to_i
  end
  ovl_a << [ovl_vx, ovl_vy]
  ############################
  ### mp3 out, volume part ###
  ############################
  vol_e = volume[i].to_f / 100
  system_log( "#{ffmpeg} -y -i \"#{workd}\\#{file_crp}.#{filee}\" -vn -acodec libmp3lame -ar 44100 -ab 256k -af \"volume=#{vol_e}\" \"#{workd}\\#{filen}.mp3\"", workd ) if(skip[i] == "off")
  efile_mp3 << "#{workd}\\#{filen}.mp3"
  efile_mp4 << "#{workd}\\#{file_crp}.#{filee}"
end

####################
### overlay part ###
####################
in_file = ""
for efile in efile_mp4
  in_file = "#{in_file} -i \"#{efile}\""
end
ovlt = ""
for i in 0...ovl_a.size - 1
  if(ovlt == "")
    ovlt = "overlay=x=#{ovl_a[i][0]}:y=#{ovl_a[i][1]}"
  else
    ovlt = "#{ovlt},overlay=x=#{ovl_a[i][0]}:y=#{ovl_a[i][1]}"
  end
end
system_log( "#{ffmpeg} -y#{in_file} -filter_complex \"#{ovlt}\" -an \"#{workd}\\out_movie.#{filee}\"", workd )
######################
### add audio part ###
######################
in_file = ""
for efile in efile_mp3
  in_file = "#{in_file} -i \"#{efile}\""
end
system_log( "#{ffmpeg} -y -i \"#{workd}\\out_movie.#{filee}\" #{in_file} -filter_complex \"amix=inputs=#{efile_mp3.size}:duration=longest:dropout_transition=2\" \"#{out_file_name}\"", workd )

3.1 Control file Un fichier de contrôle a été créé et lu pour spécifier l'emplacement du fichier vidéo et la méthode d'édition. Ce qui suit est un exemple. De plus, la vidéo après avoir entré le mot-clé "row_div" sera connectée verticalement au lieu d'horizontal.

control.txt


ffmpeg C:\ffmpeg\bin\ffmpeg.exe
work   work
out    out.mp4

movie  org_data\a-san.mp4
resize 1280x720
volume 100
fadein 60
delay  1.5
time   208.5
crop   0x0 640x720
skip   off

movie  org_data\b-san.mp4
resize 1280x720
volume 120
fadein 60
delay  -2.5
time   212.5
crop   320x0 640x720
skip   off
ruby movie_on_playing.rb control.txt

Avec des commandes telles que, les commandes ffmpeg sont générées et exécutées séquentiellement en fonction des informations de contrôle.

4. Explication du traitement

Le traitement de chaque partie est expliqué selon la partie ○○ de la section commentaire.

4.1. Resize part Redimensionnez la vidéo en fonction du paramètre "redimensionner" dans le fichier de contrôle. Mettez la valeur de redimensionnement après "scale =" et générez la commande suivante.

ffmpeg.exe -y -i "org_data\a-san.mp4" -vf "scale=1280:720" "work\a-san_1280x720.mp4"

Si la valeur de "delay" est +, la vidéo qui n'affiche rien avant sera liée, donc le fondu sera également défini ici.

ffmpeg.exe -y -i "org_data\a-san.mp4" -vf "fade=in:0:30,scale=1280:720" "work\a-san_1280x720.mp4"

4.2. Black movie part Si la valeur de "delay" est + (pour retarder le début de la vidéo), une vidéo sans vidéo sera créée s'il n'y a pas de son pendant le nombre de secondes spécifié (Remarque: pas sans son). Mettez la valeur après "d =" et générez la commande suivante.

ffmpeg.exe -y -f lavfi -i "color=c=black:s=1280x720:r=30000/1001:d=1.5" -f lavfi -i "aevalsrc=0|0:c=stereo:s=44100:d=1.5" "work\black_1280x720_1p5.mp4"

Etant donné que ce processus a tendance à poser des problèmes dans la vidéo après la concaténation, l'image peut être modifiée dans la 3ème colonne avec "délai [secondes] [fréquence d'images]". Cependant, cette méthode peut ne pas fonctionner correctement, donc lors de l'édition d'une vidéo à l'aide de ffmpeg, il est plus facile de filmer avec une marge suffisante de la vidéo originale, puis de la couper. Si vous souhaitez tourner une vidéo à partir de maintenant, dites à l'interprète: "Appuyez sur le bouton d'enregistrement, attendez environ 5 secondes, puis lancez la lecture."

4.3. Concat/cut part Si la valeur de "delay" est- (pour démarrer la vidéo plus tôt), mettez-la dans la valeur de "-ss" et générez la commande suivante.

ffmpeg.exe -y -i "work\a-san_1280x720.mp4" -ss 2.5 "work\a-san_1280x720_cut.mp4"

Si la valeur de "délai" est +, il sera lié à la vidéo précédente sans vidéo.

ffmpeg.exe -y -safe 0 -f concat -i "work\concat_1.txt" -c:v copy -c:a copy -map 0:v -map 0:a "work\a-san_1280x720_con.mp4"

4.4. Fade, crop, to part Ici, vous pouvez recadrer la vidéo et spécifier l'heure de fin. Si la valeur de "delay" est-, réglez le fondu ici. Dans ffmpeg, la taille après recadrage et la position de recadrage sont spécifiées dans cet ordre, mais comme c'était un peu difficile à comprendre personnellement, le fichier de contrôle utilise le format "recadrage [position de recadrage] [taille après recadrage]". Il est converti en "crop (X1) x (Y1) (X2) x (Y2)" → ffmpeg "crop = (X2): (Y2): (X1): (Y1)". L'un des points forts de l'utilisation de Ruby est que vous pouvez décider vous-même du format en fonction de vos préférences.

La valeur de "pad =" est calculée à partir de la taille recadrée de chaque vidéo. "-to" est calculé à partir des valeurs de "time" et "delay" dans le fichier de contrôle.

ffmpeg.exe -y -i "work\a-san_1280x720_cut.mp4" -vf "fade=in:0:30,crop=640:720:0:0,pad=1280:720:0:0" -to 210.0 "work\a-san_1280x720_cropped.mp4"

4.5. Mp3 out, volume part Extrayez des fichiers sonores uniquement à partir de vidéos chronométrées (avec -ss et -to définis). Dans ffmpeg, 100% du volume est "1.0", mais dans le fichier de contrôle, 100% est "100". Mettez "valeur de réglage / 100" après "volume =" et générez la commande suivante.

ffmpeg.exe -y -i "work\a-san_1280x720_cropped.mp4" -vn -acodec libmp3lame -ar 44100 -ab 256k -af "volume=1.0" "work\a-san.mp3"

4.6. Overlay part Les vidéos ajustées individuellement sont combinées en une seule vidéo par superposition. Calculez à partir de la valeur de récolte et générez la commande suivante.

ffmpeg.exe -y -i "work\a-san_1280x720_cropped.mp4" -i "work\b-san_1280x720_cropped.mp4" -filter_complex "overlay=x=640:y=0" -an "work\out_movie.mp4"

4.7. Add audio part Enfin, entrez tous les sons et vous avez terminé. Conservez le nom du fichier mp3 comme un tableau, connectez-les dans l'ordre avec -i, entrez la taille du tableau comme valeur de "amix = inputs =" et générez la commande suivante.

ffmpeg.exe -y -i "work\out_movie.mp4" -i "work\a-san.mp3" -i "work\a-san.mp3" -filter_complex "amix=inputs=2:duration=longest:dropout_transition=2" "out.mp4"

Ce "out.mp4" est une vidéo terminée.

5. Autre

L'un des principaux inconvénients de la saisie de commandes dans CUI et de l'édition de vidéos est que vous ne pouvez pas éditer tout en regardant ou en écoutant la synchronisation et le volume. Par conséquent, la vidéo créée par la méthode ci-dessus est

Etc. peut être demandé. À ce stade, afin d'accélérer la ** vidéo non ajustée en utilisant le résultat précédent **, créez un élément appelé "ignorer" dans le fichier de contrôle, et s'il est "activé", ignorez le traitement de cette vidéo. Je le fais.

6. À la fin

ffmpeg est un outil très sophistiqué et facile à utiliser que j'utilise depuis longtemps, mais c'est beaucoup de travail de taper des dizaines ou des centaines de commandes, notamment pour calculer et saisir des informations liées à la taille de la vidéo. Est requis. J'espère que cet article vous a aidé à faciliter cet effort.

Recommended Posts

Modifiez facilement des vidéos de performance avec ffmpeg en utilisant Ruby
Remarques sur l'utilisation de FCM avec Ruby on Rails
Essayez d'utiliser le kit de récepteur GPS avec Raspberry Pi 3 (Ruby)
Ressentez facilement le type de base et le type de référence avec ruby
Ressentez facilement le type de base et le type de référence avec ruby 2