簡體   English   中英

fluent-ffmpeg 寬高比與作物

[英]fluent-ffmpeg aspect ratio with crop

我在使用fluent-ffmpeg更改 16:9、1:1 和 9:16 之間的寬高比時遇到問題 當我嘗試從 16:9 更改為 9:16 時,我收到了一個壓縮視頻,但實際上我想要額外的部分應該被刪除。

我嘗試了很多組合:

FFmpeg().input(video).size("608x?").aspect("9:16").output(tempFile).run();

我輸入 16:9 視頻 1920x1080 我輸入 16:9 視頻 1920x1080

.

我的預期結果 9:16 視頻 608x1080

我的預期結果 9:16 視頻 608x1080

我帶來的最佳解決方案:
由於fluent-ffmpeg不提供任何內置方法來裁剪和縮放視頻,這就是我們需要自己實現它的原因。

第1步:
我們需要通過裁剪額外的(橫向到縱向)或通過添加黑條(縱向到橫向)以避免壓縮或拉伸視頻來計算中間裁剪分辨率以實現目標縱橫比。

筆記:
我們還可以在人像到風景的情況下添加一個很好的模糊效果,但這會增加一個額外的步驟(需要 ffmpeg 模糊過濾器)。

第2步:
我們將簡單地放大或縮小到我們的目標分辨率。

代碼似乎太長了,但相信我,這是一種簡單的方法,可以分為多種方法。 從底層開始理解。
如果您需要JavaScript版本,請忽略types

只需運行此代碼,它就會為您裁剪/縮放單個視頻。

 import * as FFmpeg from "fluent-ffmpeg"; function resizingFFmpeg( video: string, width: number, height: number, tempFile: string, autoPad?: boolean, padColor?: string ): Promise<string> { return new Promise((res, rej) => { let ff = FFmpeg().input(video).size(`${width}x${height}`); autoPad ? (ff = ff.autoPad(autoPad, padColor)) : null; ff.output(tempFile) .on("start", function (commandLine) { console.log("Spawned FFmpeg with command: " + commandLine); console.log("Start resizingFFmpeg:", video); }) // .on("progress", function(progress) { // console.log(progress); // }) .on("error", function (err) { console.log("Problem performing ffmpeg function"); rej(err); }) .on("end", function () { console.log("End resizingFFmpeg:", tempFile); res(tempFile); }) .run(); }); } function videoCropCenterFFmpeg( video: string, w: number, h: number, tempFile: string ): Promise<string> { return new Promise((res, rej) => { FFmpeg() .input(video) .videoFilters([ { filter: "crop", options: { w, h, }, }, ]) .output(tempFile) .on("start", function (commandLine) { console.log("Spawned FFmpeg with command: " + commandLine); console.log("Start videoCropCenterFFmpeg:", video); }) // .on("progress", function(progress) { // console.log(progress); // }) .on("error", function (err) { console.log("Problem performing ffmpeg function"); rej(err); }) .on("end", function () { console.log("End videoCropCenterFFmpeg:", tempFile); res(tempFile); }) .run(); }); } function getDimentions(media: string) { console.log("Getting Dimentions from:", media); return new Promise<{ width: number; height: number }>((res, rej) => { FFmpeg.ffprobe(media, async function (err, metadata) { if (err) { console.log("Error occured while getting dimensions of:", media); rej(err); } res({ width: metadata.streams[0].width, height: metadata.streams[0].height, }); }); }); } async function videoScale(video: string, newWidth: number, newHeight: number) { const output = "scaledOutput.mp4"; const { width, height } = await getDimentions(video); if ((width / height).toFixed(2) > (newWidth / newHeight).toFixed(2)) { // y=0 case // landscape to potrait case const x = width - (newWidth / newHeight) * height; console.log(`New Intrim Res: ${width - x}x${height}`); const cropping = "tempCropped-" + output; let cropped = await videoCropCenterFFmpeg( video, width - x, height, cropping ); let resized = await resizingFFmpeg(cropped, newWidth, newHeight, output); // unlink temp cropping file // fs.unlink(cropping, (err) => { // if (err) console.log(err); // console.log(`Temp file ${cropping} deleted Successfuly...`); // }); return resized; } else if ((width / height).toFixed(2) < (newWidth / newHeight).toFixed(2)) { // x=0 case // potrait to landscape case // calculate crop or resize with padding or blur sides // or just return with black bars on the side return await resizingFFmpeg(video, newWidth, newHeight, output, true); } else { console.log("Same Aspect Ratio forward for resizing"); return await resizingFFmpeg(video, newWidth, newHeight, output); } } videoScale("./path-to-some-video.mp4", 270, 480);

添加到上述解決方案中,我不是很喜歡。

下面的例程嘗試模擬對象擬合:包含CSS 裁剪和大小調整。 這首先計算出中間調整大小步驟需要的大小以保持縱橫比並提供必要的寬度和高度以裁剪到所需的輸出,然后對結果運行視頻過濾器裁剪以僅提取所需的輸出尺寸.

我還使用temp npm 包在系統臨時文件夾中生成空文件,這些文件將用作ffmpeg輸出文件目標。

import FFMpeg from 'fluent-ffmpeg';
import temp from 'temp-write';

function getDimensions(media) {
    return new Promise((resolve, reject) => {
        FFMpeg.ffprobe(media, async (err, metadata) => {
            if (err) {
                reject(err);
                return;
            }
            resolve({
                mediaWidth: metadata.streams[0].width,
                mediaHeight: metadata.streams[0].height,
            });
        });
    });
}

function FFMpegPromisify(routine, output) {
    return new Promise((resolve, reject) => {
        routine
            .on('error', (err) => {
                reject(err);
            })
            .on('end', () => {
                resolve();
            })
            .save(output);
    });
}

module.exports = {
    resize: async ({ data, width, height }) => {
        let path = temp.sync(data);
        const { mediaWidth, mediaHeight } = await getDimensions(path);
        let mediaAspectRatio = mediaWidth / mediaHeight;
        let widthResizeRatio = width / mediaWidth;
        let heightResizeRatio = height / mediaHeight;
        let maxAdjustedWidth = Math.round(Math.max(mediaWidth * widthResizeRatio, height * mediaAspectRatio));
        let maxAdjustedHeight = Math.round(Math.max(mediaHeight * heightResizeRatio, width / mediaAspectRatio));

        let tempResizePath = temp.sync('', 'file.mp4');
        await FFMpegPromisify(FFMpeg(path).format('mp4').size(`${maxAdjustedWidth}x${maxAdjustedHeight}`), tempResizePath);

        let tempCropPath = temp.sync('', 'file.mp4');
        let cropX = (maxAdjustedWidth - width) / 2;
        let cropY = (maxAdjustedHeight - height) / 2;
        await FFMpegPromisify(FFMpeg(tempResizePath).format('mp4').videoFilter([
            {
                filter: "crop",
                options: {
                    w: width,
                    h: height,
                    x: cropX,
                    y: cropY
                },
            }
        ]), tempCropPath);

        return tempCropPath; // contains the final, cropped result
    }
}

let file = require('fs').readFileSync('C:\\FFMpeg\\sample.mp4');

module.exports.resize({ data: file, width: 320, height: 1080 });

我覺得其他答案使它變得比需要的更復雜。

正如其他人所說,沒有辦法直接輸入寬高比,所以你應該在裁剪之前找到所需的尺寸。

這是一個使用ffprobe獲取視頻尺寸的代碼,並找到具有所需縱橫比的所選視頻的最大可能尺寸。

在此示例中,它將獲得 6:9 寬高比的最大文件尺寸。

const ffmpeg = require('fluent-ffmpeg');
ffmpeg.ffprobe(`./Videos/video.mp4`, function (err, metadata) {
if (err) {
    console.error(err);
} else {

    let mediaWidth = metadata.streams[0].width;
    let mediaHeight = metadata.streams[0].height;
    let widthAspectRatio = 6;
    let heightAspectRation = 9;
    let newWidth = 1;
    let newHeight = 1;

    while (newWidth < mediaWidth && newHeight < mediaHeight) {
        newWidth += widthAspectRatio;
        newHeight += heightAspectRation;
    }
    newHeight -= widthAspectRatio;
    newHeight -= heightAspectRation;

    console.log("Original dimensions:", mediaWidth, mediaHeight);
    console.log("New dimensions:", newWidth, newHeight);

}
});

輸出:

Original dimensions: 2560 1440
New dimensions: 961 1426

之后,只需將視頻裁剪到您獲得的尺寸即可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM