使用ffmpeg或Javacv转换视频

背景

最近有一个需求,需要将一个mp4的视频转换为avi的视频,为此,查找资料找到了ffmpeg、Javacv、Xuggler等方法,其中Xuggler我导不入依赖,就放弃了,这里简单记录一下FFmpeg和Javacv的使用。

实现

FFmpeg

FFmpeg(Fast Forward MPEG)是一个强大的开源多媒体框架和工具集,用于处理音频、视频、多媒体流以及多种多媒体文件格式。它是一个跨平台工具,可在多个操作系统上运行,包括Linux、Windows、MacOS X等。

一:准备工作

1、安装FFmpeg。找到FFmpeg的官网,选择自己对应的系统的FFmpeg压缩包,下载并解压到自己的电脑。
2、将FFmpeg运行路径添加到环境变量中。

二:代码实现

    /** Windows系统* */
    public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("win");
    /** Linux系统* */
    public static final boolean IS_LINUX = System.getProperty("os.name").toLowerCase().contains("linux");
  
     /**
     * ffmpeg -i input.mp4 -c:v mjpeg output.avi
     * 使用ffmpeg转码
     * @return
     */
    public void ffmpegDecode(String path,String newFilePath,Integer width,Integer height) {
        /**
         * -an 表示不保留音频流。
         * -codec(-c) 指定输入输出的解码编码器 codec指定为copy则将输入流直接复制到输出流不进行编解码操作。
         * -r 设置某个流的帧率
         *-f 强制设置输入输出的文件格式,默认情况下ffmpeg会根据文件后缀名判断文件格式.
         *
         */
        String cmd;
        /*如果是windows系统**/
        if (IS_WINDOWS) {
            log.info("Windows系统");

            cmd="ffmpeg -i "+path;
            if (null!=width&&null!=height){
                cmd=cmd+" -s "+width+"x"+height;
            }
            cmd=cmd+" -c:v mjpeg "+newFilePath;
            winExec(cmd, newFilePath, () -> dosomething);
        }
        /*如果是Linux系统**/
        if (IS_LINUX){
            log.info("Linux系统");

            cmd = "/usr/local/ffmpeg/bin/ffmpeg -i " + path;
            if (null!=width&&null!=height){
                cmd=cmd+" -s "+width+"x"+height;
            }
            cmd=cmd+" -c:v mjpeg "+newFilePath;
            linuxExec(cmd,newFilePath,()-> dosomething);
        }
    }

Windows

    /**
     * 在Windows上执行ffmpeg转码命令
     * @param cmd 命令
     */
    public void winExec(String cmd ,String newPath, Runnable callback ) {
        log.info("执行命令[ " + cmd + "]");
        Runtime runtime = Runtime.getRuntime();
        try {
            Process process = runtime.exec(cmd);
            new DealProcessStream(process.getInputStream()).start();
            new DealProcessStream(process.getErrorStream()).start();
            // 等待ffmpeg进程执行完毕
            int exitCode = process.waitFor();
            if (exitCode == 0) {
             	//do something.
            } else {
                // 处理ffmpeg执行失败的情况
                log.error("转换失败");
            }
            callback.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Linux

    /**
     * 在Linux上执行ffmpeg转码命令
     * @param cmd 命令
     */
    public void linuxExec(String cmd,String newPath, Runnable callback) {
        log.info("执行命令[" + cmd + "]");
        String[] cmds = {"/bin/sh","-c",cmd};
        Process process;
        Runtime run = Runtime.getRuntime();
        try {
            process = run.exec(cmds);
            // Process process = run.exec(new String[]{"sh", "-c", cmd});
            Field field = process.getClass().getDeclaredField("pid");
            field.setAccessible(true);
            new DealProcessStream(process.getInputStream()).start();
            new DealProcessStream(process.getErrorStream()).start();
            // 等待ffmpeg进程执行完毕
            int exitCode = process.waitFor();
            if (exitCode == 0) {
            	//do something.
            } else {
                // 处理ffmpeg执行失败的情况
               log.error("转换失败");
            }
            callback.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

处理流信息的线程

public class DealProcessStream extends Thread {

        private InputStream inputStream;

        public DealProcessStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        public void run() {
            InputStreamReader inputStreamReader = null;
            BufferedReader br = null;
            try {
                inputStreamReader = new InputStreamReader(inputStream);
                br = new BufferedReader(inputStreamReader);
                // 不打印信息
                while (br.readLine() != null);
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }finally {
                try {
                    if (br != null) {
                        br.close();
                    }
                    if (inputStreamReader != null) {
                        inputStreamReader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
}

我这里只是将mp4视频简单的转换为指定大小的avi视频,ffmpeg的功能非常强大,如果想要了解更多功能,需要详细去了解一下。

Javacv

JavaCV(Java Computer Vision)是一个用于计算机视觉和多媒体处理的开源Java库,它基于多个计算机视觉和多媒体处理库,如OpenCV、FFmpeg、FFtw和其他一些库。JavaCV提供了对这些库的Java绑定,使Java开发人员能够利用这些底层库的功能,以便进行图像处理、视频处理、计算机视觉任务等。

导入依赖

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>1.5.8</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>1.5.8</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.21-1.5.8</version>
            <classifier>linux-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.21-1.5.8</version>
            <classifier>windows-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>5.1.2-1.5.8</version>
            <classifier>linux-x86_64</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>5.1.2-1.5.8</version>
            <classifier>windows-x86_64</classifier>
        </dependency>

实现

    public  static void convertMp4ToAvi(String inputMp4Path, String outputAviPath,Integer width,Integer height){
        FFmpegLogCallback.set();
        // 设置日志级别为 AV_LOG_QUIET,禁用所有日志信息
        avutil.av_log_set_level(avutil.AV_LOG_QUIET);
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputMp4Path);
        try {
            grabber.start();
            if (null==width||null==height){
                width = grabber.getImageWidth();
                height = grabber.getImageHeight();
            }
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputAviPath, width, height);
            recorder.setFormat("avi");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_MJPEG); //设置视频解码器
            recorder.setVideoBitrate(grabber.getVideoBitrate()); // 设置视频比特率,根据需要进行调整
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 设置音频编解码器
            recorder.setFrameRate(grabber.getFrameRate());
            // recorder.setAudioCodec(avcodec.AV_CODEC_ID_NONE); //去掉音频
            recorder.setAudioChannels(2); // 设置音频通道数
            recorder.start();
            while (true) {
                org.bytedeco.javacv.Frame frame = grabber.grab();
                if (frame == null) {
                    break;
                }
                recorder.record(frame);
            }
            grabber.stop();
            grabber.release();
            recorder.stop();
            recorder.release();
        } catch (FFmpegFrameGrabber.Exception | FFmpegFrameRecorder.Exception e) {
            e.printStackTrace();
        }
    }

使用JavaCV来进行视频转码,其实就是使用JavaCV的FFmpeg部分来进行视频编解码和转换,本质上还是FFmpeg。使用JavaCV后,打包的文件会变得特别大,介意这点的需要注意一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值