Можете ли вы вызвать FFMPEG в функции Firebase Cloud

в документации по облачным функциям Firebase вы можете использовать ImageMagick из облачной функции:https://firebase.google.com/docs/functions/use-cases

возможно ли сделать что-то подобное, но вызвать FFMPEG вместо ImageMagick? Хотя миниатюры изображений великолепны, я также хотел бы добавить входящие изображения в видеофайл, хранящийся в хранилище Firebase.

5 ответов


ffmpeg не предустановлен (в значительной степени просто ImageMagick); чтобы увидеть, что именно установлено, проверьте файл Dockerfile здесь: https://github.com/GoogleCloudPlatform/nodejs-docker/blob/master/runtime-image/Dockerfile - ...

однако вы можете загружать произвольные двоичные файлы при загрузке кода с помощью gcloud beta functions deploy потому что все в текущем каталоге (за исключением node_modules) будет загружен.

Примечание: у вас есть только доступ для записи на диск at /tmp/.

Вариант 1: Используйте ffmpeg-статический модуль npm

ffmpeg-static - это модуль npm, который строит правильный двоичный файл ffmpeg на основе текущей системы во время npm install. Поскольку облачные функции строят ваш код в облаке, он построит правильный двоичный файл ffmpeg.

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

вы можете увидеть его в действии в облачные функции для примеров 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');

(спасибо Daniel Lessa за указание этого модуля в ответ.)

Вариант 2: загрузите свой собственный двоичный файл

вы можете включить двоичный файл ffmpeg как часть загрузки, а затем запустить команду оболочки, используя что-то вроде child_process.exec. Вам понадобится двоичный файл ffmpeg, скомпилированный для целевой платформы (Debian / jessie).

список файлов с предварительно скомпилированный двоичный файл ffmpeg

./
../
index.js
ffmpeg

затем запустите, например gcloud beta functions deploy myFunc --trigger-http

.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
});

используйте 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()

хотя технически вы можете запустить FFMPEG на экземпляре функций Firebase,вы быстро достигнете небольших квот.

по состоянию на ответ, вместо этого вы можете использовать функции для запуска запроса на более мощный движок приложений GCP или вычислительные службы. Процесс App Engine может захватить файл из того же ведра, обрабатывать транскодирование и загрузить готовый файл обратно в ведро. Если вы проверите другие ответы по ссылке, один пользователь разместил образец РЕПО, который делает именно это.


насколько я знаю, ffmpeg не предварительно установлен на контейнерах.

когда мне нужно было перекодировать носитель, я заглянул в emscripten кросс-компиляции версию ffmpeg. Казалось, что он отлично работает в средах локальных узлов, но я никогда не собирался его развертывать (так как нам не нужно было перекодировать в конце концов).


/**
 * 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