ffmpeg转码与加水印

文章目录

    • 转码 与加水印
      • 引入jar包
      • 代码
      • ffmpeg安装
      • 错误
        • 解决方法

转码 与加水印

引入jar包

        <dependency>
            <groupId>net.bramp.ffmpeg</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>0.6.2</version>
        </dependency>

代码

import lombok.extern.slf4j.Slf4j;
import net.bramp.ffmpeg.FFmpeg;
import net.bramp.ffmpeg.builder.FFmpegBuilder;
import net.bramp.ffmpeg.job.FFmpegJob;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

/**
 * @description 阿里云文件上传
 *
 * @author lilinchun
 * @date 2024/10/17
 */
@Slf4j
public class OssUtil {


    public static void main(String[] args) {
        String videoUrl = "xxx.mp4";
        try {
            convertAndUpload(videoUrl);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("转换和上传失败: " + e.getMessage());
        }
    }

    /**
     * 将mp4文件url地址转换并上传到oss中
     * @param url mp4地址
     * @return m3u8文件地址
     */
    public static String convertAndUpload(String url) throws Exception {
        String vodId = null;
        // 截取文件名称
        String fileName = url.substring(url.lastIndexOf('/') + 1);
        int lastDotIndex = fileName.lastIndexOf('.');
        String name = fileName.substring(0, lastDotIndex);
        String fileNameNew = name+"mark"+".mp4";
        // 当前项目的文件路径
        Path tempDir = Files.createTempDirectory("ffmpeg-temp");
        Path inputFilePath = tempDir.resolve(fileName);
        Path inputFilePathWatermark = tempDir.resolve(fileNameNew);
        Path outputM3U8Path = tempDir.resolve(name + ".m3u8");

        // 创建TS文件输出目录
        Path outputTSDir = tempDir.resolve(name + "_ts");
        Files.createDirectories(outputTSDir);
        log.info("临时文件路径:{}", tempDir.toString());
        try {
            downloadFileNew(url, tempDir.toString());
            // 设置水印
            watermarkVoid(inputFilePath,inputFilePathWatermark);
            // 转换生成M3U8和TS文件  指定播放片段为20s
            String ffmpegCommand = String.format(
                    "ffmpeg -i %s -codec: copy -start_number 0 -hls_time 20 -hls_list_size 0 -f hls -hls_segment_filename %s/%s_%%03d.ts %s",
                    inputFilePathWatermark.toAbsolutePath(),
                    outputTSDir.toAbsolutePath(),
                    name,
                    outputM3U8Path.toAbsolutePath()
            );
            // 执行FFmpeg命令转换视频
            Process process = Runtime.getRuntime().exec(ffmpegCommand);
            process.waitFor();


            String indexFile = outputM3U8Path.toString();
            List<String> tsFiles = new ArrayList<>();
            System.out.println("解析文件路径地址:" + outputM3U8Path);
            for (int i = 0; ; i++) {
                Path tsFilePath = outputTSDir.resolve(String.format("%s_%03d.ts", name, i));
                if (!Files.exists(tsFilePath)) {
                    break;
                }
                System.out.println("解析文件路径ts地址:" + tsFilePath);
                tsFiles.add(tsFilePath.toString());
            }
            // 上传阿里云视屏点播服务
            vodId = UploadVodByApi.uploadVideo(indexFile, tsFiles, name + ".m3u8");
            // 清理临时文件
            cleanup(tempDir);
            log.info("临时文件路径:{}", tempDir.toString());
        } catch (IOException e) {
            e.printStackTrace();
            log.error("文件转换失败:{}", e.getMessage());
        }
        return vodId;
    }

    /**
     * 设置视屏水印
     * @param inputFilePath 原始视屏路径
     * @param inputFilePathWatermark  水印视屏路径
     */
    private static void watermarkVoid(Path inputFilePath,Path inputFilePathWatermark){
        log.info("文件名称:{},{}",inputFilePath.toAbsolutePath(),inputFilePathWatermark.toAbsolutePath());
        // 构建FFmpeg命令
        String ffmpegCommandWatermark = String.format(
                "ffmpeg -i \"%s\" -vf \"drawtext=text='nk.benwunet.com':x=mod(n\\,w+tw)-tw:y=10:fontsize=24:fontcolor=white\" -codec:a copy \"%s\"",
                inputFilePath.toAbsolutePath(),
                inputFilePathWatermark.toAbsolutePath()
        );
        log.info("执行加水印的命令:{}",ffmpegCommandWatermark);
        try {
            // 使用ProcessBuilder来启动外部进程
            // 使用ProcessBuilder来启动外部进程
            ProcessBuilder processBuilder = new ProcessBuilder("ffmpeg", "-i", inputFilePath.toAbsolutePath().toString(),
                    "-vf", "drawtext=text='nk.benwunet.com':x=mod(n\\,w+tw)-tw:y=10:fontsize=24:fontcolor=white",
                    "-codec:a", "copy",
                    inputFilePathWatermark.toAbsolutePath().toString());

            // 将错误输出重定向到标准输出
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            // 读取输出
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                log.info(line);
            }
            // 等待进程完成
            int exitCode = process.waitFor();
            if (exitCode == 0) {
                log.info("Video watermarking completed successfully.");
            } else {
                log.error("FFmpeg command failed with exit code: " + exitCode);
            }
            // 关闭资源
            reader.close();
        }catch (Exception e){
            log.error("Error occurred while processing the video: ", e);
        }
    }

    /**
     * 下载文件
     * @param urlStr 文件地址
     * @param saveDir 文件路径
     * @throws IOException 异常
     */
    public static void downloadFileNew(String urlStr, String saveDir) throws IOException {
        CloseableHttpClient httpClient = HttpClientSingleton.getInstance();
        HttpGet request = new HttpGet(urlStr);
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            int responseCode = response.getStatusLine().getStatusCode();

            if (responseCode == 200) {
                String fileName = urlStr.substring(urlStr.lastIndexOf('/') + 1);
                String saveFilePath = saveDir + File.separator + fileName;

                try (InputStream inputStream = response.getEntity().getContent();
                     FileOutputStream outputStream = new FileOutputStream(saveFilePath)) {

                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                }
            } else {
                throw new IOException("No file to download. Server replied HTTP code: " + responseCode);
            }
        }
    }


    /**
     * 清理文件
     * @param dir 文件地址  示例:/tmp/ffmpeg-temp9193762578321295193
     * @throws IOException 异常
     */
    private static void cleanup(Path dir) throws IOException {
        Files.walk(dir)
                // 逆序遍历,先删除子文件
                .sorted((a, b) -> -a.compareTo(b))
                .forEach(path -> {
                    try {
                        Files.delete(path);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
    }

}


ffmpeg安装

可以安装宝塔的

1、安装管理器
在这里插入图片描述
2、安装ffmpeg的版本

3、测试

检查版本

ffmpeg -version

在这里插入图片描述

错误

No such filter: ‘drawtext’
[vost#0:0/libx264 @ 0x7eabd00] Error initializing a simple filtergraph
Error opening output file 01mark.mp4

转码中使用到了drawtext,但是ffmpeg并没有drawtext,所以报错了

解决方法

1、确认FFmpeg版本
确保你安装的FFmpeg版本支持drawtext滤镜。运行以下命令来查看所有可用的滤镜:

ffmpeg -filters

在这里插入图片描述
我之前是安装的ffmpeg-6.1 发现没有这个滤镜,之后我安装了ffmpeg-4.4.1 就有了,可以正常使用

在这里插入图片描述

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

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

相关文章

网络原理 网络协议栈

POSIX API与网络协议栈 unix有不同的衍生版本&#xff0c;针对不同的版本&#xff0c;通过Posix定义了一套标准的操作系统接口API&#xff0c;使得不同的开发版本可以使用相同的API调用&#xff0c;具有可移植性。 网络连接相关API&#xff1a; 客户端 socket() bind() con…

将vscode上的项目提交到github上

1.windows终端中 创建github仓库 创建完成 提交代码 git init git config --global user.email "fuyulai2024163.com" git config --global user.name "Fuyulai-Hub" git add . git commit -m "first commit" git remote add origin https://g…

P4645 [COCI2006-2007#3] BICIKLI(Tarjan+topsort求到某点的方案数)

P4645 [COCI2006-2007#3] BICIKLI - 洛谷 | 计算机科学教育新生态 思路&#xff1a; 我们考虑输出inf的情况&#xff0c;可以发现当从1出发到2经过的任意一个点处于一个环内时&#xff0c;路径条数是无穷多的。 有向图上从s到t的经过点&#xff0c;就是从s出发所能经过的所有…

Linux C/C++编程中的多线程编程基本概念

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com…

MongoDB整合SpringBoot

MongoDB整合SpringBoot 环境准备 1.引入依赖 <!--spring data mongodb--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> 2.配置yml spr…

记录过程校准器:精准测量的未来驱动力与市场蓝海

在科技日新月异的今天&#xff0c;精确测量已成为工业制造、科学研究、环境监测等众多领域的核心要素。记录过程校准器&#xff0c;作为确保测量设备准确性和可靠性的关键工具&#xff0c;正扮演着越来越重要的角色。随着智能制造、物联网技术的快速发展&#xff0c;以及全球对…

【NLP 9、实践 ① 五维随机向量交叉熵多分类】

目录 五维向量交叉熵多分类 规律&#xff1a; 实现&#xff1a; 1.设计模型 2.生成数据集 3.模型测试 4.模型训练 5.对训练的模型进行验证 调用模型 你的平静&#xff0c;是你最强的力量 —— 24.12.6 五维向量交叉熵多分类 规律&#xff1a; x是一个五维(索引)向量&#xff…

STM32使用RCC(Reset Clock Contorl,复位时钟控制器)配置时钟以及时钟树

RCC主要作用 设置系统时钟SYSCLK&#xff08;System Clock&#xff09;频率&#xff1b;设置AHB、APB2、APB1以及各个外设分频因子&#xff0c;从而设置HCLK、PCLK2、PCLK1以及各个外设的时钟频率&#xff1b;控制AHB、APB2、APB1这三条总线时钟以及每个外设的时钟开启&#xf…

使用mtools搭建MongoDB复制集和分片集群

mtools介绍 mtools是一套基于Python实现的MongoDB工具集&#xff0c;其包括MongoDB日志分析、报表生成及简易的数据库安装等功能。它由MongoDB原生的工程师单独发起并做开源维护&#xff0c;目前已经有大量的使用者。 mtools所包含的一些常用组件如下&#xff1a; mlaunch支…

随记:win11 win+g 捕获 不能录视频 不用下载注册表修复工具

问题&#xff1a; 我解决的方法&#xff1a; win R 打开 再去 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\GameDVR 这是我的问题&#xff0c;要是没有这个&#xff0c;可能是其他原因了 还有就是我还看到一个 上面那个不能解决的可以试试这个

算法刷题Day11: BM33 二叉树的镜像

点击题目链接 思路 转换为子问题&#xff1a;左右子树相反转。遍历手法&#xff1a;后序遍历 代码 class Solution:def Transverse(self,root: TreeNode):if root None:return rootnewleft self.Transverse(root.left)newright self.Transverse(root.right)# 对root节点…

【赵渝强老师】PostgreSQL的服务器日志文件

PostgreSQL数据库的物理存储结构主要是指硬盘上存储的文件&#xff0c;包括&#xff1a;数据文件、日志文件、参数文件、控制文件、WAL预写日志文件等等。下面重点讨论一下PostgreSQL的服务器日志文件。 视频讲解如下 【赵渝强老师】PostgreSQL的服务器日志文件 通过使用pg_ct…

【人工智能】深度解剖利用人工智能MSA模型

目录 情感分析的应用一、概述二、研究背景三、主要贡献四、模型结构和代码五、数据集介绍六、性能展示七、复现过程 情感分析的应用 近年来社交媒体的空前发展以及配备高质量摄像头的智能手机的出现&#xff0c;我们见证了多模态数据的爆炸性增长&#xff0c;如电影、短视频等…

行业标杆!鸿翼入选WAIC 2024《2024大模型典型示范应用案例集》

​7月5日&#xff0c;在2024世界人工智能大会“迈向 AGI&#xff1a;大模型焕新与产业赋能”论坛上&#xff0c;《2024大模型典型示范应用案例集》&#xff08;以下简称《案例集》&#xff09;重磅发布&#xff01;鸿翼AI项目成功入选&#xff0c;彰显了鸿翼在大模型应用领域的…

nodejs循环导出多个word表格文档

文章目录 nodejs循环导出多个word表格文档一、文档模板编辑二、安装依赖三、创建导出工具类exportWord.js四、调用五、效果图nodejs循环导出多个word表格文档 结果案例: 一、文档模板编辑 二、安装依赖 // 实现word下载的主要依赖 npm install docxtemplater pizzip --save/…

ABAP DIALOG屏幕编程1

一、DIALOG屏幕编程 DIALOG屏幕编程是SAP ABAP中用于创建用户交互界面的一种技术&#xff0c;主要用于开发事务性应用程序。它允许用户通过屏幕输入或操作数据&#xff0c;程序根据用户的操作执行逻辑处理。 1、DIALOG编程的主要组件 a、屏幕 (Screen) DIALOG程序的核心部分…

Shell免交互

Shell免交互 一. 变量配置1.1 在E0F外面的变量可以直接传入使用1.2 EOF的输入内容可以直接赋值给变量 二. expect语句2.1 转义符2.2 expect的语法2.3 格式2.4 脚本外传参2.5 嵌套 三. 访问其它主机 交互&#xff1a;当我们使用程序时&#xff0c;需要进入程序发出对应的指令&am…

清风数学建模学习笔记——Topsis法

数模评价类&#xff08;2&#xff09;——Topsis法 概述 Topsis:Technique for Order Preference by Similarity to Ideal Solution 也称优劣解距离法&#xff0c;该方法的基本思想是&#xff0c;通过计算每个备选方案与理想解和负理想解之间的距离&#xff0c;从而评估每个…

【认证法规】安全隔离变压器

文章目录 定义反激电源变压器 定义 安全隔离变压器&#xff08;safety isolating transformer&#xff09;&#xff0c;通过至少相当于双重绝缘或加强绝缘的绝缘使输入绕组与输出绕组在电气上分开的变压器。这种变压器是为以安全特低电压向配电电路、电器或其它设备供电而设计…

喆塔科技携手国家级创新中心,共建高性能集成电路数智化未来

集创新之力成数智之塔 近日&#xff0c;喆塔科技与国家集成电路创新中心携手共建“高性能集成电路数智化联合工程中心”并举行签约揭牌仪式。出席此次活动的领导嘉宾包含&#xff1a;上海市经济和信息化委员会、上海市集成电路行业协会、复旦大学微电子学院、国家集成电路创新中…