Ошибка при редактировании видео в цикле с использованием ffmpeg

#javascript #node.js #video #ffmpeg #fluent-ffmpeg

Вопрос:

Я создал видеоредактор, и мне нужно вырезать несколько разделов в видео с помощью fluent-ffmpeg на узле. Я получаю эту ошибку при редактировании видео…

 error3:  Error: ffmpeg exited with code 1: Cannot find a matching stream for unlabeled input pad 1 on filter Parsed_concat_0

    at ChildProcess.<anonymous> (/Users/xbotpc/Work/[client_name]/[client_project_name]/node_modules/fluent-ffmpeg/lib/processor.js:182:22)
    at ChildProcess.emit (events.js:198:13)
    at ChildProcess.EventEmitter.emit (domain.js:466:23)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)
 

Я получаю эту ошибку, когда редактирую выходное видео во второй раз.

Весь вывод выглядит так…

 source /Users/xbotpc/Work/[client_name]/[client_project_name]/video_files/raw_1800150.mp4
outputVideoFilePath /Users/xbotpc/Work/[client_name]/[client_project_name]/video_files/OUTPUT_VIDEO_FILE_raw_1800150.mp4
Video Cut 1
Video Cut 2
Video stitched
startTime, endTime 1s 4s
source /Users/xbotpc/Work/[client_name]/[client_project_name]/video_files/OUTPUT_VIDEO_FILE_raw_1800150.mp4
outputVideoFilePath /Users/xbotpc/Work/[client_name]/[client_project_name]/video_files/OUTPUT_VIDEO_FILE_raw_1800150.mp4
Video Cut 1
Video Cut 2
error3:  Error: ffmpeg exited with code 1: Cannot find a matching stream for unlabeled input pad 1 on filter Parsed_concat_0

    at ChildProcess.<anonymous> (/Users/xbotpc/Work/[client_name]/[client_project_name]/node_modules/fluent-ffmpeg/lib/processor.js:182:22)
    at ChildProcess.emit (events.js:198:13)
    at ChildProcess.EventEmitter.emit (domain.js:466:23)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)
Video stitched error rimRafError null
deleteSection error ERROR3
 

Следующая моя реализация…

 const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');
const rimraf = require('rimraf');
const isEmpty = require('./isEmpty');
const path = require('path');
const ffprobePath = require('ffprobe-static').path;

const { VIDEO_EDITING_FILES_DIRECTORY } = process.env;

/**
 * @typedef {Object} VideoEdit 
 * @property {Function} VideoEdit.deleteSection
 * @property {Function} VideoEdit.generateScreenshots
 */

/**
 * Video Editing function
 * @param {Object} EditVideo input
 * @param {string} EditVideo.inputVideoFileName input file name.
 * @param {string} EditVideo.outputVideoFileName output file name.
 * @returns {Promise<VideoEdit>} VideoEdit
 */
function videoEdit({ inputVideoFileName, outputVideoFileName }) {
    // Setting the path of libraries.
    ffmpeg.setFfmpegPath(ffmpegPath);
    ffmpeg.setFfprobePath(ffprobePath);

    const outputFolder = VIDEO_EDITING_FILES_DIRECTORY;
    if (isEmpty(outputVideoFileName)) {
        outputVideoFileName = `OUTPUT_VIDEO_FILE_${inputVideoFileName}`;
    }
    const inputVideoFilePath = path.join(__dirname, `${VIDEO_EDITING_FILES_DIRECTORY}/${inputVideoFileName}`);
    let outputVideoFilePath = path.join(__dirname, `${VIDEO_EDITING_FILES_DIRECTORY}/${outputVideoFileName}`);

    return new Promise((resolve, reject) => {
        // ffprobe gets the metadata of file
        ffmpeg.ffprobe(inputVideoFilePath, (err, data) => {
            const duration = data.format.duration;
            resolve({
                /**
                 * Deletes a section of video.
                 * @param {Object} DeleteOptions Deletion options
                 * @param {string} DeleteOptions.startTime Time to start deleting
                 * @param {string} DeleteOptions.endTime Time to end deleting
                 * @returns {Promise<'DONE' | 'ERROR'>}
                 */
                deleteSection: ({ startTime, endTime }) => {
                    return new Promise((resolve1, reject1) => {

                        const fStartTime = '0';
                        const fDuration = startTime;
                        const sStartTime = endTime;
                        const sDuration = (duration - parseInt(endTime, 10)).toString();

                        const tempFolder = path.join(__dirname, `${outputFolder}/temp`);
                        fs.mkdirSync(tempFolder);

                        const firstHalfFileName = `${tempFolder}/temp_first_half.mp4`;
                        const secondHalfFileName = `${tempFolder}/temp_second_half.mp4`;
                        const source = fs.existsSync(outputVideoFilePath) ? outputVideoFilePath : inputVideoFilePath;

                        const firstHalf = new ffmpeg({
                            source
                        })
                        firstHalf
                            .setStartTime(fStartTime)
                            .setDuration(fDuration)
                            .on('end', (err) => {
                                if (!err) {
                                    console.log('Video Cut 1')
                                    const secondHalf = new ffmpeg({
                                        source
                                    });
                                    secondHalf
                                        .setStartTime(sStartTime)
                                        .setDuration(sDuration)
                                        .on('end', (err) => {
                                            if (!err) {
                                                console.log('Video Cut 2');
                                                new ffmpeg()
                                                    .input(firstHalfFileName)
                                                    .input(secondHalfFileName)
                                                    .on('end', (err) => {
                                                        if (!err) {
                                                            console.log('Video stitched');
                                                            console.log('startTime, endTime', startTime, endTime)
                                                            rimraf(tempFolder, (rimRafError) => {
                                                                if (rimRafError) {
                                                                    console.error('rimRafError', rimRafError);
                                                                    reject1('ERROR3');
                                                                } else {
                                                                    resolve1('DONE')
                                                                }
                                                            });
                                                        }
                                                    })
                                                    .on('error', (err) => {
                                                        console.log('error3: ', err);
                                                        rimraf(tempFolder, (rimRafError) => {
                                                            console.error('Video stitched error rimRafError', rimRafError);
                                                            reject1('ERROR3');
                                                        });
                                                    })
                                                    .mergeToFile(outputVideoFilePath, outputFolder);
                                            } else {
                                                console.log('error2.1: ', err);
                                                reject('ERROR2.1');
                                                rimraf(tempFolder, (rimRafError) => {
                                                    console.error('rimRafError', rimRafError);
                                                });
                                            }
                                        })
                                        .on('error', (err) => {
                                            console.log('error2: ', err);
                                            reject('ERROR2');
                                            rimraf(tempFolder, (rimRafError) => {
                                                console.error('rimRafError', rimRafError);
                                            });
                                        })
                                        .output(secondHalfFileName)
                                        .run();
                                }
                            })
                            .on('error', (err) => {
                                console.log('error1: ', err);
                                reject('ERROR1');
                                rimraf(tempFolder, (rimRafError) => {
                                    console.error('rimRafError', rimRafError);
                                });
                            })
                            .output(firstHalfFileName)
                            .run();
                    });
                },
            })
        });
    })
}


module.exports = videoEdit;
 

Я вызываю функцию удаления в цикле, чтобы удалить несколько частей видео. Вызывающая функция выглядит так.

 const videoEdit = require('../../../utils/videoEdit');

const deleteSection = async (params) => {
    try {

        const inputVideoFileName = `raw_${params.content_id}.mp4`;
        const videoInstance = await videoEdit({
            inputVideoFileName,
            // outputVideoFileName: 'output_video.mp4'
        });

        const sectionDeletionData = [
            {
                "startTime": "1s",
                "endTime": "4s"
            },
            {
                "startTime": "5s",
                "endTime": "8s"
            }
        ];
        for (let i = 0; i < sectionDeletionData.length; i  ) {
            await videoInstance.deleteSection({
                startTime: sectionDeletionData[i].startTime,
                endTime: sectionDeletionData[i].endTime
            });
        }

        console.log('end?');
    } catch (error) {
        console.error('deleteSection error', error)
        return [];
    }
}