Video Creator – #Parte 9: Renderização de video com Python

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:

https://variavelconstante.com.br/wp-content/uploads/2022/02/Um-pouco-sobre-Bitcoin.mp4

Muito legal né 😀

Agora é só fazer o upload para o Youtube. No próximo post eu mostro como eu fiz!

Abraço!

Exit mobile version