目标
将所有格式的视频转码为H5能播放的mp4格式,也可以截取任意一帧图片
全量依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.3</version>
</dependency>
最小依赖
直接引入javacv-platform依赖,打包后大约有800MB,以下是支持linux-x86_64和windows-x86_64平台的转码和截图功能的最小依赖配置
<properties>
<javacv.version>1.5.3</javacv.version>
<javacv.linux-x86_64>linux-x86_64</javacv.linux-x86_64>
<javacv.windows-x86_64>windows-x86_64</javacv.windows-x86_64>
</properties>
<dependencies>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>${javacv.version}</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>flycapture</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libdc1394</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>libfreenect2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>librealsense2</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>videoinput</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>artoolkitplus</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>flandmark</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>leptonica</artifactId>
</exclusion>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>tesseract</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.2-${javacv.version}</version>
<classifier>${javacv.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.2-${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.9-${javacv.version}</version>
<classifier>${javacv.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.9-${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.3.0-${javacv.version}</version>
<classifier>${javacv.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.3.0-${javacv.version}</version>
<classifier>${javacv.windows-x86_64}</classifier>
</dependency>
</dependencies>
截图配置类
TargetShot.java
package com.videotest.video;
public class TargetShot {
private String targetParentPath;//截图保存目录,如果不设置,则与源文件目录相同
private String prefix = "";//截图文件名前缀
private String suffix = "_shot";//截图文件简单名后缀
private String extName = "jpg";//截图文件扩展名
private int frameNum = 1;//截取视频第几帧
public String getTargetParentPath() {
return targetParentPath;
}
public TargetShot setTargetParentPath(String targetParentPath) {
this.targetParentPath = targetParentPath;
return this;
}
public String getPrefix() {
return prefix;
}
public TargetShot setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
public String getSuffix() {
return suffix;
}
public TargetShot setSuffix(String suffix) {
this.suffix = suffix;
return this;
}
public String getExtName() {
return extName;
}
public TargetShot setExtName(String extName) {
this.extName = extName;
return this;
}
public int getFrameNum() {
return frameNum;
}
public TargetShot setFrameNum(int frameNum) {
if (frameNum < 1) {
frameNum = 1;
}
this.frameNum = frameNum;
return this;
}
@Override
public String toString() {
return "TargetShot [targetParentPath=" + targetParentPath + ", prefix=" + prefix + ", suffix=" + suffix
+ ", extName=" + extName + ", frameNum=" + frameNum + "]";
}
}
转码配置类
TargetVideo.class
package com.videotest.video;
public class TargetVideo {
private String targetParentPath;//转码后的文件保存目录,如果不设置,则与源文件目录相同
private String prefix = "";//转码后的文件名前缀
private String suffix = "_recode";//转码后的文件简单名后缀
private boolean closeSound;//转码后的文件是否删除音轨,默认不删除
public String getTargetParentPath() {
return targetParentPath;
}
public TargetVideo setTargetParentPath(String targetParentPath) {
this.targetParentPath = targetParentPath;
return this;
}
public String getPrefix() {
return prefix;
}
public TargetVideo setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
public String getSuffix() {
return suffix;
}
public TargetVideo setSuffix(String suffix) {
this.suffix = suffix;
return this;
}
public boolean isCloseSound() {
return closeSound;
}
public TargetVideo setCloseSound(boolean closeSound) {
this.closeSound = closeSound;
return this;
}
@Override
public String toString() {
return "TargetVideo [targetParentPath=" + targetParentPath + ", prefix=" + prefix + ", suffix=" + suffix
+ ", closeSound=" + closeSound + "]";
}
}
提取音频配置类
TargetSound.class
package com.videotest.video;
public class TargetSound {
private String targetParentPath;//截图保存目录,如果不设置,则与源文件目录相同
private String prefix = "";//截图文件名前缀
private String suffix = "_sound";//截图文件简单名后缀
private String extName = "mp3";//截图文件扩展名
public String getTargetParentPath() {
return targetParentPath;
}
public TargetSound setTargetParentPath(String targetParentPath) {
this.targetParentPath = targetParentPath;
return this;
}
public String getPrefix() {
return prefix;
}
public TargetSound setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
public String getSuffix() {
return suffix;
}
public TargetSound setSuffix(String suffix) {
this.suffix = suffix;
return this;
}
public String getExtName() {
return extName;
}
public TargetSound setExtName(String extName) {
this.extName = extName;
return this;
}
@Override
public String toString() {
return "TargetSound [targetParentPath=" + targetParentPath + ", prefix=" + prefix + ", suffix=" + suffix
+ ", extName=" + extName + "]";
}
}
源视频处理结果类
VideoInfo.class
package com.videotest.video;
public class VideoInfo<T> {
private T id;//数据唯一标识
private boolean sourceExists;//源文件是否存在
private boolean sourceIsFile;//源文件是否文件
private String sourceAbsolutePath;//sourceParentPath + "/" + sourceName
private String sourceParentPath = "";//源文件目录
private String sourceName = ""; //sourceSimpleName + ["." + extName]
private String sourceSimpleName = "";
private String sourceExtName = "";//扩展名
/**
* @See {org.bytedeco.ffmpeg.global.avcodec}
*/
private int sourceVideoCodec;//视频编码格式
/**
* @See {org.bytedeco.ffmpeg.global.avcodec}
*/
private int sourceAudioCodec;//音频编码格式
private String sourceFormat = "";//封装格式
private boolean shotExists;//处理结果是否存在截图
private String targetShotPath;//截图绝对路径
private String targetShotName;//截图文件名
private boolean videoExists;//处理结果是否存在转码后的视频
private String targetVideoPath;//转码后的视频绝对路径
private String targetVideoName;//转码后的视频文件名
private boolean soundExists;//处理结果是否存在音频
private String targetSoundPath;//提取的音频绝对路径
private String targetSoundName;//提取的音频文件名
public VideoInfo(T id) {
this.id = id;
}
public T getId() {
return id;
}
void setId(T id) {
this.id = id;
}
public boolean isSourceExists() {
return sourceExists;
}
void setSourceExists(boolean sourceExists) {
this.sourceExists = sourceExists;
}
public boolean isSourceIsFile() {
return sourceIsFile;
}
void setSourceIsFile(boolean sourceIsFile) {
this.sourceIsFile = sourceIsFile;
}
public String getSourceAbsolutePath() {
return sourceAbsolutePath;
}
void setSourceAbsolutePath(String sourceAbsolutePath) {
this.sourceAbsolutePath = sourceAbsolutePath;
}
public String getSourceParentPath() {
return sourceParentPath;
}
void setSourceParentPath(String sourceParentPath) {
this.sourceParentPath = sourceParentPath;
}
public String getSourceName() {
return sourceName;
}
void setSourceName(String sourceName) {
this.sourceName = sourceName;
}
public String getSourceSimpleName() {
return sourceSimpleName;
}
void setSourceSimpleName(String sourceSimpleName) {
this.sourceSimpleName = sourceSimpleName;
}
public String getSourceExtName() {
return sourceExtName;
}
void setSourceExtName(String sourceExtName) {
this.sourceExtName = sourceExtName;
}
public int getSourceVideoCodec() {
return sourceVideoCodec;
}
void setSourceVideoCodec(int sourceVideoCodec) {
this.sourceVideoCodec = sourceVideoCodec;
}
public int getSourceAudioCodec() {
return sourceAudioCodec;
}
void setSourceAudioCodec(int sourceAudioCodec) {
this.sourceAudioCodec = sourceAudioCodec;
}
public String getSourceFormat() {
return sourceFormat;
}
void setSourceFormat(String sourceFormat) {
this.sourceFormat = sourceFormat;
}
public boolean getShotExists() {
return shotExists;
}
void setShotExists(boolean shotExists) {
this.shotExists = shotExists;
}
public String getTargetShotPath() {
return targetShotPath;
}
void setTargetShotPath(String targetShotPath) {
this.targetShotPath = targetShotPath;
}
public String getTargetShotName() {
return targetShotName;
}
void setTargetShotName(String targetShotName) {
this.targetShotName = targetShotName;
}
public boolean getVideoExists() {
return videoExists;
}
void setVideoExists(boolean videoExists) {
this.videoExists = videoExists;
}
public String getTargetVideoPath() {
return targetVideoPath;
}
void setTargetVideoPath(String targetVideoPath) {
this.targetVideoPath = targetVideoPath;
}
public String getTargetVideoName() {
return targetVideoName;
}
void setTargetVideoName(String targetVideoName) {
this.targetVideoName = targetVideoName;
}
public boolean isSoundExists() {
return soundExists;
}
void setSoundExists(boolean soundExists) {
this.soundExists = soundExists;
}
public String getTargetSoundPath() {
return targetSoundPath;
}
void setTargetSoundPath(String targetSoundPath) {
this.targetSoundPath = targetSoundPath;
}
public String getTargetSoundName() {
return targetSoundName;
}
void setTargetSoundName(String targetSoundName) {
this.targetSoundName = targetSoundName;
}
@Override
public String toString() {
return "VideoInfo [id=" + id + ", sourceExists=" + sourceExists + ", sourceIsFile=" + sourceIsFile
+ ", sourceAbsolutePath=" + sourceAbsolutePath + ", sourceParentPath=" + sourceParentPath
+ ", sourceName=" + sourceName + ", sourceSimpleName=" + sourceSimpleName + ", sourceExtName="
+ sourceExtName + ", sourceVideoCodec=" + sourceVideoCodec + ", sourceAudioCodec=" + sourceAudioCodec
+ ", sourceFormat=" + sourceFormat + ", shotExists=" + shotExists + ", targetShotPath=" + targetShotPath
+ ", targetShotName=" + targetShotName + ", videoExists=" + videoExists + ", targetVideoPath="
+ targetVideoPath + ", targetVideoName=" + targetVideoName + ", soundExists=" + soundExists
+ ", targetSoundPath=" + targetSoundPath + ", targetSoundName=" + targetSoundName + "]";
}
}
核心类
Video.class
package com.videotest.video;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import javax.imageio.ImageIO;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FFmpegLogCallback;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class Video<T> {
private static final Logger logger = LoggerFactory.getLogger(Video.class);
private static final String DEFAULT_VIDEO_EXTNAME = "mp4";
private static final String DEFAULT_FORMAT = "mov,mp4,m4a,3gp,3g2,mj2";
private VideoInfo<T> videoInfo;
private File sourceFile;
private TargetShot targetShot;
private TargetVideo targetVideo;
private TargetSound targetSound;
private Video (T id) {
this.videoInfo = new VideoInfo<T>(id);
}
public static <T> Video<T> getInstance(T id) {
return new Video<T>(id);
}
public Video<T> sourceFile (String sourceAbsolutePath) {
return this.sourceFile(new File(sourceAbsolutePath));
}
public Video<T> sourceFile (String sourceParentPath, String sourceName) {
return this.sourceFile(new File(sourceParentPath, sourceName));
}
public Video<T> sourceFile (File sourceFile) {
this.sourceFile = sourceFile;
return this;
}
/**
* 开启截图功能
* @return
*/
public Video<T> enableShot () {
this.targetShot = new TargetShot();
return this;
}
public Video<T> enableShot (TargetShot targetShot) {
this.targetShot = targetShot;
return this;
}
/**
* 开启转码功能
* @return
*/
public Video<T> enableVideo () {
this.targetVideo = new TargetVideo();
return this;
}
public Video<T> enableVideo (TargetVideo targetVideo) {
this.targetVideo = targetVideo;
return this;
}
/**
* 开启提取音频功能
* @return
*/
public Video<T> enableSound () {
this.targetSound = new TargetSound();
return this;
}
public Video<T> enableSound (TargetSound targetSound) {
this.targetSound = targetSound;
return this;
}
/**
* 开始转码截图
* @return
*/
public VideoInfo<T> start () {
avutil.av_log_set_level(avutil.AV_LOG_INFO);
FFmpegLogCallback.set();
VideoInfo<T> videoInfo = this.videoInfo;
File sourceFile = this.sourceFile;
TargetVideo targetVideo = this.targetVideo;
TargetShot targetShot = this.targetShot;
TargetSound targetSound = this.targetSound;
FFmpegFrameGrabber frameGrabber = null;
try {
videoInfo.setSourceExists(sourceFile.exists());
videoInfo.setSourceIsFile(sourceFile.isFile());
videoInfo.setSourceAbsolutePath(sourceFile.getAbsolutePath().replace("\\", "/"));
videoInfo.setSourceParentPath(sourceFile.getParent().replace("\\", "/"));
if (targetVideo != null && targetVideo.getTargetParentPath() == null) {
targetVideo.setTargetParentPath(videoInfo.getSourceParentPath());
}
if (targetShot != null && targetShot.getTargetParentPath() == null) {
targetShot.setTargetParentPath(videoInfo.getSourceParentPath());
}
if (targetSound != null && targetSound.getTargetParentPath() == null) {
targetSound.setTargetParentPath(videoInfo.getSourceParentPath());
}
videoInfo.setSourceName(sourceFile.getName());
if (!sourceFile.isFile()) {
return videoInfo;
}
int dotLastIndex = videoInfo.getSourceName().lastIndexOf(".");
if (dotLastIndex > 0 && dotLastIndex < videoInfo.getSourceName().length() - 1) {
videoInfo.setSourceSimpleName(videoInfo.getSourceName().substring(0, dotLastIndex));
videoInfo.setSourceExtName(videoInfo.getSourceName().substring(dotLastIndex + 1));
} else {
videoInfo.setSourceSimpleName(videoInfo.getSourceName());
}
frameGrabber = FFmpegFrameGrabber.createDefault(sourceFile);
frameGrabber.start();
videoInfo.setSourceVideoCodec(frameGrabber.getVideoCodec());
videoInfo.setSourceAudioCodec(frameGrabber.getAudioCodec());
videoInfo.setSourceFormat(frameGrabber.getFormat());
if (!this.isVideo()) {
throw new org.bytedeco.javacv.FrameGrabber.Exception("[" + videoInfo.getSourceAbsolutePath() + "] 不是视频文件");
}
if (targetShot != null) {
this.shot(frameGrabber);
}
} catch (Exception e) {
logger.warn(sourceFile.getAbsolutePath() + " -> error", e);
} finally {
if (frameGrabber != null) {
try {
frameGrabber.close();
} catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
logger.warn("frameGrabber.close异常", e);
}
frameGrabber = null;
}
}
if (targetVideo != null) {
this.convert2Mp4();
}
if (targetSound != null && this.hasAudio()) {
this.extractSound();
}
return videoInfo;
}
private void shot (FFmpegFrameGrabber frameGrabber) throws Exception {
TargetShot targetShot = this.targetShot;
VideoInfo<T> videoInfo = this.videoInfo;
Frame frame = null;
int i = 0;
int fps = (int) frameGrabber.getFrameRate();
int lengthInFrames = frameGrabber.getLengthInFrames();
int frameNum = targetShot.getFrameNum();
BufferedImage buffer = null;
while (i < lengthInFrames) {
frame = frameGrabber.grabImage();
if (frameNum <= 1
|| i >= fps && i % (fps * frameNum) == 0
|| i == (lengthInFrames - 1) / fps * fps) {
Java2DFrameConverter converter = new Java2DFrameConverter();
buffer = converter.getBufferedImage(frame);
File file = new File(targetShot.getTargetParentPath(), targetShot.getPrefix() + videoInfo.getSourceSimpleName() + targetShot.getSuffix() + "." + targetShot.getExtName());
Files.deleteIfExists(file.toPath());
ImageIO.write(buffer, targetShot.getExtName(), file);
videoInfo.setShotExists(true);
videoInfo.setTargetShotPath(file.getAbsolutePath().replace("\\", "/"));
videoInfo.setTargetShotName(file.getName());
logger.info(videoInfo.getTargetShotPath() + " -> 截图完成");
break;
}
i++;
}
frameGrabber.stop();
}
private void convert2Mp4 () {
VideoInfo<T> videoInfo = this.videoInfo;
TargetVideo targetVideo = this.targetVideo;
File sourceFile = this.sourceFile;
FFmpegFrameGrabber frameGrabber = null;
FFmpegFrameRecorder recorder = null;
try {
File videoOutFile = new File(targetVideo.getTargetParentPath());
if (videoOutFile.isFile()) {
throw new FileAlreadyExistsException("[" + targetVideo.getTargetParentPath() + "] 是文件并且已存在");
}
if (!videoOutFile.exists()) {
videoOutFile.mkdirs();
}
File file = new File(targetVideo.getTargetParentPath(), targetVideo.getPrefix() + videoInfo.getSourceSimpleName() + targetVideo.getSuffix() + "." + DEFAULT_VIDEO_EXTNAME);
String videoOutFilePath = file.getAbsolutePath().replace("\\", "/");
if (this.canPlayInH5IgnoreExtName() && (!targetVideo.isCloseSound() || !this.hasAudio())) {
Files.copy(new File(videoInfo.getSourceParentPath(), videoInfo.getSourceName()).toPath(), new File(videoOutFilePath).toPath(), StandardCopyOption.REPLACE_EXISTING);
} else {
frameGrabber = FFmpegFrameGrabber.createDefault(sourceFile);
frameGrabber.start();
Frame captured_frame = null;
recorder = new FFmpegFrameRecorder(videoOutFilePath, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat(DEFAULT_VIDEO_EXTNAME);
recorder.setFrameRate(frameGrabber.getFrameRate());
int bitrate = Long.valueOf(frameGrabber.getFormatContext().bit_rate()).intValue();
if (bitrate > 0) {
int videoBitrate = frameGrabber.getVideoBitrate();
int audioBitrate = frameGrabber.getAudioBitrate();
if (audioBitrate < 1) {
recorder.setVideoBitrate(bitrate);
} else if (videoBitrate < 1) {
recorder.setAudioBitrate(bitrate);
} else {
double rate = (videoBitrate + audioBitrate) * 1.0 / bitrate;
if (rate > 0.95 && rate < 1.05) {
recorder.setVideoBitrate(videoBitrate);
recorder.setAudioBitrate(audioBitrate);
} else {
recorder.setVideoBitrate(Math.round(bitrate * 1.0f * videoBitrate / (videoBitrate + audioBitrate)));
recorder.setAudioBitrate(Math.round(bitrate * 1.0f * audioBitrate / (videoBitrate + audioBitrate)));
}
}
}
recorder.setSampleRate(frameGrabber.getSampleRate());
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.start();
if (targetVideo.isCloseSound()) {
while (true) {
captured_frame = frameGrabber.grabImage();
if (captured_frame == null) {
logger.info(videoOutFilePath + " 转码完成");
break;
}
if (frameGrabber.getTimestamp() > -1) {
recorder.setTimestamp(frameGrabber.getTimestamp());
}
recorder.record(captured_frame);
}
} else {
while (true) {
captured_frame = frameGrabber.grabFrame();
if (captured_frame == null) {
logger.info(videoOutFilePath + " 转码完成");
break;
}
if (frameGrabber.getTimestamp() > -1) {
recorder.setTimestamp(frameGrabber.getTimestamp());
}
recorder.record(captured_frame);
}
}
}
videoInfo.setVideoExists(true);
videoInfo.setTargetVideoPath(videoOutFilePath);
videoInfo.setTargetVideoName(file.getName());
} catch (Exception e) {
logger.warn(sourceFile.getAbsolutePath() + " -> 转码 error", e);
} finally {
if (recorder != null) {
try {
recorder.close();
} catch (Exception e) {
logger.warn("recorder.close异常", e);
}
}
if (frameGrabber != null) {
try {
frameGrabber.close();
} catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
logger.warn("frameGrabber.close异常", e);
}
}
}
}
private void extractSound() {
VideoInfo<T> videoInfo = this.videoInfo;
TargetSound targetSound = this.targetSound;
File sourceFile = this.sourceFile;
FFmpegFrameGrabber frameGrabber = null;
FFmpegFrameRecorder recorder = null;
try {
File soundOutFile = new File(targetSound.getTargetParentPath());
if (soundOutFile.isFile()) {
throw new FileAlreadyExistsException("[" + targetSound.getTargetParentPath() + "] 是文件并且已存在");
}
if (!soundOutFile.exists()) {
soundOutFile.mkdirs();
}
File file = new File(targetSound.getTargetParentPath(), targetSound.getPrefix() + videoInfo.getSourceSimpleName() + targetSound.getSuffix() + "." + targetSound.getExtName());
String soundOutFilePath = file.getAbsolutePath().replace("\\", "/");
frameGrabber = FFmpegFrameGrabber.createDefault(sourceFile);
frameGrabber.start();
Frame captured_frame = null;
recorder = new FFmpegFrameRecorder(soundOutFilePath, frameGrabber.getAudioChannels());
recorder.setFormat(targetSound.getExtName());
recorder.setSampleRate(frameGrabber.getSampleRate());
int bitrate = Long.valueOf(frameGrabber.getFormatContext().bit_rate()).intValue();
if (bitrate > 0) {
int videoBitrate = frameGrabber.getVideoBitrate();
int audioBitrate = frameGrabber.getAudioBitrate();
if (audioBitrate > 0) {
if (videoBitrate < 1) {
recorder.setAudioBitrate(bitrate);
} else {
double rate = (videoBitrate + audioBitrate) * 1.0 / bitrate;
if (rate > 0.95 && rate < 1.05) {
recorder.setAudioBitrate(audioBitrate);
} else {
recorder.setAudioBitrate(Math.round(bitrate * 1.0f * audioBitrate / (videoBitrate + audioBitrate)));
}
}
}
}
recorder.start();
while (true) {
captured_frame = frameGrabber.grabSamples();
if (captured_frame == null) {
logger.info(soundOutFilePath + " 音频提取完成");
break;
}
if (frameGrabber.getTimestamp() > -1) {
recorder.setTimestamp(frameGrabber.getTimestamp());
}
recorder.recordSamples(captured_frame.samples);
}
videoInfo.setTargetSoundPath(soundOutFilePath);
videoInfo.setTargetSoundName(file.getName());
videoInfo.setSoundExists(true);
} catch (Exception e) {
logger.warn(sourceFile.getAbsolutePath() + " -> 音频提取 error", e);
} finally {
if (recorder != null) {
try {
recorder.close();
} catch (Exception e) {
logger.warn("recorder.close异常", e);
}
}
if (frameGrabber != null) {
try {
frameGrabber.close();
} catch (org.bytedeco.javacv.FrameGrabber.Exception e) {
logger.warn("frameGrabber.close异常", e);
}
}
}
}
private boolean isVideo () {
return this.videoInfo.getSourceVideoCodec() != avcodec.AV_CODEC_ID_NONE;
}
private boolean hasAudio () {
return this.videoInfo.getSourceAudioCodec() != avcodec.AV_CODEC_ID_NONE;
}
private boolean canPlayInH5IgnoreExtName () {
if (!this.isVideo()) {
return false;
}
VideoInfo<T> videoInfo = this.videoInfo;
if (this.hasAudio() && videoInfo.getSourceAudioCodec() != avcodec.AV_CODEC_ID_AAC) {
return false;
}
if (videoInfo.getSourceVideoCodec() != avcodec.AV_CODEC_ID_H264) {
return false;
}
if (!DEFAULT_FORMAT.equals(videoInfo.getSourceFormat())) {
return false;
}
return true;
}
}
怎么使用
package com.videotest;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.videotest.video.TargetShot;
import com.videotest.video.TargetSound;
import com.videotest.video.TargetVideo;
import com.videotest.video.Video;
import com.videotest.video.VideoInfo;
public class Test {
private static final Logger logger = LoggerFactory.getLogger(Test.class);
public static void main(String[] args) throws JsonProcessingException {
File file = new File("D:/data");
File[] listFiles = file.listFiles();
for (File file2 : listFiles) {
VideoInfo<Long> videoInfo = Video
.getInstance(4622L)
// .getInstance("234567765")
// .sourceFile("D:/案例视频/测试android", "同业36-安卓.mp4")
// .sourceFile("D:/1607589556364qq.mp4")
.sourceFile(file2)
.enableShot(null)
// .enableShot(new TargetShot())
// .enableShot(new TargetShot().setExtName("png").setPrefix("tmp_").setTargetParentPath("D:/data"))
.enableVideo(null)
// .enableVideo(new TargetVideo())
// .enableVideo(new TargetVideo().setCloseSound(true)) //关闭音频
.enableSound()
// .enableSound(new TargetSound().setExtName("mp3").setPrefix("tmp_").setSuffix("").setTargetParentPath("D:/data"))
.start();
String writeValueAsString = new ObjectMapper().writeValueAsString(videoInfo);
logger.info(writeValueAsString);
}
}
}