1. 引入 ws.schild MAVEN 依赖:
<dependency><groupId>ws.schild</groupId><artifactId>jave-all-deps</artifactId><version>3.5.0</version></dependency><dependency><groupId>ws.schild</groupId><artifactId>jave-core</artifactId><version>3.5.0</version></dependency><dependency><groupId>ws.schild</groupId><artifactId>jave-nativebin-win64</artifactId><version>3.5.0</version></dependency>
2. MyVideoUtils:
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.AudioInfo;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.info.VideoInfo;
import ws.schild.jave.info.VideoSize;
import ws.schild.jave.progress.EncoderProgressListener;import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.Set;public class MyVideoUtils {public static void main(String[] args) throws EncoderException {// 压缩前文件路径File source = new File("D:\\素材\\视频\\video_0001.mp4");// 压缩后的文件路径File target = new File("D:\\素材\\视频\\video_0001_output1.mp4");compress( source,target,0.3 );
}public static void compress(File sourceVideoFile, File targetVideoFile, Double scale) throws EncoderException {try {System.out.println("---------------开始压缩---------------");long start = System.currentTimeMillis();// 尺寸的比例BigDecimal scale_size = BigDecimal.valueOf(scale);// 码率相关的比率BigDecimal scale_rate = BigDecimal.valueOf(scale).multiply(BigDecimal.valueOf(scale));// 输出源视频的信息MultimediaObject multimediaObject_source = new MultimediaObject(sourceVideoFile);MultimediaInfo multimediaInfo_source = multimediaObject_source.getInfo();AudioInfo audioInfo_source = multimediaInfo_source.getAudio();VideoInfo videoInfo_source = multimediaInfo_source.getVideo();// 时长long seconds = multimediaInfo_source.getDuration() / 1000L;// 每秒几帧float frameRate = videoInfo_source.getFrameRate();System.out.println( "seconds = " + seconds );System.out.println( "frameRate = " + frameRate );int totalFrame = BigDecimal.valueOf(seconds).multiply(BigDecimal.valueOf(frameRate)).intValue();System.out.println( "totalFrame = " + totalFrame );System.out.println( "原视频 bitRate = " + videoInfo_source.getBitRate() );System.out.println( "原视频 frameRate = " + videoInfo_source.getFrameRate() );System.out.println( "原视频 decoder = " + videoInfo_source.getDecoder() );VideoSize videoSize_source = videoInfo_source.getSize();System.out.println( "源视频宽x高:" + videoSize_source.getWidth() + "x" + videoSize_source.getHeight() );Map<String, String> metadata = videoInfo_source.getMetadata();Set<String> keys = metadata.keySet();for( String key:keys ){System.out.println( key + " = " + metadata.get( key ) );}System.out.println();// 音频编码属性配置AudioAttributes audioAttributes = new AudioAttributes();audioAttributes.setCodec("libmp3lame");int audioBitRate_new = BigDecimal.valueOf(audioInfo_source.getBitRate()).multiply(scale_rate).intValue();System.out.println( "audioBitRate_new = " + audioBitRate_new );audioAttributes.setBitRate( audioBitRate_new );// 设置重新编码的音频流中使用的声道数(1 =单声道,2 = 双声道(立体声))audioAttributes.setChannels(1);// int audioSamplingRate_new = BigDecimal.valueOf(audioInfo_source.getSamplingRate()).multiply(scale_rate).intValue();// System.out.println( "audioSamplingRate_new = " + audioSamplingRate_new );// todo 设置此值报错 "ws.schild.jave.EncoderException: Exit code of ffmpeg encoding run is 1",暂不知道什么原因???// audioAttributes.setSamplingRate( audioSamplingRate_new );// 视频编码属性配置VideoAttributes videoAttributes = new VideoAttributes();// 设置编码// videoAttributes.setCodec("mpeg4");videoAttributes.setCodec( "h264" );int videoBitRate_new = BigDecimal.valueOf(videoInfo_source.getBitRate()).multiply(scale_rate).intValue();System.out.println( "videoBitRate_new = " + videoBitRate_new );videoAttributes.setBitRate( videoBitRate_new );int newHeight = BigDecimal.valueOf(videoInfo_source.getSize().getHeight()).multiply(scale_size).intValue();int newWidth = BigDecimal.valueOf(videoInfo_source.getSize().getWidth()).multiply(scale_size).intValue();// 新的宽高都必须是2的整数倍!!!newHeight = MyMathUtils.getClosestNuumberThatCanBeDividedBy2( newHeight );newWidth = MyMathUtils.getClosestNuumberThatCanBeDividedBy2( newWidth );VideoSize videoSize_new = new VideoSize( newWidth,newHeight );videoAttributes.setSize( videoSize_new );// 编码设置EncodingAttributes encodingAttributes = new EncodingAttributes();encodingAttributes.setOutputFormat("mp4");encodingAttributes.setAudioAttributes( audioAttributes );encodingAttributes.setVideoAttributes( videoAttributes );// 设置值编码Encoder encoder = new Encoder();// encoder.encode( multimediaObject_source, targetVideoFile, encodingAttributes );// 压缩转换进度监听EncoderProgressListener encoderProgressListener = new EncoderProgressListener() {@Overridepublic void sourceInfo(MultimediaInfo info) {}@Overridepublic void progress(int permil) {double processPercent = BigDecimal.valueOf(permil).divide(BigDecimal.valueOf(1000d), 4, RoundingMode.HALF_UP).doubleValue();System.out.println( "压缩转换进度:" + MyMathUtils.double2PercentFormat( processPercent ) );}@Overridepublic void message(String message) {System.out.println( "message ------------------------------> " + message );}};encoder.encode( multimediaObject_source, targetVideoFile, encodingAttributes,encoderProgressListener );System.out.println("---------------结束压缩---------------");long end = System.currentTimeMillis();System.out.println("压缩前大小:"+ MyMathUtils.byte2M( sourceVideoFile.length() ) + "M,压缩后大小:" + MyMathUtils.byte2M( targetVideoFile.length() ) + "M" );System.out.println("压缩耗时:" + MyMathUtils.mill2Second( ( end - start ) ) + "秒" );} catch (EncoderException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();}}
}
3. MyMathUtils:
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;public class MyMathUtils {public static Integer getScale(Double num) {if( num == null || num == 0d ){return 0;}double decimalPart = BigDecimal.valueOf(num).subtract(BigDecimal.valueOf(num.intValue())).doubleValue();if( decimalPart == 0d ){return 0;}String decimalPartStr = String.valueOf(decimalPart);String decimalPartStr1 = decimalPartStr.substring( decimalPartStr.indexOf(".") + 1 );return decimalPartStr1.length();}/*** 计算 Double 集合中不为空并且大于0的元素的个数* @param nums* @return*/public static Integer calculateCountForNotNullAndBiggerThanZero(List<Double> nums) {if( nums == null || nums.size()== 0 ){return 0;}Integer count = 0;for( Double num:nums ){if( num != null && num > 0d ){count++;}}return count;}public static Double calculateSum(List<Double> nums) {if( nums == null || nums.size() == 0 ){return 0d;}BigDecimal sum = BigDecimal.ZERO;for( Double num:nums ){if( num == null || num == 0d ){continue;}sum = sum.add( BigDecimal.valueOf( num ) );}return sum.doubleValue();}public static Double nullDouble2Zero(Double num) {if( num == null ){return 0d;}return num;}public static Integer nullInteger2Zero(Integer num) {if( num == null ){return 0;}return num;}public static Double float2Double(Float f) {if( f == null ){return null;}return f.doubleValue();}public static String double2PercentFormat(Double d) {if( d == null ){d = 0d;}double d_percent = BigDecimal.valueOf(d).multiply(BigDecimal.valueOf(100d)).setScale(4, RoundingMode.HALF_UP).doubleValue();return d_percent + "%";}public static Double byte2M( Long byteLenth ){return BigDecimal.valueOf( byteLenth ).divide( BigDecimal.valueOf( 1024d ),4,RoundingMode.HALF_UP ).divide( BigDecimal.valueOf( 1024d ),4,RoundingMode.HALF_UP ).doubleValue();}public static Double mill2Second( Long mill ){return BigDecimal.valueOf( mill ).divide( BigDecimal.valueOf( 1000d ),4,RoundingMode.HALF_UP ).doubleValue();}/*** 获得与传入的数字 num 最接近的能被2整除的数字* @param num* @return*/public static int getClosestNuumberThatCanBeDividedBy2(int num) {int result = BigDecimal.valueOf(BigDecimal.valueOf(num).divide(BigDecimal.valueOf(2d), 2, RoundingMode.HALF_UP).intValue()).multiply(BigDecimal.valueOf(2d)).intValue();return result;}
}
4. 将压缩后的文件上传至文件服务器( 例如 minio )获得在线 url,例如 :https://xxx.xxx.com/minio/xxx-bucket/20240705131522-124765sd65sad65sa6d7asd6sa7d56235e675sadasd.mp4,并使用 ht video 标签播放:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>MP4 Video Player Demo</title>
</head>
<body><video width="320" height="240" controls><source src="https://xxx.xxx.com/minio/xxx-bucket/20240705131522-124765sd65sad65sa6d7asd6sa7d56235e675sadasd.mp4" type="video/mp4">您的浏览器不支持Video标签。</video>
</body>
</html>
播放效果如下:
只有声音没有画面,但是播放压缩之前的原始视频就ok,使用 vlc查看下视频编码信息:
发现编解码器不一样,于是尝试将编解码器换成H264试一下:
// videoAttributes.setCodec("mpeg4");videoAttributes.setCodec( "h264" );
重新压缩后播放出画面了: