filipedeschamps / video-maker

Projeto open source para fazer vídeos automatizados
MIT License
2.43k stars 631 forks source link

Using ffmeg as a alternative to AfterEfects #143

Open Krymancer opened 5 years ago

Krymancer commented 5 years ago

Procurando outras maneiras, para não nos predemos a ferramentas como o AfterEfects, poderíamos usar simplesmente algo como o FFmpeg que é capaz de fazer coisas como essa aqui, inclusive procurando um pouco no git encontrei alguns node modules como esse e esse aqui, ou seja não acho que seria bom nos prendermos a ferramentas a quais embora poderosas não tenhamos tanta liberdade, embora isso se complique em algumas etapas provavelmente o resultado sera maravilhoso.

rattones commented 5 years ago

Top esse algoritmo de detecção de movimento que esse cara fez com o FFmpeg, já deu pra pensar num monte de usabilidade pra isso em sistemas de segurança ...

marcusbrandt commented 5 years ago

Eu encontrei o libopenshot que é uma lib do OpenShot, parece ser uma alternativa interessante. Infelizmente não tive tempo de testar nenhuma das ferramentas, mas acredito que vale dar uma olhada.

Patricia7sp commented 5 years ago

Ola @Krymancer @rattones @marcusbrandt tudo bem com voces? Eu achei muito legal este ffmpeg e resolvi usa-lo, ainda mais pq eu uso o linux. Mas queria ver se voces conseguem me dar um help. Esta me aparecendo um erro no final,quando executo o node index.js, ele nao chega a criar o video. Nao sei se o meu codigo do video.js esta totalmente correto ( se esta faltando adicionar algo ou ate mesmo retirar algo). Vou enviar para voces darem uma olhada se puderem

Patricia7sp commented 5 years ago
const gm = require('gm').subClass({imageMagick: true})
const state = require('./state.js')
const spawn = require('child_process').spawn
const path = require('path')
const rootPath = path.resolve(__dirname, '..')
const ffmpeg = require('ffmpeg')

async function robot() {
  const content = state.load()

  await convertAllImages(content)
  await createAllSentenceImages(content)
  await createYouTubeThumbnail()
  await createFFmpeg(content)
  await renderVideoWithFFmpeg()

  state.save(content)

  async function convertAllImages(content) {
    for (let sentenceIndex = 0; sentenceIndex < content.sentences.length; sentenceIndex++) {
      await convertImage(sentenceIndex)
    }
  }

  async function convertImage(sentenceIndex) {
    return new Promise((resolve, reject) => {
      const inputFile = `./content/${sentenceIndex}-original.png[0]`
      const outputFile = `./content/${sentenceIndex}-converted.png`
      const width = 1920
      const height = 1080

      gm()
        .in(inputFile)
        .out('(')
          .out('-clone')
          .out('0')
          .out('-background', 'white')
          .out('-blur', '0x9')
          .out('-resize', `${width}x${height}^`)
        .out(')')
        .out('(')
          .out('-clone')
          .out('0')
          .out('-background', 'white')
          .out('-resize', `${width}x${height}`)
        .out(')')
        .out('-delete', '0')
        .out('-gravity', 'center')
        .out('-compose', 'over')
        .out('-composite')
        .out('-extent', `${width}x${height}`)
        .write(outputFile, (error) => {
          if (error) {
            return reject(error)
          }

          console.log(`> Image converted: ${inputFile}`)
          resolve()
        })

    })
  }

  async function createAllSentenceImages(content) {
    for (let sentenceIndex = 0; sentenceIndex < content.sentences.length; sentenceIndex++) {
      await createSentenceImage(sentenceIndex, content.sentences[sentenceIndex].text)
    }
  }

  async function createSentenceImage(sentenceIndex, sentenceText) {
    return new Promise((resolve, reject) => {
      const outputFile = `./content/${sentenceIndex}-sentence.png`

      const templateSettings = {
        0: {
          size: '1920x400',
          gravity: 'center'
        },
        1: {
          size: '1920x1080',
          gravity: 'center'
        },
        2: {
          size: '800x1080',
          gravity: 'west'
        },
        3: {
          size: '1920x400',
          gravity: 'center'
        },
        4: {
          size: '1920x1080',
          gravity: 'center'
        },
        5: {
          size: '800x1080',
          gravity: 'west'
        },
        6: {
          size: '1920x400',
          gravity: 'center'
        }

      }

      gm()
        .out('-size', templateSettings[sentenceIndex].size)
        .out('-gravity', templateSettings[sentenceIndex].gravity)
        .out('-background', 'transparent')
        .out('-fill', 'white')
        .out('-kerning', '-1')
        .out(`caption:${sentenceText}`)
        .write(outputFile, (error) => {
          if (error) {
            return reject(error)
          }

          console.log(`> Sentence created: ${outputFile}`)
          resolve()
        })
    })
  }

  async function createYouTubeThumbnail() {
    return new Promise((resolve, reject) => {
      gm()
        .in('./content/0-converted.png')
        .write('./content/youtube-thumbnail.jpg', (error) => {
          if (error) {
            return reject(error)
          }

          console.log('> Creating YouTube thumbnail')
          resolve()
        })
    })
  }

  async function createFFmpeg(content) {
    await state.saveScript(content)
  }

  async function renderVideoWithFFmpeg() {
    return new Promise((resolve, reject) => {
      const aerenderFilePath = '/Applications/ffmpeg/aerender'
      const templateFilePath = `${rootPath}/templates/1/template.aep`
      const destinationFilePath = `${rootPath}/content/output.mov`

      console.log('> Starting  ffmpeg')

      const aerender = spawn(aerenderFilePath, [
        '-comp', 'main',
        '-project', templateFilePath,
        '-output', destinationFilePath
      ])

      aerender.stdout.on('data', (data) => {
        process.stdout.write(data)
      })

      try {
        const process = new ffmpeg('/path/to/your_movie.avi');
        process.then(function (video) {

            video.fnExtractSoundToMP3('/path/to/your_audio_file.mp3', function (error, file) {
                if (!error)
                    console.log('Audio file: ' + file);
            });
        }, function (err) {
            console.log('Error: ' + err);
        });
    } catch (e) {
        console.log(e.code);
        console.log(e.msg);
    }

      aerender.on('close', () => {
        console.log('> ffmpeg closed')
        resolve()
      })
    })
  }

}

module.exports = robot
Patricia7sp commented 5 years ago

ERRO

The input file does not exist events.js:173 throw er; // Unhandled 'error' event ^

Error: spawn /Applications/ffmpeg/aerender ENOENT at Process.ChildProcess._handle.onexit (internal/child_process.js:246:19) at onErrorNT (internal/child_process.js:427:16) at processTicksAndRejections (internal/process/next_tick.js:76:17) Emitted 'error' event at: at Process.ChildProcess._handle.onexit (internal/child_process.js:252:12) at onErrorNT (internal/child_process.js:427:16) at processTicksAndRejections (internal/process/next_tick.js:76:17)

Krymancer commented 5 years ago

function convertImage(sentenceIndex) { return new Promise((resolve, reject

Pelo erro que você mostrou parece ser algo faltando, você esta passando as imagens por parametro de maneira correta? fora que o ffmepg n usa esse template do after efects que estar no seu código, tenta dar uma utilizada no ffmeg sozinho e ver se tu renderiza um video e tanta aplicar ai

Patricia7sp commented 5 years ago

Muito obrigada pela atencao! @Krymancer. Peco ate desculpas, eu ainda sou bastante leiga perto de voces na area de programacao, vai fazer uma ano que estou estudando, nao tenho muita expertise.

felvieira commented 5 years ago

Eu fiz um pr pro projeto com uma solução parecida, e ficou bem usável. Deem uma olhada lá.

sephilin commented 5 years ago

@felvieira

Eu fiz um pr pro projeto com uma solução parecida, e ficou bem usável. Deem uma olhada lá.

Você conseguiu produzir o vídeo? Eu tentei o seu método mas ele diz o seguinte:

Error: Error: ffmpeg exited with code 1: Error reinitializing filters! Failed to inject frame into filter network: Invalid argument Error while processing the decoded data for stream #0:0 Conversion

Eu consegui usar o videoshow mas de outra forma.

felvieira commented 5 years ago

Cara esse erro deve ser pq as imagens que vc ta retornando pra fazer o video nao tem as mesmas dimensões. Verifica se é isso mesmo, coloca as imagens todas com o mesmo tamanho e manda bala que é pra ir redondinho.

Depois verifica se ta td instalado certinhos todas as libs que o fffmpeg requer.

wfrsilva commented 5 years ago

Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere?

sephilin commented 5 years ago

Se quiser eu mando minha versão. Pode ser?

Le ven. 19 juill. 2019 09 h 42, WENDEL FABIANO RIBEIRO DA SILVA < notifications@github.com> a écrit :

Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/filipedeschamps/video-maker/issues/143?email_source=notifications&email_token=ACOACZZMRZPYGMU4URJLG43QAHAFZA5CNFSM4HHT4K72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LVDUQ#issuecomment-513233362, or mute the thread https://github.com/notifications/unsubscribe-auth/ACOACZ2CFSQHS5AEYL6LHFLQAHAFZANCNFSM4HHT4K7Q .

felvieira commented 5 years ago

Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere?

Testei no Windows e no Linux e foi tranquilo. O que pega eh saber instalar o ffmeg no windows ( tem algumas coisas que precisa configurar na mão ) . Mas no linux eh de boa.

wfrsilva commented 5 years ago

Se quiser eu mando minha versão. Pode ser? Le ven. 19 juill. 2019 09 h 42, WENDEL FABIANO RIBEIRO DA SILVA < notifications@github.com> a écrit : Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere? — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#143?email_source=notifications&email_token=ACOACZZMRZPYGMU4URJLG43QAHAFZA5CNFSM4HHT4K72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LVDUQ#issuecomment-513233362>, or mute the thread https://github.com/notifications/unsubscribe-auth/ACOACZ2CFSQHS5AEYL6LHFLQAHAFZANCNFSM4HHT4K7Q .

Po, se puder enviar, fico muito grato mesmo...

sephilin commented 5 years ago

Se quiser eu mando minha versão. Pode ser? Le ven. 19 juill. 2019 09 h 42, WENDEL FABIANO RIBEIRO DA SILVA < notifications@github.com> a écrit : Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere? — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#143?email_source=notifications&email_token=ACOACZZMRZPYGMU4URJLG43QAHAFZA5CNFSM4HHT4K72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LVDUQ#issuecomment-513233362>, or mute the thread https://github.com/notifications/unsubscribe-auth/ACOACZ2CFSQHS5AEYL6LHFLQAHAFZANCNFSM4HHT4K7Q .

Po, se puder enviar, fico muito grato mesmo...

Segue o link então, fiz uns ajustes, por que minha versão na maquina já está muito alterada, dai peguei a versao mais basica possivel para que você possa, a partir dai, fazer suas alteraçoes ... só clonar, da o npm install e ir pra galera... Obs: A legenda esta azul, se quiser trocar a cor, altera o "PrimaryColour" dentro do robo video.js Na minha versão atual, ele tem tratamento para varios erros, mas essa versão que te passei está bem crua! https://github.com/sephilin/video-maker

wfrsilva commented 5 years ago

Se quiser eu mando minha versão. Pode ser? Le ven. 19 juill. 2019 09 h 42, WENDEL FABIANO RIBEIRO DA SILVA < notifications@github.com> a écrit : Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere? — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#143?email_source=notifications&email_token=ACOACZZMRZPYGMU4URJLG43QAHAFZA5CNFSM4HHT4K72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LVDUQ#issuecomment-513233362>, or mute the thread https://github.com/notifications/unsubscribe-auth/ACOACZ2CFSQHS5AEYL6LHFLQAHAFZANCNFSM4HHT4K7Q .

Po, se puder enviar, fico muito grato mesmo...

Segue o link então, fiz uns ajustes, por que minha versão na maquina já está muito alterada, dai peguei a versao mais basica possivel para que você possa, a partir dai, fazer suas alteraçoes ... só clonar, da o npm install e ir pra galera... Obs: A legenda esta azul, se quiser trocar a cor, altera o "PrimaryColour" dentro do robo video.js Na minha versão atual, ele tem tratamento para varios erros, mas essa versão que te passei está bem crua! https://github.com/sephilin/video-maker

Rodou, show, tem opcoa de portugues ...

Meu pt deu problema, ingles gerou 👍 Escolha a linguagem [1, 2, 3, 0]: 2 (node:10792) UnhandledPromiseRejectionWarning: HTTPConnectionPool(host='pt-br.wikipedia.org', port=80): Max retries exceeded with url: /w/api.php?srinfo=suggestion&srsearch=Mandela&format=json&srlimit=1&list=search&srprop=&limit=1&action=query (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fd717754a10>: Failed to establish a new connection: [Errno -2] Name or service not known',)) (node:10792) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:10792) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

ghost commented 5 years ago

= = Consegue sim, pelo menos no Mac. Vou publucar em breve meu codigo completo. Pode seguir a risca as instrucoes q funciona.

Enviado do meu iPhone

Em 19 de jul de 2019, à(s) 11:05, Vitor Justiniano Menezes notifications@github.com escreveu:

Se quiser eu mando minha versão. Pode ser?

Le ven. 19 juill. 2019 09 h 42, WENDEL FABIANO RIBEIRO DA SILVA < notifications@github.com> a écrit :

Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/filipedeschamps/video-maker/issues/143?email_source=notifications&email_token=ACOACZZMRZPYGMU4URJLG43QAHAFZA5CNFSM4HHT4K72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LVDUQ#issuecomment-513233362, or mute the thread https://github.com/notifications/unsubscribe-auth/ACOACZ2CFSQHS5AEYL6LHFLQAHAFZANCNFSM4HHT4K7Q .

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

leodutra commented 4 years ago

@ghost algum progresso?

antonino3g commented 4 years ago

Consegui fazer! O repositório do @felvieira quebrou um galhão . Adaptei somente para o uso do FFpmeg, só uso linux pra codar. Contudo, FFpmeg é uma solução FREE, rodando em qualquer SO :sunglasses:. As dependências pra rodar são:


# Videoshow
$ npm i videoshow

# Ffmpeg
npm install ffmpeg

# @ffmpeg-installer/ffmpeg
npm install --save @ffmpeg-installer/ffmpeg

# @ffprobe-installer/ffprobe
npm install --save @ffprobe-installer/ffprobe
felvieira commented 4 years ago

Consegui fazer! O repositório do @felvieira quebrou um galhão . Adaptei somente para o uso do FFpmeg, só uso linux pra codar. Contudo, FFpmeg é uma solução FREE, rodando em qualquer SO 😎. As dependências pra rodar são:

# Videoshow
$ npm i videoshow

# Ffmpeg
npm install ffmpeg

# @ffmpeg-installer/ffmpeg
npm install --save @ffmpeg-installer/ffmpeg

# @ffprobe-installer/ffprobe
npm install --save @ffprobe-installer/ffprobe

Mto bom @apfjunior ! O ffmpg da uma dorzinha de cabeca pra configurar no windows msm

EMQuirino commented 4 years ago

Se quiser eu mando minha versão. Pode ser? Le ven. 19 juill. 2019 09 h 42, WENDEL FABIANO RIBEIRO DA SILVA < notifications@github.com> a écrit : Alguém conseguiu substituir ? Vejo varias tentativas, mas ate agora parece que funcionando mesmo, ninguém conseguiu substituir o AE. Confere? — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#143?email_source=notifications&email_token=ACOACZZMRZPYGMU4URJLG43QAHAFZA5CNFSM4HHT4K72YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LVDUQ#issuecomment-513233362>, or mute the thread https://github.com/notifications/unsubscribe-auth/ACOACZ2CFSQHS5AEYL6LHFLQAHAFZANCNFSM4HHT4K7Q .

Po, se puder enviar, fico muito grato mesmo...

Segue o link então, fiz uns ajustes, por que minha versão na maquina já está muito alterada, dai peguei a versao mais basica possivel para que você possa, a partir dai, fazer suas alteraçoes ... só clonar, da o npm install e ir pra galera... Obs: A legenda esta azul, se quiser trocar a cor, altera o "PrimaryColour" dentro do robo video.js Na minha versão atual, ele tem tratamento para varios erros, mas essa versão que te passei está bem crua! https://github.com/sephilin/video-maker

Rodou, show, tem opcoa de portugues ...

Meu pt deu problema, ingles gerou 👍 Escolha a linguagem [1, 2, 3, 0]: 2 (node:10792) UnhandledPromiseRejectionWarning: HTTPConnectionPool(host='pt-br.wikipedia.org', port=80): Max retries exceeded with url: /w/api.php?srinfo=suggestion&srsearch=Mandela&format=json&srlimit=1&list=search&srprop=&limit=1&action=query (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fd717754a10>: Failed to establish a new connection: [Errno -2] Name or service not known',)) (node:10792) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:10792) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Conseguiu fazer funcionar em pt-br, @wfrsilva ?