FFmpeg的简单使用【Windows】--- 视频倒叙播放

实现功能

点击【选择文件】按钮可以选择视频,当点击【开始处理】按钮之后,会先将视频上传到服务器,然后开始进行视频倒叙播放的处理,当视频处理完毕之后会将输出的文件路径返回,同时在页面中将处理好的视频展示出来。

效果展示

转换成功效果展示
上传成功
处理后

代码实现

说明:

前端代码是使用vue编写的。

后端接口的代码是使用nodejs进行编写的。

前端代码

<template>
  <div id="app">
    <!-- 显示上传的视频 -->
    <div>
      <h2>将要处理的视频</h2>
      <video
        v-for="video in uploadedVideos"
        :key="video.src"
        :src="video.src"
        controls
        style="width: 200px"
      ></video>
    </div>

    <!-- 上传视频按钮 -->
    <input type="file" @change="uploadVideo" accept="video/*" />

    <!-- 显示处理后的视频 -->
    <div>
      <h2>已处理后的视频</h2>
      <video
        v-for="video in processedVideos"
        :key="video.src"
        :src="video.src"
        controls
        style="width: 200px"
      ></video>
    </div>

    <button @click="processVideos">开始处理</button>
  </div>
</template>

<script setup>
import axios from "axios";
import { ref } from "vue";

const uploadedVideos = ref([]);
const processedVideos = ref([]);
let videoIndex = 0;

const uploadVideo = async (e) => {
  const files = e.target.files;
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    const videoSrc = URL.createObjectURL(file);
    uploadedVideos.value.push({ id: videoIndex++, src: videoSrc, file });
  }
};

const processVideos = async () => {
  const formData = new FormData();
  for (const video of uploadedVideos.value) {
    formData.append("video", video.file); // 使用实际的文件对象
  }
  try {
    const response = await axios.post(
      "http://localhost:3000/user/single/process",
      formData,
      {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      }
    );
    const processedVideoSrc = response.data.path;
    processedVideos.value.push({
      id: videoIndex++,
      src: "http://localhost:3000/" + processedVideoSrc,
    });
  } catch (error) {
    console.error("Error processing video:", error);
  }
};
</script>

补充说明:

 accept="video/*":指定了只接受视频文件类型,这将过滤掉非视频文件,使得用户在选择文件时只能看到并选择视频文件。

video/*:是一个通配符,表示所有已知的视频文件类型。如果你只想接受特定的视频格式(例如MP4和WebM),你可以指定他们,如下所示:

accept=".mp4, .webm"

或者,如果你想要更精确地控制,可以使用MIME类型:

accept="video/mp4, video/webm"

后端代码

routers =》users.js

var express = require('express');
var router = express.Router();
const multer = require('multer');
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');
const upload = multer({
  dest: 'public/uploads/',
  storage: multer.diskStorage({
    destination: function (req, file, cb) {
      cb(null, 'public/uploads'); // 文件保存的目录
    },
    filename: function (req, file, cb) {
      // 提取原始文件的扩展名
      const ext = path.extname(file.originalname).toLowerCase(); // 获取文件扩展名,并转换为小写
      // 生成唯一文件名,并加上扩展名
      const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
      const fileName = uniqueSuffix + ext; // 新文件名
      cb(null, fileName); // 文件名
    }
  })
});
const fs = require('fs');

// 处理单个视频文件
router.post('/single/process', upload.single('video'), (req, res) => {
  console.log(req.file)
  const videoPath = req.file.path;
  // 使用filename进行拼接是为了防止视频被覆盖
  const outputPath = `public/processed/reversed_${req.file.filename}`;


  ffmpeg()
    .input(videoPath)
    .outputOptions([
      '-vf reverse'// 反转视频帧顺序
    ])
    .output(outputPath)
    .on('end', () => {
      res.json({ message: 'Video processed successfully.', path: outputPath.replace('public', '') });
    })
    .on('error', (err) => {
      console.log(err)
      res.status(500).json({ error: 'An error occurred while processing the video.' });
    })
    .run();
});

module.exports = router;

multer配置项说明:

  • 配置选项:
    • dest:指定了文件的临时存储路径,如果提供了storage选择,则dest可能不会被使用。
    • storage:使用multer.diskStorage() 来定义文件如何存储在磁盘上。
  • destination函数:
    • 这个函数用于指定文件存储的目录
    • req:请求对象
    • file:包含了关于上传文件的信息
    • cb:回调函数,用于通知multer存储路径
  • filename函数:
    • 这个函数用于生成存储在磁盘上的文件名
    • req:请求对象
    • file:含有文件的相关信息
    • cb:回调函数,用于通知穆拉特人文件名
    • 在这里,我们生成了一个唯一的文件名,该文件名包含当前时间戳和一个随机数,以避免文件名冲突,并保留了原始文件的扩展名。

ffmpeg()说明:

当你调用 ffmpeg() 时,实际上创建了一个 ffmpeg 实例。这个实例可以用来设置输入文件、输出文件以及一系列的处理选项。

ffmpeg() 返回的是一个 Command 对象,这个对象包含了用于设置和执行 ffmpeg 命令的方法。这些方法可以链接起来形成一个流式操作链。

  • .input():指定输入文件路径
  • .output():指定输出文件路径
  • .on()、.once():监听事件,比如完成、错误等
  • .run():执行ffmpeg命令
  • .cancel():取消正在运行的命令
  • .getCommand():获取将要执行的完整命令字符串

典型的ffmpeg命令执行流程

  1. 初始化:创建 ffmpeg 示例并指定输入文件
  2. 设置输出文件:使用 .output() 方法来设置文件路径
  3. 添加命令行参数:使用 .addOption() 或者链式调用 .option() 方法来添加额外的 ffmpeg参数
  4. 监听事件:监听 end error 事件来处理命令执行的结果
  5. 执行命令:调用 .run() 方法来开始执行命令

 

routers =》index.js

var express = require('express');
var router = express.Router();

router.use('/user', require('./users'));

module.exports = router;

 app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// 使用cors解决跨域问题
app.use(require('cors')());

app.use('/', indexRouter);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SHELL脚本之重定向符号的使用。

一.shell脚本&#xff08;对应完成某一个功能的命令熟悉与否&#xff0c;决定着shell脚本的熟练与否。&#xff09; 一个shell脚本就是一个普通的文本文件。 作用&#xff1a;将重复执行的操作写成脚本&#xff0c;自动执行。 二.Linux操作系统中重定向符号的使用。 类型&a…

ESP32接入扣子(Coze) API使用自定义智能体

使用ESP32接入Coze API实现聊天机器人的教程 本示例将使用ESP32开发板通过WiFi接入 Coze API&#xff0c;实现一个简单的聊天机器人功能。用户可以通过串口向机器人输入问题&#xff0c;ESP32将通过Coze API与智能体进行通信&#xff0c;并返回对应的回复。本文将详细介绍了如…

selenium有多个frame页时的操作方法(5)

之前文章我们提到&#xff0c;在webdriver.WebDriver类有一个switch_to方法&#xff0c;通过switch_to.frame()可以切换到不同的frame页然后才再定位某个元素做一些输入/点击等操作。 比如下面这个测试网站有2个frame页&#xff1a;http://www.sahitest.com/demo/framesTest.h…

wordpress 子比主题美化 四宫格 多宫格 布局插件

wordpress 主题美化 四宫格 多宫格 布局插件&#xff08;只在子比主题上测试过&#xff0c;其它主题没测试&#xff09; A5资源网四宫格布局插件是一个功能丰富的WordPress插件,专为创建自适应的四宫格布局而设计。这个插件具有以下主要特点: 灵活的布局: 支持1到8个宫格的自定…

如何设置 GitLab 密码长度?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 60天专业…

星海智算:【RVC】无需部署一键启动

镜像介绍 RVC全称 Retrieval-based-Voice-Conversion-WebUI 简称 RVC 一个基于VITS的简单易用的语音转换&#xff08;变声器&#xff09;框架 RVC 是一款前沿的音色替换项目&#xff0c;可以进行歌曲的翻唱&#xff0c;和实时的变声&#xff0c;具有低延迟、优秀的变声效果、…

【可答疑】基于51单片机的自动烘手器(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

C++ 算法学习——1.8 快速幂算法

背景知识&#xff1a; 1.位运算 在C中&#xff0c;位运算是对整数类型的位进行操作的一种运算方式。常见的位运算符包括按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09;、取反&#xff08;~&#xff09;、左移&am…

芯课堂 | Synwit_UI_Creator(μgui)平台之图像处理篇

今天小编给大家介绍的是UI_Creator&#xff08;μgui&#xff09;平台下关于图像处理的选项。 UI_Creator&#xff08;μgui&#xff09;平台图片类控件有图像控件和分级图像控件&#xff0c;均包含以下选项&#xff1a; 1、消除水波纹&#xff1a; 由于16位真彩色&#xff08…

基础IO -- 理解文件(1)

目录 一&#xff1a;回顾文件 二&#xff1a;加深对文件的理解 1.概念 2.以w写方式打开 3.以a追加方式打开 4.重定向 一&#xff1a;回顾文件 以前学习过在C语言中的文件操作&#xff0c; 但那根本是不足以理解文件的&#xff0c;即站在语言角度是不可能理解文件的 我们要…

QT 中如何保存matlab 能打开的.mat数据矩阵!

Windows 上安装并使用 MATIO 库来保存 MATLAB 格式的 .mat 文件&#xff0c;需要进行以下步骤&#xff1a; 1. 下载并安装 CMake MATIO 使用 CMake 构建项目&#xff0c;因此你需要先安装 CMake。 前往 CMake 官网下载适用于 Windows 的安装程序并安装。 2. 下载 MATIO 库源…

说下SSL/TLS四次握手过程?

参考自&#xff1a;SSL/TLS四次握手过程是怎么样的&#xff1f;HTTPS、SSL、TLS三者之间的联系和区别 一.SSL/TLS 简介 SSL(Secure Socket Layer 安全套接层)是基于 HTTPS 下的一个协议加密层&#xff0c;用于解决 HTTP 在传输数据时使用明文而导致的不安全问题。 SSL 是 HT…

AD报错failed to add class member\net

什么原因导致的我到现在还没弄懂&#xff0c;总之解决方法是在PCB端删除所有现有的并且可删除的nets与components。下次问题复现了再补充截图&#xff08;不想再遇到了球球了这种玄学问题&#xff09;。 网络截图&#xff1a; 解决步骤&#xff1a;设计->类 把可删除的网络…

西门子828d的plc一些信息记录

1、虽然是200的plc但是引入了DB的形式替代原来的V存储区。 2、用户自定义DB块范围&#xff0c;DB9000-DB9063,共64个DB块。 可用地址范围如上图 机床MCP483面板地址表&#xff0c;其它类型的面板地址自己在828d简明调试手册里查看。 如何上载828d的plc程序&#xff1a; 1.通…

coze bot开发的最小实践

一、coze专业版开通 网站地址&#xff1a;扣子专业版-火山引擎 开通流程&#xff1a; 1."立即使用" 扣子专业版 1.点击【立即使用】2.登录账号(上一步已登录可跳过) 2.进行实名认证后&#xff0c;开启【扣子专业版】&#xff08;已认证可跳过&#xff09; 1. 前…

Mysql行转列的写法

一、何为行转列&#xff1f; 在搞清楚这一概率之前&#xff0c;不妨来认识一下我们mysql表二维表结构数据&#xff0c;例如学生成绩表格&#xff0c;属性有学生姓名&#xff0c;学生科目&#xff0c;成绩&#xff0c;表结构如下&#xff1a; CREATE TABLE test_9 (id int(11) …

Android map 获取值

Android Map 获取值的完整指南 在Android开发中&#xff0c;使用Map&#xff08;映射&#xff09;来存储和检索数据是非常常见的需求。Map是一种键值对集合&#xff0c;能够快速而高效地根据特定的键获取值。在这篇文章中&#xff0c;我们将深入探讨如何在Android应用中使用Ma…

Linux的zookeeper安装部署

1.zookeeper是一个分布式的,开放源码的分布式应用程序协调服务,是hadoop和HBASE的重要组件 2.下载zookeeper安装包zookeeper安装包https://archive.apache.org/dist/zookeeper/zookeeper-3.5.9/ 移动到Linux解压 解压到/export/server文件夹 命令: tar -xvf apache-zooke…

AI大模型:生产中的RAG,为何表现不尽如人意?

RAG&#xff08;Retrieval Augmented Generation,检索增强生成&#xff09;是一个将大规模语言模型(LLM)与来自外部知识源的检索相结合的框架,以改进问答能力的工程框架。虽然一切都看起来很美好&#xff0c;在实际应用中&#xff0c;RAG的状态确是“一看就会&#xff0c;一用就…

【Linux】嵌入式Linux系统的组成、u-boot编译

Linux—嵌入式Linux系统的组成、u-boot编译 前言一、嵌入式Linux系统的组成1.1 嵌入式Linux系统和PC完整的操作系统的对比如下&#xff1a;1.2 PC机—Windows系统启动流程&#xff08;PC机—Linux系统、嵌入式ARM—linux系统的启动流程类似&#xff09; 二、编译u-boot2.1 u-bo…