ffmpeg, работающий в облачной функции, молча терпит неудачу / никогда не завершается - PullRequest
0 голосов
/ 17 июня 2020

Я пытаюсь реализовать облачную функцию, которая будет запускать ffmpeg при загрузке корзины Google. Я играл со сценарием, основанным на https://kpetrovi.ch/2017/11/02/transcoding-videos-with-ffmpeg-in-google-cloud-functions.html

Исходный сценарий требует небольшой настройки, поскольку библиотека немного развивалась. Моя текущая версия находится здесь:

const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
const ffmpeg = require('fluent-ffmpeg');
const ffmpeg_static = require('ffmpeg-static');

console.log("Linking ffmpeg path to:", ffmpeg_static)
ffmpeg.setFfmpegPath(ffmpeg_static);

exports.transcodeVideo = (event, callback) => {
    const bucket = storage.bucket(event.bucket);
    console.log(event);
    if (event.name.indexOf('uploads/') === -1) {
        console.log("File " + event.name + " is not to be processed.")
        return;
    }

    // ensure that you only proceed if the file is newly createdxxs
    if (event.metageneration !== '1') {
        callback();
        return;
    }

    // Open write stream to new bucket, modify the filename as needed.
    const targetName = event.name.replace("uploads/", "").replace(/[.][a-z0-9]+$/, "");
    console.log("Target name will be: " + targetName);

    const remoteWriteStream = bucket.file("processed/" + targetName + ".mp4")
        .createWriteStream({
            metadata: {
                //metadata: event.metadata, // You may not need this, my uploads have associated metadata
                contentType: 'video/mp4', // This could be whatever else you are transcoding to
            },
        });

    // Open read stream to our uploaded file
    const remoteReadStream = bucket.file(event.name).createReadStream();

    // Transcode
    ffmpeg()
        .input(remoteReadStream)
        .outputOptions('-c:v copy') // Change these options to whatever suits your needs
        .outputOptions('-c:a aac')
        .outputOptions('-b:a 160k')
        .outputOptions('-f mp4')
        .outputOptions('-preset fast')
        .outputOptions('-movflags frag_keyframe+empty_moov')
        // https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/346#issuecomment-67299526
        .on('start', (cmdLine) => {
            console.log('Started ffmpeg with command:', cmdLine);
        })
        .on('end', () => {
            console.log('Successfully re-encoded video.');
            callback();
        })
        .on('error', (err, stdout, stderr) => {
            console.error('An error occured during encoding', err.message);
            console.error('stdout:', stdout);
            console.error('stderr:', stderr);
            callback(err);
        })
        .pipe(remoteWriteStream, { end: true }); // end: true, emit end event when readable stream ends
};

Эта версия работает правильно, и я вижу это в журналах:

2020-06-16 21:24:22.606  Function execution took 912 ms, finished with status: 'ok'
2020-06-16 21:24:52.902  Started ffmpeg with command: ffmpeg -i pipe:0 -c:v copy -c:a aac -b:a 160k -f mp4 -preset fast -movflags frag_keyframe+empty_moov pipe:1

Кажется, выполнение функции заканчивается до фактической команды ffmpeg, которая затем никогда не завершается.

Есть ли способ сделать ffmpeg «синхронным» или «блокирующим», чтобы он завершился до выполнения функции?

1 Ответ

2 голосов
/ 19 июня 2020

Из документации Google Cloud кажется, что функция должна принимать три аргумента: (data, context, callback) пробовали ли вы это или знаете, что context является необязательным? Из документации кажется, что, если функция принимает три аргумента, рассматривается как фоновая функция, если она принимает только два аргумента, рассматривается как фоновая функция, только если она возвращает Promise.

Больше, чем это другой момент:

1: здесь не вызывается функция callback, если в ваших тестах ваша функция завершилась с этой строкой журнала, это еще один момент, предполагающий, что вызов второго аргумента в качестве функции обратного вызова является обязательным шаг для завершения процесса sh:

if (event.name.indexOf('uploads/') === -1) {
    console.log("File " + event.name + " is not to be processed.")
    return;
}

Я бы посоветовал добавить еще console.log (или многие другие, если хотите), чтобы прояснить поток: в свой вопрос вы вставили только 1 строку журнала, это не так уж и полезно, чтобы сказать это регистрируется после строки системного журнала

ссылке, которую вы использовали в качестве учебного пособия, почти три года, возможно, облако Google тем временем изменило свой интерфейс.

Однажды было сказано, что если acceptint три аргумента, а не только два, не решает вашу проблему, вы можете попробовать изменить свою функцию в Promise:

exports.transcodeVideo = (event, callback) => new Promise((resolve, reject) => {
    const bucket = storage.bucket(event.bucket);
    console.log(event);
    if (event.name.indexOf('uploads/') === -1) {
        console.log("File " + event.name + " is not to be processed.")
        return resolve(); // or reject if this is an error case
    }

    // ensure that you only proceed if the file is newly createdxxs
    if (event.metageneration !== '1') {
        return resolve(); // or reject if this is an error case
    }

    // Open write stream to new bucket, modify the filename as needed.
    const targetName = event.name.replace("uploads/", "").replace(/[.][a-z0-9]+$/, "");
    console.log("Target name will be: " + targetName);

    const remoteWriteStream = bucket.file("processed/" + targetName + ".mp4")
        .createWriteStream({
            metadata: {
                //metadata: event.metadata, // You may not need this, my uploads have associated metadata
                contentType: 'video/mp4', // This could be whatever else you are transcoding to
            },
        });

    // Open read stream to our uploaded file
    const remoteReadStream = bucket.file(event.name).createReadStream();

    // Transcode
    ffmpeg()
        .input(remoteReadStream)
        .outputOptions('-c:v copy') // Change these options to whatever suits your needs
        .outputOptions('-c:a aac')
        .outputOptions('-b:a 160k')
        .outputOptions('-f mp4')
        .outputOptions('-preset fast')
        .outputOptions('-movflags frag_keyframe+empty_moov')
        // https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/346#issuecomment-67299526
        .on('start', (cmdLine) => {
            console.log('Started ffmpeg with command:', cmdLine);
        })
        .on('end', () => {
            console.log('Successfully re-encoded video.');
            resolve();
        })
        .on('error', (err, stdout, stderr) => {
            console.error('An error occured during encoding', err.message);
            console.error('stdout:', stdout);
            console.error('stderr:', stderr);
            reject(err);
        })
        .pipe(remoteWriteStream, { end: true }); // end: true, emit end event when readable stream ends
});

Надеюсь на это помогает.

...