FFmpeg多张图片合成视频?

前言

商家在发布商品的时候,大部分情况下是没有视频的,这样往往会造成商品展示不全等问题,而视频制作又比较麻烦,为了解决此痛点,我们需要提供一键合成视频的功能。

之所以选择 FFmpeg,是因为我们期望后续能够进行视频剪辑、字幕添加等更复杂的音视频操作。下面我们就来了解下什么是 FFmpeg。

音视频开发免费学习地址:https://ke.qq.com/course/3202131?flowToken=1042316

(点击链接免费报名,先关注,不迷路)

什么是FFmpeg

FFmpeg 是一款知名的开源音视频处理软件,它提供了丰富而友好的接口支持开发者进行二次开发,也就是说,我们可以把 FFmpeg 看作是一个跨平台的视频处理程序:

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的 音频/视频编解码库libavcodec。

FFmpeg中的 “FF” 指的是 “Fast Forward”,“mpeg” 则是 “Moving Picture Experts Group”

FFmpeg的原理

FFmpeg对音视频的处理过程可以简概为:解复用 => 解码 => 编码 => 复用器。

FFmpeg的使用

常见情况下使用 FFmpeg 首先要在当前系统配置 FFmpeg 环境,也就是对 FFmpeg 工具进行安装和配置,环境配置完成之后就可以使用命令行工具进行 FFmpeg 的调用。

FFmpeg部分简单的命令行操作示例:

获取音视频文件信息:

$ ffmpeg -i video.mp4

转换视频文件格式(转换 mp4 文件到 avi 文件):

$ ffmpeg -i video.mp4 video.avi

更改视频文件分辨率:

$ ffmpeg -i input.mp4 -filter:v scale=1280:720 -c:a copy output.mp4

视频中提取图像:

$ ffmpeg -i input.mp4 -r 1 -f image2 image-%3d.png

当然,FFmpeg 还可以进行更多的操作,在此不进行更多的举例,感兴趣的同学可以参考官方文档(http://www.ffmpeg.org/)

FFmpeg在Node.js中的应用

在Node.js中有一个非常好用的模块,它就是 fluent-ffmpeg:

This library abstracts the complex command-line usage of ffmpeg into a fluent, easy to use node.js module. In order to be able to use this module, make sure you have ffmpeg installed on your system (including all necessary encoding libraries like libmp3lame or libx264).

简而言之 fluent-ffmpeg 对 FFmpeg 复杂的命令行进行了一定的封装,抽象为我们使用起来非常舒服的各类方法和API,可以看下它的一些常见操作:

fluent-ffmpeg的部分简单示例:

指定输入:

ffmpeg('input1.avi').input('input2.avi').input(fs.createReadStream('input3.avi'));

音频选项:

// 禁用音频
ffmpeg('input1.avi').noAudio();// 设置音频比特率
ffmpeg('input1.avi').audioBitrate(128);// 设置音频频率
ffmpeg('input1.avi').audioFrequency(22050);

视频选项:

// 设置编解码器
ffmpeg('input1.avi').videoCodec('libx264');// 设置输出帧大小和纵横比
ffmpeg('input1.avi').size('640x?').aspect('4:3');

远程环境使用fluent-ffmpeg(@ffmpeg-installer/ffmpeg)

了解到 fluent-ffmpeg 之后,我们会发现它简介中提到的重要一点(make sure you have ffmpeg installed on your system (including all necessary encoding libraries like libmp3lame or libx264).),请确保在系统上安装了 ffmpeg (包括所有必要的编码库,如libmp3lame或libx264)。那么随之而来的一个问题就是,当我把服务部署到远程机器时,远程机器如果没有安装过FFmpeg环境怎么办。

解决这个问题我们用到的是另一个库 @ffmpeg-installer/ffmpeg:

Installs a binary of ffmpeg for the current platform and provides a path and version. Supports Linux, Windows and Mac OS/X.

@ffmpeg-installer/ffmpeg 能为当前平台安装 FFmpeg 二进制文件,让我们具备在多个环境中去调用 FFmpeg 的能力。它和 fluent-ffmpeg 结合使用,只需如下操作:

const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfmpegPath(ffmpegPath);

将图片合成为视频

下面就是我们想要使用 FFmpeg 进行的工作,将多张不同大小尺寸的图片合成为带有一定动画切换效果的视频:

(为方便上传,视频转换成了gif)

动图封面

整个过程分为以下几步:

1、输入被操作对象

确定基础视频和目标视频,并输入基础视频。这个步骤主要就是确认了输入和输出的目标。

// 基础视频
const baseVideo = path.join(__dirname, '../assets/baseVideo30.mp4')// 目标视频
const savePath = path.join(__dirname, '../assets/temp-video.mp4')// 基础视频输入
let baseInput = await ffmpeg().input(baseVideo)

2、图片输入

方法和视频的输入相同,因为存在多张图片,所以使用for循环输入

for (let i = 0; i < img_list.length; i++) { baseInput = await baseInput.input(img_list[i])
}

3、complexFilter动画处理

实现这个功能的主要逻辑都在于动画部分,要计算好我们如何让多张图片进行有规律的切换,然后将动画规律嵌套到代码内。上面动画的规则其实就是每张图片都从最左侧运动进入居中位置,停留数秒后,向下方运动直到出视频外。

complexFilter 方法允许为命令设置复杂的 filtergraph 。这个API其实对应原生 FFmpeg 的-filter_complex命令,-filter_complex 可以帮助我们实现加字幕、裁剪、缩放、旋转等。我们这里使用的就是 -filter_complex 对输入对象的控制和处理能力。

首先需要处理图片的大小,让不同比例的图片都能缩放为可以居中并且全部展示的大小,如果我们不对图片进行大小处理的话,默认情况会使用图片的原有大小(即使图片大小超出了视频大小范围)。

处理图片和视频大小使用的是 scale 滤镜,首先将视频比例设置为宽高640、480:

let complexFilter = '[0:v]scale=w=640:h=480[videobase];'

上面的代码我们进行一下拆分解释:

// 0-操作对象编号 v-对象内视频信息
'[0:v]'// scale滤镜,设置输入目标的宽高
'scale=w=640:h=480'// outputs输出流 相当于对当前操作后的对象进行标记
'[videobase]'

将图片大小根据视频比例进行宽高设置:

const videoWidth = 640 // 视频宽
const videoHeight = 480 // 视频高
for (let i = 0; i < img_list.length; i++) {complexFilter += `[${i + 1}:v]scale=w='iw*min(${videoWidth}/iw,${videoHeight}/ih)':h='ih*min(${videoWidth}/iw,${videoHeight}/ih)'[img${i + 1}];`
}

上面的代码我们进行一下拆分解释,

// 此处同[0:v],但是0代表我们第一个输入(base视频),所以根据for循环,每次累加得到输入的图片
'[${i + 1}:v]'// iw ih 就是 inputs width 和 inputs height,代表当前操作的图片的原始宽高
// 此处我们将两个值与视频宽高分别对比,并使用 min() 取最小值进行缩放。
'scale=w='iw*min(${videoWidth}/iw,${videoHeight}/ih)':h='ih*min(${videoWidth}/iw,${videoHeight}/ih)''// 同为 outputs 输出标识,即经过此次循环,我们得到了 img1, img2, img3 ..... 等输出对象
'[img${i + 1}]' 

视频和图片的大小比例处理完成后我们使用 overlay 滤镜进行动画处理,其实就是将图片在视频内进行位移。overlay 的能力就是覆盖,将多个输入源进行相互覆盖处理。我们来看这部分的全部代码:

// imgInterval 是通过图片数量和视频长度计算的每张图片展示间隔。
for (let i = 0; i < img_list.length; i++) { // 输入图片的动画控制if (i === 0) {complexFilter += `[videobase][img${i + 1}]overlay='main_w/2-overlay_w/2':'if(gte(t, ${(i + 1) * imgInterval}), min(main_h/2-overlay_h/2+(t-${(i + 1) * imgInterval})*900,main_h), main_h/2-overlay_h/2)'${i === img_list.length - 1 ? '' : `[a${i}];`}`} else {complexFilter += `[a${i - 1}][img${i + 1}]overlay='if(gte(t, ${i * imgInterval}), min(-overlay_w+(t-${i * imgInterval})*900,main_w/2-overlay_w/2),NAN)':'if(gte(t, ${(i + 1) * imgInterval}), min(main_h/2-overlay_h/2+(t-${(i + 1) * imgInterval})*900,main_h), main_h/2-overlay_h/2)'${i === img_list.length - 1 ? '' : `[a${i}];`}`}
}

我们将以上代码拆解,首先,第一张图片默认视频封面,也就是没有入场效果,直接在视频中心的,所以当 i === 0 时单独处理,根据时间判断,当停留时间到达后,会以每秒钟900的速度从下方移动出视频:

// videobase 为上面标记的视频对象,img1 为上面标记的第一张图片对象
// 此处的能力就是将输入对象 img1 覆盖在 videobase 上
'[videobase][img${i + 1}]'// overlay=x(横坐标轴相关操作):y(纵坐标轴相关操作)
// 第一张图片x轴不需要进行移动
'overlay='main_w/2-overlay_w/2''// FFmpeg 的 if语句: if (条件, 条件成立, 条件不成立) 
// main_w - 整个视频的宽度
// overlay_w - 当前操控的输入对象(图片)的宽度
// gte(t, ${(i + 1) * imgInterval})  t为当前时间,通过时间控制是否移动
// min(main_h/2-overlay_h/2+(t-${(i + 1) * imgInterval})*900,main_h) 当前高度,900为移动速度(与时间正比), main_h就是临界值
'if(gte(t, ${(i + 1) * imgInterval}), min(main_h/2-overlay_h/2+(t-${(i + 1) * imgInterval})*900,main_h), main_h/2-overlay_h/2)'

非第一行图片时增加了x轴的处理,根据图片排名和图片停留时间计算出开始运动的时间,并由此刻从左侧进入视频。在居中位置停留后,由下方移出,方法同以上y轴的移动方法:

// 覆盖标识
'[a${i - 1}][img${i + 1}]'// 同上解释,main_w/2-overlay_w/2 为x轴移动的临界值,也就是居中位置, 900 就是x轴与时间成正比的速度
'if(gte(t, ${i * imgInterval}), min(-overlay_w+(t-${i * imgInterval})*900,main_w/2-overlay_w/2),NAN)'

需要注意的地方就是每个图片滤镜完成后的输出标识,此标识需出现在下一张图片的被覆盖对象位置上:

'[a${i - 1}][img${i + 1}]' // [a${i - 1}] 即上一张图片的输出标识

滤镜添加完毕后我们就已经完成了主要的工作,整个动画效果都已经衔接在了一起,最后得到的 complexFilter 命令如下所示:

'[0:v]scale=w=640:h=480[videobase];[1:v]scale=w='iw*min(640/iw,480/ih)':h='ih*min(640/iw,480/ih)'[img1];[2:v]scale=w='iw*min(640/iw,480/ih)':h='ih*min(640/iw,480/ih)'[img2];[3:v]scale=w='iw*min(640/iw,480/ih)':h='ih*min(640/iw,480/ih)'[img3];[4:v]scale=w='iw*min(640/iw,480/ih)':h='ih*min(640/iw,480/ih)'[img4];[videobase][img1]overlay='main_w/2-overlay_w/2':'if(gte(t, 3.75), min(main_h/2-overlay_h/2+(t-3.75)*900,main_h), main_h/2-overlay_h/2)'[a0];[a0][img2]overlay='if(gte(t, 3.75), min(-overlay_w+(t-3.75)*900,main_w/2-overlay_w/2),NAN)':'if(gte(t, 7.5), min(main_h/2-overlay_h/2+(t-7.5)*900,main_h), main_h/2-overlay_h/2)'[a1];[a1][img3]overlay='if(gte(t, 7.5), min(-overlay_w+(t-7.5)*900,main_w/2-overlay_w/2),NAN)':'if(gte(t, 11.25), min(main_h/2-overlay_h/2+(t-11.25)*900,main_h), main_h/2-overlay_h/2)'[a2];[a2][img4]overlay='if(gte(t, 11.25), min(-overlay_w+(t-11.25)*900,main_w/2-overlay_w/2),NAN)':'if(gte(t, 15), min(main_h/2-overlay_h/2+(t-15)*900,main_h), main_h/2-overlay_h/2)''

最后我们可以进行一些其他操作,并设置输出路径

baseInput.complexFilter([ // 上面的滤镜complexFilter,]).videoBitrate('2048k') // 比特率.aspect('4:3') // 视频比例.duration(video_long) // 视频停止时间.on('end', () => { // 视频处理完成console.log('video one end');taskInfo.savePath = savePath;resolve();}).on('error', (error) => { // 视频处理失败console.log('an error happend: create one video' + error);reject(error);}).save(savePath); // 保存路径
})

注意mp4的编码问题

大部分同学对MP4的理解是后缀为 .mp4 的文件,但其实MP4有非常复杂的含义(参考MPEG-4 Part 14(http://en.wikipedia.org/wiki/Mp4)),它本身不是一种简单的视频格式,而是一个包装了视频和音频格式的容器。MP4的视频格式可以有 DivX 也可有 H264,vp8,vp9,theora。

每个浏览器因为专利费等原因对不同格式的视频支持情况也不相同,具体可以参考HTML5 video(https://en.wikipedia.org/wiki/HTML5_video)

所以在此次需求中,就遇到了视频无法在浏览器播放的问题,原因就是一开始的basevideo是直接由 FFmpeg 默认产生。为了解决这个问题,我们使用了一个底层编码为 H264 格式的底视频。这里如果各位同学对 FFmpeg 有深入研究,有更好的解决方案的话,欢迎提供其他解决思路~

总结

以上就是本次对 FFmpeg 的一些介绍和实际开发中的使用,这只是 FFmpeg 的冰山一角,它还有很多更加强大的能力,大家如果对音视频感兴趣可以深入进行学习。

当然,如果对本文中实际的解决方案有疑问或者有更好的建议,欢迎进行讨论~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/707564.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐网站(11)Autodraw免费画画自动识别网站

今天我想分享一个神奇的网站&#xff0c;它可以根据你所画的内容自动生成相应的风格&#xff0c;对于那些不太擅长画画的朋友来说简直是个福音&#xff0c;再也不用担心自己不擅长绘画了&#xff01; 草率的画一只鸟 比如我们选择第一个看看&#xff0c;比我画的好多了 链接直…

前端工程化 - 快速通关 - vue

目录 npm 2.1环境 2.2命令 2.3使用流程 Vite 3.1简介 3.2实战 Vue3 4.1组件化 4.2SFC 4.3Vue工程 4.4基础使用 4.5进阶用法 4.6总结 npm npm 是 nodejs 中进行 包管理 的工具&#xff1b; 下载&#xff1a;Node.js — Run JavaScript Everywhere 2.1环境 ●安…

Springboot整合 Spring Cloud Alibaba Sentinel

1.Sentinel介绍 官方文档地址&#xff1a; https://sentinelguard.io/zh-cn/docs/introduction.html https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入…

什么样的技术管理者会被委以重任?

很多技术管理者在职场遭遇瓶颈&#xff0c;长期在中基层管理岗位上徘徊&#xff0c;似乎怎么努力都没有用&#xff0c;难以突破。 而那些被委以重任的技术管理者&#xff0c;往往展现出一些共同的特征&#xff0c;这些特征有助于他们在职业生涯中脱颖而出&#xff0c;今天就聊…

CVE-2022-1015:nf_tables 中整数溢出导致的栈溢出

文章目录 前言漏洞分析漏洞利用OOB ReadOOB Write exploit修复 前言 影响版本&#xff1a;v5.12.0~v5.17.0 测试版本&#xff1a;v5.17.0 编译选项&#xff1a; CONFIG_NF_TABLESy CONFIG_NETFILTER_NETLINKy CONFIG_BINFMT_MISCy CONFIG_USER_NSy CONFIG_E1000y CONFIG_E1…

手机恢复数据:方法与注意事项

手机数据恢复是许多用户在面对意外数据丢失时迫切需要解决的问题。无论是误删文件、系统故障还是其他原因导致的数据丢失&#xff0c;采取正确的恢复方法至关重要。在本文中&#xff0c;我们将探讨手机恢复数据的方法与注意事项&#xff0c;帮助您有效地找回丢失的重要信息。 手…

【K8s】专题四:Kubernetes 安装方法之 Sealos

以下内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01;如果对您有帮助&#xff0c;烦请点赞、关注、转发&#xff01;欢迎扫码关注个人公众号&#xff01; 目录 一、Sealos 简介 二、Sealos 下载、安装 三、Sealos 部署 Kubernetes 集群 四、Sealos 常…

Cocktail for Mac 激活版:一站式系统优化与管理神器

Cocktail for Mac是一款专为Mac用户打造的系统优化与管理工具&#xff0c;凭借其强大的功能和简便的操作&#xff0c;赢得了广大用户的喜爱。这款软件集系统清理、修复和优化于一身&#xff0c;能够帮助用户轻松解决Mac系统中的各种问题&#xff0c;提高系统性能。 Cocktail fo…

【Open AI】GPT-4o深夜发布:视觉、听觉跨越式升级

北京时间5月14日1点整&#xff0c;OpenAI 召开了首场春季发布会&#xff0c;CTO Mira Murati 在台上和团队用短短不到30分钟的时间&#xff0c;揭开了最新旗舰模型 GPT-4o 的神秘面纱&#xff0c;以及基于 GPT-4o 的 ChatGPT&#xff0c;均为免费使用。 本文内容来自OpenAI网站…

PCIE协议-2-事务层规范-Ordering and Receive Buffer Flow Control

2.6 流量控制&#xff08;Flow Control FC&#xff09; 流量控制&#xff08;Flow Control FC&#xff09;用于防止接收器缓冲区溢出&#xff0c;并确保遵守第2.4节中定义的排序规则。请注意&#xff0c;流量控制机制由请求者用来跟踪链路另一端组件中可用的队列/缓冲区空间…

羊大师分析,羊奶健康生活的营养源泉

羊大师分析&#xff0c;羊奶健康生活的营养源泉 羊奶&#xff0c;作为一种古老的饮品&#xff0c;近年来因其独特的营养价值和健康益处而备受关注。今天&#xff0c;羊大师就来探讨一下羊奶与健康之间的紧密联系。 羊奶富含蛋白质、脂肪、维生素和矿物质等多种营养成分。羊奶…

【好书推荐-第十六期】《 LangChain技术解密:构建大模型应用的全景指南》(Github 6800+示例!)

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…