Criando vídeos com React e Remotion: Olá, mundo!!!

Olá, pessoal!!! Essa publicação será tanto um olá mundo para meu primeiro post nesse site, como também um olá mundo para o Remotion.

Remotion é um framework voltado para criação de vídeos usando o React. Como descrito por eles, você terá uma tela em branco para brincar e desenhar quadro a quadro o que quiser:

The basic idea behind Remotion is that we'll give you a frame number and a blank canvas, and the freedom to render anything you want using React.

Tendo isso em mente, precisamos entender a estrutura básica que nos permite gerar nossos primeiros vídeos.

Instalação

Para renderização de vídeo o Remotion usa o ffmpeg e o ffprobe, então você precisar checar se em sua máquina esses softwares estão instalados e se o PATH do sistema aponta para eles.

Por fim, será necessário ter instalado o bom e velho Node.Js (Versão 14 ou Superior) e o NPM.

Criação do Projeto

Para criar um projeto, basta digitar o seguinte comando no terminal:

npm init video

Eu particularmente utilizo o yarn, para isso basta digitar o seguinte comando:

yarn create video

Após executar o comando você verá uma tela semelhante a essa:

Nome do Projeto

Dê um nome para seu projeto, nesse exemplo eu nomeei de ola-mundo. Após isso você irá para uma tela com muitos templates legais para você escolher e começar a fazer seus vídeos.

Escolha de Templates

Eu escolhi o template que já vem configurado o Tailwind, se o Tailwind não for de sua preferência, você pode começar no template Blank. Depois que você estiver mais familiarizado, dê uma olhada nos outros templates, eu achei fenomenal o template Text To Speech.

Após a escolha do template, seu projeto já estará criado, bastando apenas abrir em uma IDE ou editor de código de sua escolha.

Estrutura do Projeto

Com o projeto criado, você terá uma estrutura no diretório src semelhante a essa:

Estrutura do Projeto

Os arquivos que devemos prestar atenção são: index.ts, Root.tsx, Composition.tsx e style.css.

O arquivo index.ts faz o registro do componente Root. O Arquvio Root.tsx é onde está o componente Root e é onde nossos "modos de renderização" são definidos por meio de composições, é onde também pode ser importados os estilos (css) e arquivos de fontes personalizado para o projeto, e por fim, o arquivo Composition.tsx é um componente onde estará toda lógica de nosso vídeo, herdando algumas informações do componente Root.

Composition

Uma Composition define a estrutura que o vídeo deverá seguir. Em um projeto podemos ter várias composições. Cada composição poderá ser utilizada para gerar um vídeo com configurações diferentes.

As composições são criadas no arquivo Root.tsx e definem a duração do vídeo (em frames por segundo), sua altura e largura e a taxa de quadros por segundo que o vídeo terá.

O legal das composições é que em um único projeto podemos gerar vídeos em vários formatos, como 16:9 e 4:3 com características e configurações diferentes, aproveitando os mesmos dados.

Esse é o modelo de uma composição padrão gerada pelo Remotion com Tailwind configurado:

//Root.tsx
import {Composition} from 'remotion';
import {MyComposition} from './Composition';
import './style.css';

export const RemotionRoot: React.FC = () => {
    return (
        <>
            <Composition
                id="MyComp"
                component={MyComposition}
                durationInFrames={240}
                fps={30}
                width={1280}
                height={720}
            />
        </>
    );
};

O Remotion renderiza cada composition como um vídeo, e deve ser informado no arquivo package.json o id da composition que será renderizada:

//package.json
{
"scripts": {
    "build": "remotion render MyComp out/video.mp4",
}

Com isso em mente, basta informar o id da composição para o vídeo que você deseja gerar.

Por exemplo, para um projeto que exporta tanto para shorts como para vídeos longos, podemos ter duas composições com as seguintes configurações:

//Root.tsx
import {Composition} from 'remotion';
import {Video} from './Video';
import {Shorts} from './Shorts';
import './style.css';

export const RemotionRoot: React.FC = () => {
    return (
        <>
            <Composition
                id="Video"
                component={Video}
                durationInFrames={18000} //Duração de 10 minutos (10min * 60sec * 30fps)
                fps={30}
                width={3840} //Vídeo em 4k
                height={2160}
            />
            <Composition
                id="Shorts"
                component={Shorts}
                durationInFrames={1800} // Duração de 1 minuto (60sec * 30fps)
                fps={30}
                width={2160} //Shorts em 4k
                height={3840}
            />
        </>
    );
};

Definido as composições, temos criar os componentes que elas apontam, no exemplo acima, as composições apontam para os componentes Video e Shorts. Poderíamos ter apenas um componente para ambas as composições, mas a meu ver é mais pratico separar em vários componentes para vídeos com formatos diferentes.

Por fim, as composições ainda permitem passar informações para os componentes que elas chamam por meio do defaultProps, como no exemplo a seguir:

//Root.tsx
import {Composition} from 'remotion';
import {Video} from './Composition';
import {Shorts] from './Shorts';
import './style.css';

export const RemotionRoot: React.FC = () => {
    const content = {
        shorts: 'conteúdo',
        video: 'conteúdo'
    }

    return (
        <>
            <Composition
                id="Video"
                component={Video}
                durationInFrames={18000} //Duração de 10 minutos (10min * 60sec * 30fps)
                fps={30}
                width={3840} //Vídeo em 4k
                height={2160}
                defaultProps={{content: content.video}}
            />
            <Composition
                id="Shorts"
                component={Shorts}
                durationInFrames={1800} // Duração de 1 minuto (60sec * 30fps)
                fps={30}
                width={2160} //Shorts em 4k
                height={3840}
                defaultProps={{content: content.shorts}}
            />
        </>
    );
};

Com isso, é possível fazer consulta a alguma api e depois passar as informações para os componentes que gerarão o vídeo.

MyComposition

O componente MyComposition é onde de fato vamos começar a desenhar nosso vídeo. Ele é atualizado quadro a quadro e tem a duração e fps que foi definido na Composition que o chamou.

Sua estrutura é parecida com o código abaixo:

//Composition.tsx
export const MyComposition = () => {
    return (
        <div className="bg-white text-black">Olá, mundo!!!</div>
    );
}

E se tivermos passado alguma props na composição, podemos ainda ter um componente defindo como:

//Composition.tsx
export const MyComposition: React.FC<{
    content: string
}> = ({content}) => {
    return (
        <div className="bg-white text-black">{content}</div>
    );
}

Apesar de simplório, do jeito que está será gerado um vídeo com fundo branco e o texto "olá, mundo". Tudo muito estático, mas funcional.

Hooks

Para deixar o vídeo um pouco mais dinâmico devemos conhecer os 2 hooks principais do Remotion: useCurrentFrame e useVideoConfig.

useCurrentFrame

O hook useCurrentFrame retorna o quadro atual em que o vídeo se encontra:

//Composition.tsx
import {useCurrentFrame} from "remotion"
export const MyComposition = () => {
    
    const currentFrame = useCurrentFrame()
    
    return (
        <div className="bg-white text-black">Olá, mundo!!! O frame atual é: {currentFrame}</div>
    );
}

O useCurrentFrame inicia em 0 e tem como valor máximo a duração total do vídeo em fps - 1. Esse hook é muito útil, principalmente para controlar animações.

useVideoConfig

O useVideoConfig retorna uma serie de informações sobre a composição do vídeo. Dentre elas: a quantos fps o vídeo está sendo renderizado, sua altura e largura, além da duração total do vídeo em fps.

//Composition.tsx
import {useCurrentFrame, useVideoConfig} from "remotion"
export const MyComposition = () => {
    
    const currentFrame = useCurrentFrame()
    const {fps, durationInFrames, width, height} = useVideoConfig()
    
    return (
        <div className="bg-white text-black">
            Resolução do Vídeo: {width}x{height}<br />
            Tempo total: {durationInFrames / fps}<br />
            Taxa de atualização: {fps}fps<br />
            Tempo atual: {currentFrame / fps}
        </div>
    );
}

Por meio desse hook, combinado com o hook do useCurrentFrame, podemos controlar como as informações serão apresentadas ao longo do vídeo.

Componentes

Agora que conhecemos os dois hooks mais utilizados, vamos ver dois componentes interessantes para usarmos em nossos projetos.

AbsoluteFill

O AbsoluteFill é um componente que já traz uma estilização padrão com as seguintes características:

//https://www.remotion.dev/docs/absolute-fill
const style: React.CSSProperties = {
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  width: "100%",
  height: "100%",
  display: "flex",
  flexDirection: "column",
};

A utilização dele é simples e direta:

//Composition.tsx
import {AbsoluteFill} from "remotion"
export const MyComposition = () => {
    return (
        <AbsoluteFill className="bg-white text-black">Olá, mundo!!!</AbsoluteFill>
    );
}

Você pode sobrescrevêr a estilização padrão sem problemas, e geralmente é muito prático começar o vídeo com esse componente, pois já tem um display flex e uma altura e largura total.

Sequence

Esse é o componente mais importante para controlar as informações que serão apresentadas. Sua função é controlar a ordem e duração que cada informação deve aparecer no vídeo.

//Composition.tsx
import {AbsoluteFill, Sequence} from "remotion"
export const MyComposition = () => {
    return (
        <AbsoluteFill className="bg-white text-black">
            <Sequence durationInFrames={120}>Olá, mundo!!!</Sequence>
            <Sequence from={120} durationInFrames={150}>Obrigado por Assistir</Sequence>
        </AbsoluteFill>
    );
}

A propriedade durationInFrame define por quanto tempo uma sequência deve ser exibida em tela, já a propriedade from informa em qual frame do vídeo deve ser começado a exibir aquela sequência. Se não informamos o from, o remotion assume que começará no frame 0, já se não informamos a durationInFrames, será entendido com a duração máxima o vídeo.

No exemplo acima, o texto "olá mundo" será exibido desde o frame 0 até o frame 120, com duração de 4 segundos (120fps / 30fps), já o texto "Obrigado por assistir" terá início no frame 120 e irá até o frame 270 com duração de 5 segundos (150fps / 30fps).

Ainda é importante ressaltar que cada<Sequence> tem uma posição absoluta, se esse comportamento não for desejado, deve-se aplicar o layout="none".

//Composition.tsx
import {AbsoluteFill, Sequence} from "remotion"
export const MyComposition = () => {
    return (
        <AbsoluteFill className="bg-white text-black">
            <Sequence durationInFrames={120} layout="none">Olá, mundo!!!</Sequence>
            <Sequence from={120} durationInFrames={150} layout="none">Obrigado por Assistir</Sequence>
        </AbsoluteFill>
    );
}

Esse componente pode ser ainda mais dinâmico se combinado com os hooks useCurrenteFrame e useVideoConfig, sendo possível flexibilizar e automatizar a duração de cada informação a ser exibida.

Preview

O Remotion possui um preview muito legal, assim não é preciso fazer build cada vez que fazer uma mudança no projeto para ver como ficou o resultado.

npm start

ou

yarn start

E como resultado temos a visualização do seguinte código:

//Composition.tsx
import {AbsoluteFill, Sequence} from "remotion"
export const MyComposition = () => {
    return (
        <AbsoluteFill className="bg-white text-black justify-center items-center text-center text-9xl">
            <Sequence durationInFrames={120} layout="none">Olá, mundo!!!</Sequence>
            <Sequence from={120} durationInFrames={150} layout="none">Obrigado por Assistir</Sequence>
        </AbsoluteFill>
    );
}

Remotion Preview

O preview também funciona para conteúdos buscado de api's ou que precise aguardar alguma tarefa assíncrona.

Considerações Finais

Como esse é apenas um "olá, mundo", muita coisa acabou ficando de fora. Em outras postagens poderá ser abordado as funções de animação, componentes de som, vídeo e imagens, também como fazer requisições a api's e até Text To Speech para narração automática. Espero que tenham gostado da publicação.

Eu amo o Remtion, incluse quando tava estudando criei um video que bate na api aqui do tabnews para buscar os conteudos e criar um video. Não consegui colocar video aqui, mas deixei o video no imgur pra você vê

https://imgur.com/a/2CTeiKy

Ual, esse projeto ficou sensacional, gostei muito das animações que você usou. O legal é que dá para complementar com uma api de text-to-speech como da Azure e adicionar narração a esse conteúdo.
text-to-speech eu prefiro usar a ElevenLabs, fica incrivel
Realmente, eu andei vendo alguns vídeos no YouTube e a qualidade é sensacional, ainda não brinquei com essa api, mas espero poder testar ela em breve. Vi que tem até como clonar nossa voz, muito louco isso, é impressionante como as ferramentas estão evoluindo e ficando cada vez mais simples de usar.