Bom dia, boa tarde, boa noite!
Essa é a nona parte da série sobre o projeto Video Creator, onde mostrarei como fiz a renderização do video.
Se você entrou direto nesse post, te aconselho a começar por aqui, para entender exatamente do que se trata essa série.
Sobre essa parte
Para renderização do video, utilizei uma lib fantástica chamada MoviePy.
A lib possui diversas funcionalidades legais, e pra quem quiser saber mais, recomendo dar uma olhada no link abaixo:
https://pypi.org/project/moviepy/
Requisitos:
Para a renderização do video, precisaremos importar as libs do MoviePy.
from moviepy.editor import *
from moviepy.video.tools.segmenting import findObjects
MoviePy:
O MoviePy utiliza algumas features do ImageMagick, e precisamos fazer alguns ajustes nas configurações.
No linux, o MoviePy detecta automaticamente o Image Magick. No windows é preciso criar uma variável de ambiente chamada IMAGEMAGICK_BINARY
contendo o caminho do binário. Como utilizei Ubuntu, não tive nenhum problema quanto a isso.
Para que ele funcione corretamente, é preciso configurar uma política de segurança, do contrário, dará erro na hora de compilar o video.
Como isso é somente uma POC, apenas comentei no arquivo /etc/ImageMagick-x/policy.xml
algumas linhas:
<!--<policy domain="path" rights="none" pattern="@*"/>-->
<!--<policy domain="resource" name="width" value="16KP"/>-->
<!--<policy domain="resource" name="height" value="16KP"/>-->
Setei algumas variáveis no arquivo rconfig.py:
# FOR VIDEO
VIDEO_INTRODUCTION_TEXT = "Variavel Constante\napresenta..."
VIDEO_SCREENSIZE = (1920,1080)
VIDEO_TEXT_COLOR_DEFAULT = 'white'
VIDEO_TEXT_FONT_DEFAULT = 'helvetica'
VIDEO_FONT_SIZE_DEFAULT = 50
VIDEO_TEXT_POSITION_DEFAULT = 'center'
VIDEO_FRAME_DURATION_DEFAULT = 4
VIDEO_TEXT_KERNING_DEFAULT = 5
VIDEO_CODEC_DEFAULT = 'mpeg4'
VIDEO_FPS_DEFAULT = 30
A parte de compilação do video, encapsulei dentro de uma função, que chamei de compile_video()
. Ela ficou assim:
def compile_video():
logging.info("Compiling video...")
#os.chdir('./')
path = CONTENT_IMAGES_PATH
# create directory
video_content = rcontent.load()
video_filename = util.remove_accents(video_content['search_term']).replace(" ", "_") + ".mp4"
video_content['video_filename'] = video_filename
video_content['youtube_details']['title'] = "Um pouco sobre {}".format(video_content['search_term'])
rcontent.save(video_content)
# carregando musica
music = AudioFileClip('{}/gypsy-jaxx.mp3'.format(CONTENT_SOUND_PATH))
new_audioclip = CompositeAudioClip([music])
screensize = VIDEO_SCREENSIZE
txt_size = (.8*screensize[0],0)
color = VIDEO_TEXT_COLOR_DEFAULT
font = VIDEO_TEXT_FONT_DEFAULT
font_size = VIDEO_FONT_SIZE_DEFAULT
list_video_clips = []
# Criando introdução
txt_clip1 = TextClip(VIDEO_INTRODUCTION_TEXT,color=color, font=font, kerning = VIDEO_TEXT_KERNING_DEFAULT, fontsize=100, method='caption', size=txt_size).set_pos(VIDEO_TEXT_POSITION_DEFAULT).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
txt_clip2 = TextClip('um pouco sobre...',color=color, font=font, kerning = VIDEO_TEXT_KERNING_DEFAULT, fontsize=100, method='caption', size=txt_size).set_pos(VIDEO_TEXT_POSITION_DEFAULT).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
txt_clip3 = TextClip(video_content['search_term'],color=color, font=font, kerning = VIDEO_TEXT_KERNING_DEFAULT, fontsize=100, method='caption', size=txt_size).set_pos(VIDEO_TEXT_POSITION_DEFAULT).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
img_clip = ImageClip("{}../../default/bg_default_new.jpeg".format(path)).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
cvc1 = CompositeVideoClip([img_clip,txt_clip1], size=screensize)
cvc2 = CompositeVideoClip([img_clip,txt_clip2], size=screensize)
cvc3 = CompositeVideoClip([img_clip,txt_clip3], size=screensize)
list_video_clips.append(cvc1)
list_video_clips.append(cvc2)
list_video_clips.append(cvc3)
#for root, folders, files in os.walk('./content/images'):
for idx, sentence in enumerate(video_content['sentences']):
f = "{}/{}_composite.jpg".format(path, idx)
filename = f if os.path.exists(f) else CONTENT_DEFAULT_PATH+"/missing_image.jpg"
# workaround to resolve the problems with grayscale image
'''
img = Image.open(filename)
rgbimg = Image.new(formatter.get(img.format, 'RGB'), img.size)
rgbimg.paste(img)
rgbimg.save(filename, format=img.format)
'''
duration = int(len(sentence['text'])/15)
txt_clip = TextClip('{}'.format(sentence['text']),color=color, font=font, kerning = 5, fontsize=font_size, method='caption', size=txt_size).set_pos('center').set_duration(duration)
im_width, im_height = txt_clip.size
color_clip = ColorClip(size=(1920, int(im_height*3)), color=(0, 0, 0)).set_pos('center')
color_clip = color_clip.set_opacity(.6)
clip_to_overlay = CompositeVideoClip([color_clip, txt_clip], size=screensize).set_position('center')
#cvc = CompositeVideoClip([txt_clip,img_clip], size=screensize)
if os.path.isfile(filename):
img_clip = ImageClip(filename).set_duration(duration)
cvc = CompositeVideoClip([img_clip,clip_to_overlay], size=screensize).set_duration(duration)
else:
cvc = CompositeVideoClip([clip_to_overlay], size=screensize).set_duration(duration)
list_video_clips.append(cvc)
# Criando o fechamento do video
txt_fechamento = TextClip('Obrigado por assistir!!!\nd(~.~)b',color=color, font=font, kerning = 5, fontsize=100, method='caption', size=screensize).set_pos('center').set_duration(4)
list_video_clips.append(txt_fechamento)
#final_clip = concatenate_videoclips(clips)
#final_clip.write_videofile('./content/coolTextEffects.avi',fps=25,codec='mpeg4')
final_clip = concatenate_videoclips(list_video_clips)
audio = afx.audio_loop(music, duration=final_clip.duration)
final = final_clip.set_audio(audio).audio_fadeout(5)
final.write_videofile('{}/{}'.format(CONTENT_PATH, video_filename),fps=VIDEO_FPS_DEFAULT,codec=VIDEO_CODEC_DEFAULT)
No início da função já criei alguns dados que serão úteis mais pra frente, e salvei no arquivo content.json
.
Depois criei um objeto contendo a música que utilizei de fundo. Para esse projeto, deixei uma música fixa de fundo, a mesma para todos os videos.
Depois setei algumas variáveis contendo as configurações do video, como resolução, tamanho, cor e fonte do texto, tamanho da tarja preto onde posicionei o texto.
Todas as configurações relacionadas ao texto foram para chegar nesse resultado:
Após o ajuste da configuração do video, criei os 3 primeiros slides do video com textos padrões de apresentação e uma imagem fixa.
Após, comecei a iterar nas sentenças para incluir o texto, e buscando a imagem correspondente aquela sentença para incluir de background.
Para conseguir chegar numa duração adequada para cada slide, cronometrei quanto tempo eu levava para ler tranquilamente um texto com 150 caracteres, +- 10 segundos. Então para setar a duração do slide contei o número de caracteres de cada sentença e dividi por 15:
duration = int(len(sentence['text'])/15)
Com isso, cada slide ficou com uma duração adequada de acordo com o tamanho da sentença que será exibida.
Por fim, inclui um texto padrão de encerramento do video, inclui a música padrão, e mandei compilar o video. O trecho que compila o video é esse abaixo, e encontra-se no finalzinho da function:
final.write_videofile('{}/{}'.format(CONTENT_PATH, video_filename),fps=VIDEO_FPS_DEFAULT,codec=VIDEO_CODEC_DEFAULT)
Eis o resultado do video sobre Bitcoin:
Muito legal né 😀
Agora é só fazer o upload para o Youtube. No próximo post eu mostro como eu fiz!
Abraço!