ffmpeg使用及java操作

发布时间:2024年01月22日

1.文档

官网:?FFmpeg
官方使用文档:?ffmpeg Documentation

中文简介:?https://www.cnblogs.com/leisure_chn/p/10297002.html

函数及时间:?ffmpeg日记1011-过滤器-语法高阶,逻辑,函数使用_ffmpeg gte(t,2)-CSDN博客

java集成ffmpeg:?SpringBoot集成ffmpeg实现视频转码播放_jave-all-deps-CSDN博客?

2.命令

快速复制视频片段
ffmpeg -ss 00:00:40 -i test.mp4 -to 00:01:00 -c:v copy -c:a copy  testOutput40.mp4

复制视频片段,并调整视频质量,crf一般为23,crf越大压缩越厉害,质量下降越多
ffmpeg -i test.mp4 -ss 00:30 -to 00:50 -c:v libx264 -crf 23 newTest1.mp4

调整视频分辨率(调整的分辨率超过原来的,不能提升画质)
ffmpeg -i test.mp4 -vf scale=432:240 video_240p.mp4 -hide_banner
ffmpeg -i test.mp4 -vf scale=-1:240 video_240p.mp4 -hide_banner

加水印(10:10水印距离左上角的像素距离)
ffmpeg -i test.mp4 -i used100.png -filter_complex "overlay=10:10" testOutputWater.mp4

--保真加水印,不怎么改变视频编码及质量
ffmpeg -i test.mp4 -i used100.png -filter_complex "overlay=10:10" -c:v libx264 -c:a copy testOutputWater2.mp4

--视频2秒后开始显示水印,gte(t\,2)
ffmpeg -i test.mp4 -vf "movie=used100.png[logo];[in][logo]overlay=x='if(gte(t\,2)\,0\,NAN)'" testOutputWater6.mp4

--在指定时间范围(20-30秒)显示水印,between(t\,20\,30)
ffmpeg -i test.mp4 -vf "movie=used100.png[logo];[in][logo]overlay=x='if(between(t\,20\,30)\,0\,NAN)'" testOutputWater9.mp4

加硬字幕
ffmpeg -i test.mp4 -vf subtitles=test.srt mp4_add_captions1.mp4

3.java代码

? ? ? ? maven依赖,使用集成的ffmpeg程序,缺点是打成的jar包很大,优点是不需要手动安装ffmpeg

        <!--   音视频   -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-all-deps</artifactId>
            <version>3.0.1</version>
            <exclusions>
                <!--  排除windows 32位系统      -->
                <exclusion>
                    <groupId>ws.schild</groupId>
                    <artifactId>jave-nativebin-win32</artifactId>
                </exclusion>
                <!--  排除linux 32位系统      -->
                <exclusion>
                    <groupId>ws.schild</groupId>
                    <artifactId>jave-nativebin-linux32</artifactId>
                </exclusion>
                <!-- 排除Mac系统-->
                <exclusion>
                    <groupId>ws.schild</groupId>
                    <artifactId>jave-nativebin-osx64</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

? ? ? ? ? 第二种方式,不引入集成的ffmpeg,手动在程序运行的服务器上安装,并设置环境变量,优点是打成的jar包较小,需自己注意版本及兼容性

        <!--音视频操作-->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>3.0.1</version>
        </dependency>

????????工具类

package mis.shared.file;

import lombok.extern.slf4j.Slf4j;
import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

/**
 * 音视频操作
 *
 * @since 2024/1/22
 */
@Slf4j
public class FfmpegUtil {

    /**
     * 通过本地路径获取多媒体文件信息(宽,高,时长,编码等)
     *
     * @param localPath 文件路径
     * @return MultimediaInfo 媒体对象,包含 (宽,高,时长,编码等)
     */
    public static MultimediaInfo GetMediaInfo(String localPath) {
        MultimediaInfo multimediaInfo = null;
        try {
            multimediaInfo = new MultimediaObject(new File(localPath)).getInfo();
        } catch (EncoderException e) {
            log.error("获取媒体信息异常!", e);
        }
        return multimediaInfo;
    }

    /**
     * 修改视频分辨率,修改分辨率超过原视频,不能提高画质
     * 标清SD(Standard Definition) 宽 x 高
     * 480p 640x480 704x480 720x480 848x480
     * 高清 HD(High Definition)
     * 720p 960x720 1280x720
     * 1080p 1440x1080 1920x1080
     * 超高清UHD(Ultra High Definition)
     * 4k 4096×3112 4096*2160
     *
     * @param inputPath  视频来源地址
     * @param outputPath 输出视频地址
     * @param width      宽度
     * @param height     高度
     */
    public static boolean ChangeScale(String inputPath, String outputPath, int width, int height) {
        ProcessWrapper ffmpeg = null;
        try {
            if (new File(outputPath).exists()) {
                log.error("目标文件已存在,outputPath:{}", outputPath);
                return false;
            }

            ffmpeg = new DefaultFFMPEGLocator().createExecutor();
            ffmpeg.addArgument("-i");
            ffmpeg.addArgument(inputPath);
            ffmpeg.addArgument("-vf");
            ffmpeg.addArgument("scale=" + width + ":" + height);
            ffmpeg.addArgument("-c:a");
            ffmpeg.addArgument("copy");
            ffmpeg.addArgument(outputPath);
            long start = System.currentTimeMillis();
            //异步执行
            ffmpeg.execute();
            //等待完成
            WaitFfmpegFinish(ffmpeg);
            log.info("花费时间:{}", System.currentTimeMillis() - start);
            return true;
        } catch (Exception e) {
            log.error("修改视频画质异常", e);
        } finally {
            if (ffmpeg != null) {
                ffmpeg.destroy();
            }
        }
        return false;
    }

    /**
     * 复制视频片段
     *
     * @param inputPath  视频来源地址
     * @param outputPath 输出视频地址
     * @param startTime  开始时间,01:02:03在视频的第1小时第2分钟第3秒处
     * @param endTime    结束时间,格式同startTime
     */
    public static boolean CopyVideo(String inputPath, String outputPath, String startTime, String endTime) {
        ProcessWrapper ffmpeg = null;
        try {
            if (new File(outputPath).exists()) {
                log.error("目标文件已存在,outputPath:{}", outputPath);
                return false;
            }

            ffmpeg = new DefaultFFMPEGLocator().createExecutor();
            ffmpeg.addArgument("-ss");
            ffmpeg.addArgument(startTime);
            ffmpeg.addArgument("-i");
            ffmpeg.addArgument(inputPath);
            ffmpeg.addArgument("-to");
            ffmpeg.addArgument(endTime);
            ffmpeg.addArgument("-c:v");
            ffmpeg.addArgument("copy");
            ffmpeg.addArgument("-c:a");
            ffmpeg.addArgument("copy");
            ffmpeg.addArgument(outputPath);
            long start = System.currentTimeMillis();
            //异步执行
            ffmpeg.execute();
            //等待完成
            WaitFfmpegFinish(ffmpeg);
            log.info("花费时间:{}", System.currentTimeMillis() - start);
            return true;
        } catch (Exception e) {
            log.error("复制视频片段异常", e);
        } finally {
            if (ffmpeg != null) {
                ffmpeg.destroy();
            }
        }
        return false;
    }

    /**
     * 视频格式转换为mp4
     */
    public static boolean FormatToMp4(String localPath, String outputPath) {
        try {
            File target = new File(outputPath);
            if (target.exists()) {
                log.error("目标文件已存在,outputPath:{}", outputPath);
                return false;
            }

            MultimediaObject multimediaObject = new MultimediaObject(new File(localPath));
            EncodingAttributes attributes = new EncodingAttributes();
            // 设置视频的音频参数
            AudioAttributes audioAttributes = new AudioAttributes();
            attributes.setAudioAttributes(audioAttributes);
            // 设置视频的视频参数
            VideoAttributes videoAttributes = new VideoAttributes();
            // 设置帧率
            videoAttributes.setFrameRate(25);
            attributes.setVideoAttributes(videoAttributes);
            // 设置输出格式
            attributes.setOutputFormat("mp4");
            Encoder encoder = new Encoder();
            encoder.encode(multimediaObject, target, attributes);
            return true;
        } catch (Exception e) {
            log.error("视频转换异常!", e);
            return false;
        }
    }

    /**
     * 获取视频缩略图
     * 获取视频第0秒的第一帧图片
     */
    public static boolean GetThumbnail(String localPath, String outputPath) {
        ProcessWrapper ffmpeg = null;
        try {
            File target = new File(outputPath);
            if (target.exists()) {
                log.error("目标文件已存在,outputPath:{}", outputPath);
                return false;
            }

            ffmpeg = new DefaultFFMPEGLocator().createExecutor();
            ffmpeg.addArgument("-i");
            ffmpeg.addArgument(localPath);
            ffmpeg.addArgument("-ss");
            ffmpeg.addArgument("0");
            ffmpeg.addArgument(outputPath);
            ffmpeg.execute();

            //等待执行完成
            WaitFfmpegFinish(ffmpeg);
        } catch (Exception e) {
            log.error("获取视频缩略图异常", e);
            return false;
        } finally {
            if (ffmpeg != null) {
                ffmpeg.destroy();
            }
        }
        return true;
    }

    /**
     * 等待命令执行完成
     */
    private static void WaitFfmpegFinish(ProcessWrapper processWrapper) throws Exception {
        //输出执行情况不是通过inputStream,而是errorStream
        try (BufferedReader br = new BufferedReader(new InputStreamReader(processWrapper.getErrorStream()))) {
            String processInfo;
            while ((processInfo = br.readLine()) != null) {
                //打印执行过程
                log.trace(processInfo);
            }
        } catch (Exception e) {
            log.error("等待执行完成异常!", e);
        }
    }

    public static void main(String[] args) {
        String inputFile = "e://develop//tmp//test.mp4";
        String outputFile = "e://develop//tmp//testOutput14.mp4";

        //复制视频指定片段
        CopyVideo(inputFile, outputFile, "00:00:10", "00:01:00");

        //获取媒体信息
        // MultimediaInfo multimediaInfo = GetMediaInfo(inputFile);
        // log.info("文件信息:{}", multimediaInfo);
        // VideoSize videoSize = multimediaInfo.getVideo().getSize();
        // log.info("文件信息,宽:{},高:{}", videoSize.getWidth(), videoSize.getHeight());

        //修改视频画质
        //ChangeScale(inputFile, outputFile, 640, 480);
    }

}

附录1

? ? ? ? ?字幕test.srt

1
00:00:20,000 --> 00:00:30,000
这是视频第20秒到30秒将显示的字幕

2
00:00:50,000 --> 00:00:60,000
这是视频第50秒到60秒将显示的字幕

文章来源:https://blog.csdn.net/leadseczgw01/article/details/135746222
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。