ffmpeg转码与加水印

news2025/1/7 7:36:19

文章目录

    • 转码 与加水印
      • 引入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 就有了,可以正常使用

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

tomcat 运行加载机制解析

tomcat 运行加载机制 从tomcat jar包的加载顺序&#xff1a; tomcat的具体运行加载 可以从 start、setclasspath、catalina文件中看出来&#xff1a; start.bat执行 去找bin目录下的catalina.bat,catalina 或去找 bin\setenv.bat以获取标准环境变量&#xff0c;然后去找bin\…

策略模式实战 - 鸭展

该示例出自著名的《HeadFirst》系列的《HeadFirst设计模式》图书的第一个设计模式。用一个鸭子展览的小应用&#xff0c;一步步揭示了如何引入和使用策略模式将示例改造的完美一些。 文章目录 红头鸭与绿头鸭橡皮鸭和诱饵鸭用接口代替继承组合关系与策略模式 红头鸭与绿头鸭 当…

B4X编程语言:设置B4J控件的上下文菜单(ContextMenu)

B4J控件的ContextMenu属性&#xff0c;也叫上下文菜单属性&#xff0c;用于在用户右键点击控件时显示一个自定义菜单(右键菜单)。在B4J中&#xff0c;设置右键菜单有两种方法&#xff1a;一种是直接用代码设置&#xff0c;一种是在设计器设置。 假设在B4XMainPage页面有…

计算机网络·考点知识点整理

根据华科历年计网题&#xff0c;整理了一些常考的知识点难点。 因特网五层协议的功能 层次名称协议功能描述典型协议举例&#xff08;至少两种&#xff09;协议分组名称应用层制定两个应用进程之间的通信规范HTTP、SMTP、FTP、Telnet、POP3、IMAP报文运输层实现进程与进程之间…

创建 React Native 项目

创建 React Native 项目 npx react-nativelatest init YourProject切换依赖源 切换好源之后&#xff0c;你需要进入 android 目录&#xff0c;然后运行 gradlew build 命令。 Android 依赖安装是使用 gradlew 进行管理的。 $ cd android $ ./gradlew build --refresh-depend…

【Vue3中Router使用】

Vue3中Router使用 1. 安装vue-router组件2. 建两个测试页面2.1 测试页面Home.vue2.2 测试页面Category.vue 3. 创建路由对象4. 在入口main.js中引入router把App.vue改成路由页面5. 测试5.1 关闭检查解决ESlint报错5.2 改文件名解决ESlint检查报错测试WebHashHistory 和WebHisto…

AD20 原理图库更新到原理图

一 点击工具&#xff0c;从库更新。快捷键TL 二 点击完成 三 执行变更&#xff0c;最后点击关闭

学习SqlSugar调用达梦数据库的存储过程的基本用法

将之前学习达梦数据库递归用法的SQL语句封装为存储过程&#xff0c;然后使用SqlSugar在C#程序中调用。   打开达梦管理工具&#xff0c;在SCHOOL数据库的存储过程文件夹新建存储过程&#xff0c;这里需注意&#xff0c;存储过程名称及参数名称都需要大写&#xff0c;且参数名…

如何让Google快速收录你的页面?

要让Google更快地收录你的网站内容&#xff0c;首先需要理解“爬虫”这个概念。Google的爬虫是帮助它发现和评估网站内容质量的工具&#xff0c;如果你的页面质量高且更新频率稳定&#xff0c;那么Google爬虫更可能频繁光顾。通常情况下&#xff0c;通过Google Search Console&…

思特奇政·企数智化产品服务平台正式发布,助力运营商政企数智能力跃迁

数字浪潮下,产业数字化进程加速发展,信息服务迎来更广阔的天地,同时也为运营商政企支撑系统提出了更高要求。12月4日,2024数字科技生态大会期间,思特奇正式发布政企数智化产品服务平台,融合应用大数据、AI等新质生产要素,构建集平台服务、精准营销、全周期运营支撑、智慧大脑于…

模型 AITDA(吸引、兴趣、信任、渴望、行动)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。吸引、兴趣、信任、渴望、行动 五步曲。 1 模型AITDA的应用 1.1 开源AI智能名片小程序的营销策略 一家企业开发了开源AI智能名片小程序&#xff0c;旨在通过S2B2C模式连接供应商和消费者。该企业采用…

工业—使用Flink处理Kafka中的数据_ProduceRecord1

1 、 使用 Flink 消费 Kafka 中 ProduceRecord 主题的数据,统计在已经检验的产品中,各设备每 5 分钟 生产产品总数,将结果存入Redis 中, key 值为

OpenSSH和OpenSSL升级

需求 centos7.9升级SSH和SSL OpenSSH升级为openssh9.8 OpenSSL升级为openssl-3.4.0 下载openssh最新版本与openssl对应版本 openssh最新版本下载地址 wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.8p1.tar.gzOpenSSL下载地址 这里下载的是3.4.0 wg…

大语言模型(2)--GPT-1

GPT-1是由OpenAI在2018年推出的第一代生成式预训练模型&#xff08;《Improving Language Understanding by Generative Pre-Training》&#xff09;&#xff0c;它采用了无监督预训练和有监督微调相结合的方法&#xff0c;以增强模型的通用任务求解能力。在此之前&#xff0c;…

IDEA 鼠标悬浮显示方法注释 javaDoc 及配置遇到的问题

方法详情&#xff1a; 鼠标悬浮时的效果&#xff1a; 设置方法&#xff1a; File -> Settings -> Editor -> Code Editing -> Quick Documentation,勾选红框中的选项 可能会遇到的问题&#xff1a; 如果不能选中&#xff0c;如下图 把下图的位置的选中项取消掉 选…

微信小程序实现图片拖拽调换位置效果 -- 开箱即用

在编写类似发布朋友圈功能的功能时&#xff0c;需要实现图片的拖拽排序&#xff0c;删除图片等功能。 一、效果展示 **博主的小程序首页也采用了该示例代码&#xff0c;可以在威信中搜索&#xff1a;我的百宝工具箱 二、示例代码 1.1、在自己的小程序中创建组件 1.2、组件…

import是如何“占领满屏“

import是如何“占领满屏“的&#xff1f; 《拒绝使用模块重导&#xff08;Re-export&#xff09;》 模块重导是一种通用的技术。在腾讯、字节、阿里等各大厂的组件库中都有大量使用。 如&#xff1a;字节的arco-design组件库中的组件&#xff1a;github.com/arco-design… …

鸿蒙分享(二):引入zrouter路由跳转+封装

码仓库&#xff1a;https://gitee.com/linguanzhong/share_harmonyos 鸿蒙api:12 鸿蒙第三方库地址&#xff1a;OpenHarmony三方库中心仓 zrouter地址&#xff1a;OpenHarmony三方库中心仓 1.引入zrouter 1.打开终端界面&#xff1a;输入 ohpm install hzw/zrouter 2.在项目…

第七节(2)、T型加减速优化处理【51单片机-TB6600驱动器-步进电机教程】

摘要&#xff1a;本节介绍解决标准T型加减速过程中的两个缺陷&#xff0c;其一是使得初速度任意设置&#xff1b;其二是降低Cn递推计算量&#xff0c;提升速度上限 一. 加速减速过程计算 1.1计算不存在匀速过程 根据基本运动定理&#xff1a; w m a x w 0 a 0 ∗ t n 0 … …

MySQL--用户权限

1.使用root用户登录MySQL客户端&#xff0c;创建一个名为userl的用户&#xff0c;初始密码为123456;创建一个名为user2的用户&#xff0c;无初始密码。然后&#xff0c;分别使用uesr1、user2登录MySQL 客户端。 创建两个用户 使用user1登录 使用user2登录 2.使用root用户登录&a…