【问题标题】:fluent-ffmpeg aspect ratio with cropfluent-ffmpeg 与作物的纵横比
【发布时间】:2020-07-04 10:33:21
【问题描述】:

我无法使用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

.

我的预期结果 9:16 视频 608x1080

【问题讨论】:

    标签: node.js ffmpeg crop aspect-ratio fluent-ffmpeg


    【解决方案1】:

    我带来的最佳解决方案:
    由于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);

    【讨论】:

      【解决方案2】:

      添加到上面的解决方案,我不是一个超级粉丝。

      下面的例程试图模拟 object-fit: contains 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 });
      

      【讨论】:

        猜你喜欢
        • 2012-01-03
        • 1970-01-01
        • 2017-11-21
        • 2023-03-19
        • 1970-01-01
        • 2014-07-28
        • 2012-07-14
        • 2014-06-13
        • 1970-01-01
        相关资源
        最近更新 更多