借鉴github上封装ffmpeg的工具来压缩、剪辑视频数据

使用java技术栈来对视频进行压缩和剪辑。
方案一:jave2实现–底层封装的是ffmpeg,但是代码不在维护了。
github地址:https://github.com/a-schild/jave2

import ws.schild.jave.Encoder;
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.AudioInfo;
import ws.schild.jave.info.VideoInfo;

import java.io.File;
import java.math.BigDecimal;

/**
 * 视频压缩工具类--参考网上的资料测试的 JAVE2
 * github: https://github.com/a-schild/jave2
 */
public class VideoCompressorUtil {
    public static final Logger logger = LoggerFactory.getLogger(VideoUtil.class);

    /**
     * 传视频File对象(这是一个具体的文件),返回压缩后File对象信息
     *
     * @param source
     */
    public static File compressionVideo(File source, String picName) {
        if (source == null) {
            return null;
        }
        String newPath = source.getAbsolutePath().substring(0, source.getAbsolutePath().lastIndexOf(File.separator)).concat(File.separator).concat(picName);
        File target = new File(newPath);
        try {
            MultimediaObject object = new MultimediaObject(source);
            // 根据视频大小来判断是否需要进行压缩,
            int maxSize = 200;
            double mb = Math.ceil(source.length() / 1048576);
            int second = (int) object.getInfo().getDuration() / 1000;
            BigDecimal bd = new BigDecimal(String.format("%.4f", mb / second));
            System.out.println("开始压缩视频了--> 视频每秒平均 " + bd + " MB ");
            // 视频 > 200MB, 或者每秒 > 0.5 MB 才做压缩
            boolean temp = mb > maxSize || bd.compareTo(new BigDecimal(0.5)) > 0;
            if (temp) {
                long time = System.currentTimeMillis();
                // 视频压缩参数
                int maxAudioBitRate = 128000;
                int maxSamplingRate = 44100;
                int maxVideoBitRate = 800000;
                int maxFrameRate = 30;

                // 音频属性设置
                AudioInfo audioInfo = object.getInfo().getAudio();
                AudioAttributes audio = new AudioAttributes();
                // 设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 128000 = 182kb)
                if (audioInfo.getBitRate() > maxAudioBitRate) {
                    audio.setBitRate(new Integer(maxAudioBitRate));
                }
                // 设置重新编码的音频流中使用的声道数(1 =单声道,2 = 双声道(立体声))。
                // 如果未设置任何声道值,则编码器将选择默认值 0。
                audio.setChannels(audioInfo.getChannels());
                // 采样率越高声音的还原度越好,文件越大
                // 设置音频采样率,单位:赫兹 hz
                // 设置编码时候的音量值,未设置为0;如果256,则音量值不会改变
                audio.setVolume(256);
                if (audioInfo.getSamplingRate() > maxSamplingRate) {
                    audio.setSamplingRate(maxSamplingRate);
                }

                // 视频编码属性配置
                VideoInfo videoInfo = object.getInfo().getVideo();
                VideoAttributes video = new VideoAttributes();
                //设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 800000 = 800kb)
                if (videoInfo.getBitRate() > maxVideoBitRate) {
                    video.setBitRate(maxVideoBitRate);
                }
                // 视频帧率:15 f / s  帧率越低,效果越差
                // 设置视频帧率(帧率越低,视频会出现断层,越高让人感觉越连续),视频帧率(Frame rate)是用于测量显示帧数的量度。
                // 所谓的测量单位为每秒显示帧数(Frames per Second,简:FPS)或“赫兹”(Hz)。
                if (videoInfo.getFrameRate() > maxFrameRate) {
                    video.setFrameRate(maxFrameRate);
                }

                EncodingAttributes attr = new EncodingAttributes();
                attr.setOutputFormat("mp4");
                attr.setAudioAttributes(audio);
                attr.setVideoAttributes(video);
                // 设置线程数
                attr.setEncodingThreads(Runtime.getRuntime().availableProcessors() / 2);

                Encoder encoder = new Encoder();
                encoder.encode(new MultimediaObject(source), target, attr);
                System.out.println("压缩总耗时:" + (System.currentTimeMillis() - time) / 1000);
                return target;
            }
        } catch (Exception e) {
            logger.error("压缩视频时的错误信息:{}", e.getMessage(), e);
        }
        return source;
    }

    public static void main(String[] args) {
        compressionVideo(new File("C:\\xf\\2024-06.mp4"), "test2.mp4");
    }
}

方案2:采用javacv,同样是封装ffmpeg,资料多
gitHub:https://github.com/bytedeco/javacv

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameRecorder;

/**
 * 视频压缩工具类--利用javaCV 实现
 */
public class VideoCompressor {
    public static final Logger logger = LoggerFactory.getLogger(VideoCompressor.class);

    public static void compressVideo(String inputPath, String outputPath, double fps, double bitrate) throws FrameGrabber.Exception, FrameRecorder.Exception {

        // 参数校验
        if (inputPath == null || outputPath == null || inputPath.isEmpty() || outputPath.isEmpty()) {
            throw new IllegalArgumentException("输入路径和输出路径不能为空");
        }
        if (fps <= 0 || bitrate <= 0) {
            throw new IllegalArgumentException("帧率和比特率必须大于0");
        }
        FFmpegFrameGrabber grabber = null;
        FFmpegFrameRecorder recorder = null;
        try {

            // 初始化帧抓取器
            grabber = new FFmpegFrameGrabber(inputPath);
            grabber.start();

            // 初始化帧记录器
            recorder = new FFmpegFrameRecorder(outputPath, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
            recorder.setFormat("mp4"); // 输出格式
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 视频编码器
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 音频编码器
            recorder.setFrameRate(fps); // 帧率
            recorder.setVideoBitrate((int) (bitrate * 1024)); // 视频比特率
            recorder.setAudioBitrate(128 * 1024); // 音频比特率
            recorder.setSampleRate(grabber.getSampleRate()); // 音频采样率
            recorder.setAudioChannels(grabber.getAudioChannels()); // 音频通道数
            recorder.start();

            Frame frame;
            while ((frame = grabber.grab()) != null) {
                if (frame.image != null) {
                    recorder.record(frame);
                }
                if (frame.samples != null) {
                    recorder.recordSamples(frame.samples);
                }
            }

            // 清理资源

        } catch (Exception e) {
            // 记录详细的异常信息和上下文
            logger.error("视频压缩过程中发生异常:{} ",e.getMessage(),e);
        } finally {
            // 确保资源的释放
            if (recorder != null) {
                try {
                    recorder.stop();
                } catch (Exception e) {
                    // 异常处理,资源释放失败的情况
                    logger.error("释放录制器资源时发生异常:{} ",e.getMessage(),e);
                }
            }
            if (grabber != null) {
                try {
                    grabber.stop();
                } catch (Exception e) {
                    // 异常处理,资源释放失败的情况
                    logger.error("释放录制器资源时发生异常:{} ",e.getMessage(),e);
                }
            }
        }
    }

    public static void main(String[] args) {
        try {
            String inputFilePath = "C:\\xf\\2022.8-2023.1.mp4";
            String outputFilePath = "C:\\xf\\compressed_video.mp4";
            double targetFps = 30; // 目标帧率
            double targetBitrate = 0.5; // 目标比特率,这里是原视频的50%
            System.out.println("Starting video compression...");
            long time = System.currentTimeMillis();
            VideoCompressor.compressVideo(inputFilePath, outputFilePath, targetFps, targetBitrate);
            System.out.println("Video compression completed.");
            System.out.println((System.currentTimeMillis() - time) / 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

视频剪辑

使用的也是javaCV


import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VideoClippingUtil {

    public static final Logger logger = LoggerFactory.getLogger(VideoClippingUtil.class);

    public static void clipVideo(String inputPath, String outputPath, double startTime, double duration) {
        // 参数校验
        if (inputPath == null || outputPath == null || startTime < 0 || duration < 0) {
            logger.error("Invalid input parameters.");
            return;
        }

        FFmpegFrameGrabber grabber = null;
        FFmpegFrameRecorder recorder = null;

        try {
            grabber = new FFmpegFrameGrabber(inputPath);
            grabber.start();

            // 初始化recorder
            recorder = initializeRecorder(outputPath, grabber);

            // 处理帧
            processFrames(grabber, recorder, (int) (startTime * grabber.getFrameRate()),
                    (int) Math.min((startTime + duration) * grabber.getFrameRate(), grabber.getLengthInFrames()));
        } catch (Exception e) {
            logger.error("Failed to clip video.", e);
        } finally {
            // 确保资源被正确关闭
            if (recorder != null) {
                try {
                    recorder.stop();
                } catch (Exception e) {
                    logger.error("Failed to stop recorder.", e);
                }
            }
            if (grabber != null) {
                try {
                    grabber.stop();
                } catch (Exception e) {
                    logger.error("Failed to stop grabber.", e);
                }
            }
        }
    }

    private static FFmpegFrameRecorder initializeRecorder(String outputPath, FFmpegFrameGrabber grabber) throws Exception {
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
        recorder.setFormat("mp4");
        recorder.setFrameRate(grabber.getFrameRate());
        recorder.setVideoQuality(0);
        recorder.start();
        return recorder;
    }

    private static void processFrames(FFmpegFrameGrabber grabber, FFmpegFrameRecorder recorder, int startFrame, int endFrame) throws Exception {
        for (int i = startFrame; i < endFrame; i++) {
            if ((i % grabber.getFrameRate()) == 0) {
                logger.info("Processing frame " + i);
            }
            Frame frame = grabber.grabImage();
            if (frame != null) {
                recorder.record(frame);
            }
        }
    }

    public static void main(String[] args) {
        String inputFilePath = "C:\\xf\\test.mp4";
        String outputFilePath = "C:\\xf\\clip.mp4";
        double startTimeSeconds = 10; // 开始时间,单位秒
        double durationSeconds = 30; // 持续时间,单位秒
        clipVideo(inputFilePath, outputFilePath, startTimeSeconds, durationSeconds);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值