web-dev-qa-db-fra.com

Pouvez-vous appeler FFMPEG dans une fonction Cloud Firebase

Selon la documentation de Firebase Cloud Functions, vous pouvez utiliser ImageMagick à partir d'une fonction de cloud: https://firebase.google.com/docs/functions/use-cases

Est-il possible de faire quelque chose de similaire mais faire appel à FFMPEG au lieu d'ImageMagick? Bien que miniaturiser les images soit une bonne chose, j'aimerais également pouvoir ajouter des images entrantes à un fichier vidéo stocké sur Firebase Storage.

11
Dave Parks

ffmpeg n'est pas préinstallé (à peu près juste ImageMagick); pour voir exactement ce qui est installé, consultez le fichier Dockerfile ici: https://github.com/GoogleCloudPlatform/nodejs-docker/blob/master/runtime-image/Dockerfile

Toutefois, vous pouvez télécharger des fichiers binaires arbitraires lorsque vous téléchargez votre code à l'aide de gcloud beta functions deploy, car tout le contenu du répertoire en cours (à l'exception de node_modules) est chargé.

Remarque: vous n'avez qu'un accès en écriture sur le disque à /tmp/.

Option 1: utiliser le module npm ffmpeg-static

ffmpeg-static est un module npm qui crée le binaire ffmpeg approprié en fonction du système actuel pendant npm install. Puisque Cloud Functions construit votre code dans le cloud, il créera le binaire ffmpeg approprié.

https://github.com/eugeneware/ffmpeg-static

Vous pouvez le voir en action dans le référentiel Cloud Functions for Firebase .

const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');

var cmd = ffmpeg('/tmp/video.avi')
  .setFfmpegPath(ffmpeg_static.path)
  .videoBitrate(1024)
  .videoCodec('divx')
  .format('avi')
  .on('end', () => {
    // ...
  })
  .on('error', err => {
    console.error(err);
  })
  .save('/tmp/file-out.avi');

(Merci Daniel Lessa pour avoir souligné ce module dans sa réponse .)

Option 2: télécharger votre propre binaire

Vous pouvez inclure un fichier binaire ffmpeg dans le cadre du téléchargement, puis exécuter une commande Shell à l'aide de quelque chose comme child_process.exec . Vous aurez besoin du fichier binaire ffmpeg compilé pour la plate-forme cible (Debian/jessie).

Liste de fichiers avec le binaire ffmpeg pré-compilé

./
../
index.js
ffmpeg

Puis lancez par exemple gcloud beta functions deploy myFunc --trigger-http

index.js

var exec = require('child_process').exec;
var cmd = 'ffmpeg -i /tmp/myvideo.mp4 /tmp/image-%d.jpg';

exec(cmd, function(error, stdout, stderr) {
  // command output is in stdout
});
25
Bret McGowen

Utilisez la lib https://github.com/eugeneware/ffmpeg-static

const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');


let cmd = ffmpeg.('filePath.mp4')
   .setFfmpegPath(ffmpeg_static.path)
   .setInputFormat('mp4')
   .output('outputPath.mp4')
   ...
   ...
   .run()
5
Daniel Lessa

Bien que vous puissiez techniquement exécuter FFMPEG sur une instance de Firebase Functions, vous atteindrez rapidement les petites limites de quota. 

Selon cette réponse , vous pouvez utiliser à la place Fonctions pour déclencher une demande auprès des services plus puissants de GCP App Engine ou Compute Engine. Le processus App Engine peut extraire le fichier du même compartiment, gérer le transcodage et télécharger le fichier final dans le compartiment. Si vous cochez les autres réponses sur le lien, un utilisateur a posté un exemple de dépôt qui fait exactement cela. 

3
ajabeckett
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.Apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for t`he specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();
const path = require('path');
const os = require('os');
const fs = require('fs');
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');

/**
 * When an audio is uploaded in the Storage bucket We generate a mono channel audio automatically using
 * node-fluent-ffmpeg.
 */
exports.generateMonoAudio = functions.storage.object().onChange(event => {
  const object = event.data; // The Storage object.

  const fileBucket = object.bucket; // The Storage bucket that contains the file.
  const filePath = object.name; // File path in the bucket.
  const contentType = object.contentType; // File content type.
  const resourceState = object.resourceState; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions).
  const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.

  // Exit if this is triggered on a file that is not an audio.
  if (!contentType.startsWith('audio/')) {
    console.log('This is not an audio.');
    return;
  }

  // Get the file name.
  const fileName = path.basename(filePath);
  // Exit if the audio is already converted.
  if (fileName.endsWith('_output.flac')) {
    console.log('Already a converted audio.');
    return;
  }

  // Exit if this is a move or deletion event.
  if (resourceState === 'not_exists') {
    console.log('This is a deletion event.');
    return;
  }

  // Exit if file exists but is not new and is only being triggered
  // because of a metadata change.
  if (resourceState === 'exists' && metageneration > 1) {
    console.log('This is a metadata change event.');
    return;
  }

  // Download file from bucket.
  const bucket = gcs.bucket(fileBucket);
  const tempFilePath = path.join(os.tmpdir(), fileName);
  // We add a '_output.flac' suffix to target audio file name. That's where we'll upload the converted audio.
  const targetTempFileName = fileName.replace(/\.[^/.]+$/, "") + '_output.flac';
  const targetTempFilePath = path.join(os.tmpdir(), targetTempFileName);
  const targetStorageFilePath = path.join(path.dirname(filePath), targetTempFileName);

  return bucket.file(filePath).download({
    destination: tempFilePath
  }).then(() => {
    console.log('Audio downloaded locally to', tempFilePath);
    // Convert the audio to mono channel using FFMPEG.
    const command = ffmpeg(tempFilePath)
      .setFfmpegPath(ffmpeg_static.path)    
      .audioChannels(1)
      .audioFrequency(16000)
      .format('flac')
      .on('error', (err) => {
        console.log('An error occurred: ' + err.message);
      })
      .on('end', () => {
        console.log('Output audio created at', targetTempFilePath);

        // Uploading the audio.
        return bucket.upload(targetTempFilePath, {destination: targetStorageFilePath}).then(() => {
          console.log('Output audio uploaded to', targetStorageFilePath);

          // Once the audio has been uploaded delete the local file to free up disk space.     
          fs.unlinkSync(tempFilePath);
          fs.unlinkSync(targetTempFilePath);

          console.log('Temporary files removed.', targetTempFilePath);
        });
      })
      .save(targetTempFilePath);
  });
});

https://github.com/firebase/functions-samples/blob/master/ffmpeg-convert-audio/functions/index.js

0
Henry

Pratiquement, non. FFMPEG traite les fichiers audio/vidéo qui dépassent généralement les quotas Fonctions Cloud ((téléchargements 10 Mo).

Vous devrez exécuter Node.js sur AppEngine de GCP.

0
Ron Royston

Autant que je sache, ffmpeg n'est pas préinstallé sur les conteneurs.

Lorsque j’ai eu besoin de transcoder un support, j’ai cherché dans une version emscripten de la compilation croisée de ffmpeg . Cela semblait bien fonctionner dans les environnements de nœuds locaux, mais je n'ai jamais eu le temps de le déployer (car nous n'avions pas besoin de transcoder après tout).

0
Frank van Puffelen