This commit is contained in:
parent
7738b2fdb2
commit
468d60c3c3
|
@ -29,7 +29,7 @@ public class VideoInferenceApp extends JFrame {
|
||||||
|
|
||||||
public VideoInferenceApp() {
|
public VideoInferenceApp() {
|
||||||
// 设置窗口标题
|
// 设置窗口标题
|
||||||
super("ONNX Inference Application");
|
super("V:sulv0302");
|
||||||
// 初始化UI组件
|
// 初始化UI组件
|
||||||
initializeUI();
|
initializeUI();
|
||||||
}
|
}
|
||||||
|
@ -114,8 +114,8 @@ public class VideoInferenceApp extends JFrame {
|
||||||
JButton playButton = new JButton("播放");
|
JButton playButton = new JButton("播放");
|
||||||
JButton pauseButton = new JButton("暂停");
|
JButton pauseButton = new JButton("暂停");
|
||||||
JButton replayButton = new JButton("重播");
|
JButton replayButton = new JButton("重播");
|
||||||
JButton fastForward5sButton = new JButton("快进1秒");
|
JButton fastForward5sButton = new JButton("快进3秒");
|
||||||
JButton rewind5sButton = new JButton("后退5秒");
|
JButton rewind5sButton = new JButton("后退3秒");
|
||||||
|
|
||||||
// 设置按钮的统一高度
|
// 设置按钮的统一高度
|
||||||
Dimension buttonSize = new Dimension(100, 30);
|
Dimension buttonSize = new Dimension(100, 30);
|
||||||
|
@ -226,7 +226,7 @@ public class VideoInferenceApp extends JFrame {
|
||||||
// 后退5秒
|
// 后退5秒
|
||||||
rewind5sButton.addActionListener(e -> {
|
rewind5sButton.addActionListener(e -> {
|
||||||
try {
|
try {
|
||||||
videoPlayer.rewind(5000); // 后退5000毫秒(5秒)
|
videoPlayer.rewind(3000); // 后退5000毫秒(5秒)
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
JOptionPane.showMessageDialog(this, "后退失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(this, "后退失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
||||||
|
@ -236,7 +236,7 @@ public class VideoInferenceApp extends JFrame {
|
||||||
// 快进5秒
|
// 快进5秒
|
||||||
fastForward5sButton.addActionListener(e -> {
|
fastForward5sButton.addActionListener(e -> {
|
||||||
try {
|
try {
|
||||||
videoPlayer.fastForward(1000); // 快进5000毫秒(5秒)
|
videoPlayer.fastForward(3000); // 快进5000毫秒(5秒)
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
JOptionPane.showMessageDialog(this, "快进失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(this, "快进失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
||||||
|
|
|
@ -20,16 +20,22 @@ import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static com.ly.onnx.utils.ImageUtils.matToBufferedImage;
|
import static com.ly.onnx.utils.ImageUtils.matToBufferedImage;
|
||||||
|
|
||||||
public class VideoPlayer {
|
public class VideoPlayer {
|
||||||
|
private static final Logger logger = Logger.getLogger(VideoPlayer.class.getName());
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// 加载 OpenCV 库
|
// 加载 OpenCV 库
|
||||||
nu.pattern.OpenCV.loadLocally();
|
nu.pattern.OpenCV.loadLocally();
|
||||||
String OS = System.getProperty("os.name").toLowerCase();
|
String OS = System.getProperty("os.name").toLowerCase();
|
||||||
if (OS.contains("win")) {
|
if (OS.contains("win")) {
|
||||||
|
// 使用发布版 FFmpeg DLL
|
||||||
System.load(ClassLoader.getSystemResource("lib/win/opencv_videoio_ffmpeg470_64.dll").getPath());
|
System.load(ClassLoader.getSystemResource("lib/win/opencv_videoio_ffmpeg470_64.dll").getPath());
|
||||||
|
// 如果需要调试版,取消注释以下行
|
||||||
|
// System.load(ClassLoader.getSystemResource("lib/win/opencv_videoio_ffmpeg470_64d.dll").getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,76 +60,117 @@ public class VideoPlayer {
|
||||||
// 定义阻塞队列来缓冲转换后的数据
|
// 定义阻塞队列来缓冲转换后的数据
|
||||||
private BlockingQueue<FrameData> frameDataQueue = new LinkedBlockingQueue<>(10); // 队列容量可根据需要调整
|
private BlockingQueue<FrameData> frameDataQueue = new LinkedBlockingQueue<>(10); // 队列容量可根据需要调整
|
||||||
|
|
||||||
|
// 添加一个锁对象用于同步 VideoCapture 访问
|
||||||
|
private final Object captureLock = new Object();
|
||||||
|
|
||||||
public VideoPlayer(VideoPanel videoPanel, ModelManager modelManager) {
|
public VideoPlayer(VideoPanel videoPanel, ModelManager modelManager) {
|
||||||
this.videoPanel = videoPanel;
|
this.videoPanel = videoPanel;
|
||||||
this.modelManager = modelManager;
|
this.modelManager = modelManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加一个方法来停止播放线程而不释放 VideoCapture
|
||||||
|
public void stopPlayback() {
|
||||||
|
synchronized (captureLock) {
|
||||||
|
isPlaying = false;
|
||||||
|
isPaused = false;
|
||||||
|
|
||||||
|
if (frameReadingThread != null && frameReadingThread.isAlive()) {
|
||||||
|
frameReadingThread.interrupt();
|
||||||
|
try {
|
||||||
|
frameReadingThread.join(); // 等待线程终止
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inferenceThread != null && inferenceThread.isAlive()) {
|
||||||
|
inferenceThread.interrupt();
|
||||||
|
try {
|
||||||
|
inferenceThread.join(); // 等待线程终止
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frameDataQueue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载视频或流
|
// 加载视频或流
|
||||||
public void loadVideo(String videoFilePathOrStreamUrl) throws Exception {
|
public void loadVideo(String videoFilePathOrStreamUrl) throws Exception {
|
||||||
stopVideo();
|
synchronized (captureLock) {
|
||||||
if (videoFilePathOrStreamUrl.equals("0")) {
|
stopVideo(); // 停止任何现有的播放并释放资源
|
||||||
int cameraIndex = Integer.parseInt(videoFilePathOrStreamUrl);
|
|
||||||
videoCapture = new VideoCapture(cameraIndex);
|
|
||||||
if (!videoCapture.isOpened()) {
|
|
||||||
throw new Exception("无法打开摄像头");
|
|
||||||
}
|
|
||||||
videoDuration = 0; // 摄像头没有固定的时长
|
|
||||||
playVideo();
|
|
||||||
} else {
|
|
||||||
// 输入不是数字,尝试打开视频文件
|
|
||||||
videoCapture = new VideoCapture(videoFilePathOrStreamUrl, Videoio.CAP_FFMPEG);
|
|
||||||
if (!videoCapture.isOpened()) {
|
|
||||||
throw new Exception("无法打开视频文件:" + videoFilePathOrStreamUrl);
|
|
||||||
}
|
|
||||||
double frameCount = videoCapture.get(Videoio.CAP_PROP_FRAME_COUNT);
|
|
||||||
double fps = videoCapture.get(Videoio.CAP_PROP_FPS);
|
|
||||||
if (fps <= 0 || Double.isNaN(fps)) {
|
|
||||||
fps = 25; // 默认帧率
|
|
||||||
}
|
|
||||||
videoDuration = (long) (frameCount / fps * 1000); // 转换为毫秒
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示第一帧
|
if (videoFilePathOrStreamUrl.equals("0")) {
|
||||||
Mat frame = new Mat();
|
int cameraIndex = Integer.parseInt(videoFilePathOrStreamUrl);
|
||||||
if (videoCapture.read(frame)) {
|
videoCapture = new VideoCapture(cameraIndex);
|
||||||
BufferedImage bufferedImage = matToBufferedImage(frame);
|
if (!videoCapture.isOpened()) {
|
||||||
videoPanel.updateImage(bufferedImage);
|
throw new Exception("无法打开摄像头");
|
||||||
|
}
|
||||||
|
videoDuration = 0; // 摄像头没有固定的时长
|
||||||
|
playVideo();
|
||||||
|
} else {
|
||||||
|
// 输入不是数字,尝试打开视频文件
|
||||||
|
videoCapture = new VideoCapture(videoFilePathOrStreamUrl, Videoio.CAP_FFMPEG);
|
||||||
|
if (!videoCapture.isOpened()) {
|
||||||
|
throw new Exception("无法打开视频文件:" + videoFilePathOrStreamUrl);
|
||||||
|
}
|
||||||
|
double frameCount = videoCapture.get(Videoio.CAP_PROP_FRAME_COUNT);
|
||||||
|
double fps = videoCapture.get(Videoio.CAP_PROP_FPS);
|
||||||
|
if (fps <= 0 || Double.isNaN(fps)) {
|
||||||
|
fps = 25; // 默认帧率
|
||||||
|
}
|
||||||
|
videoDuration = (long) (frameCount / fps * 1000); // 转换为毫秒
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示第一帧
|
||||||
|
Mat frame = new Mat();
|
||||||
|
boolean frameRead = videoCapture.read(frame);
|
||||||
|
if (frameRead && !frame.empty()) {
|
||||||
|
BufferedImage bufferedImage = matToBufferedImage(frame);
|
||||||
|
videoPanel.updateImage(bufferedImage);
|
||||||
|
currentTimestamp = 0;
|
||||||
|
} else {
|
||||||
|
throw new Exception("无法读取第一帧");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置到视频开始位置
|
||||||
|
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_FRAMES, 0);
|
||||||
|
if (!success) {
|
||||||
|
throw new Exception("无法重置视频到起始位置。");
|
||||||
|
}
|
||||||
currentTimestamp = 0;
|
currentTimestamp = 0;
|
||||||
} else {
|
|
||||||
throw new Exception("无法读取第一帧");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置到视频开始位置
|
|
||||||
videoCapture.set(Videoio.CAP_PROP_POS_FRAMES, 0);
|
|
||||||
currentTimestamp = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 播放
|
// 播放视频
|
||||||
public void playVideo() {
|
public void playVideo() {
|
||||||
if (videoCapture == null || !videoCapture.isOpened()) {
|
synchronized (captureLock) {
|
||||||
JOptionPane.showMessageDialog(null, "请先加载视频文件或流。", "提示", JOptionPane.WARNING_MESSAGE);
|
if (videoCapture == null || !videoCapture.isOpened()) {
|
||||||
return;
|
JOptionPane.showMessageDialog(null, "请先加载视频文件或流。", "提示", JOptionPane.WARNING_MESSAGE);
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (isPlaying) {
|
|
||||||
if (isPaused) {
|
|
||||||
isPaused = false; // 恢复播放
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
if (isPlaying) {
|
||||||
|
if (isPaused) {
|
||||||
|
isPaused = false; // 恢复播放
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPlaying = true;
|
||||||
|
isPaused = false;
|
||||||
|
|
||||||
|
frameDataQueue.clear(); // 开始播放前清空队列
|
||||||
}
|
}
|
||||||
|
|
||||||
isPlaying = true;
|
|
||||||
isPaused = false;
|
|
||||||
|
|
||||||
frameDataQueue.clear(); // 开始播放前清空队列
|
|
||||||
|
|
||||||
// 创建并启动帧读取和转换线程
|
// 创建并启动帧读取和转换线程
|
||||||
frameReadingThread = new Thread(() -> {
|
frameReadingThread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
double fps = videoCapture.get(Videoio.CAP_PROP_FPS);
|
double fps;
|
||||||
|
synchronized (captureLock) {
|
||||||
|
fps = videoCapture.get(Videoio.CAP_PROP_FPS);
|
||||||
|
}
|
||||||
if (fps <= 0 || Double.isNaN(fps)) {
|
if (fps <= 0 || Double.isNaN(fps)) {
|
||||||
fps = 25; // 默认帧率
|
fps = 25; // 默认帧率
|
||||||
}
|
}
|
||||||
|
@ -137,7 +184,11 @@ public class VideoPlayer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Mat frame = new Mat();
|
Mat frame = new Mat();
|
||||||
if (!videoCapture.read(frame) || frame.empty()) {
|
boolean frameRead;
|
||||||
|
synchronized (captureLock) {
|
||||||
|
frameRead = videoCapture.read(frame);
|
||||||
|
}
|
||||||
|
if (!frameRead || frame.empty()) {
|
||||||
isPlaying = false;
|
isPlaying = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +199,9 @@ public class VideoPlayer {
|
||||||
FrameData frameData = new FrameData(bufferedImage, stringObjectMap);
|
FrameData frameData = new FrameData(bufferedImage, stringObjectMap);
|
||||||
frameDataQueue.put(frameData); // 阻塞,如果队列已满
|
frameDataQueue.put(frameData); // 阻塞,如果队列已满
|
||||||
// 控制帧率
|
// 控制帧率
|
||||||
currentTimestamp = (long) videoCapture.get(Videoio.CAP_PROP_POS_MSEC);
|
synchronized (captureLock) {
|
||||||
|
currentTimestamp = (long) (videoCapture.get(Videoio.CAP_PROP_POS_FRAMES) * 1000 / fps);
|
||||||
|
}
|
||||||
// 控制播放速度
|
// 控制播放速度
|
||||||
long processingTime = System.currentTimeMillis() - startTime;
|
long processingTime = System.currentTimeMillis() - startTime;
|
||||||
long sleepTime = frameDelay - processingTime;
|
long sleepTime = frameDelay - processingTime;
|
||||||
|
@ -214,12 +267,82 @@ public class VideoPlayer {
|
||||||
inferenceThread.start();
|
inferenceThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加重播方法
|
||||||
|
public void replayVideo() throws Exception {
|
||||||
|
synchronized (captureLock) {
|
||||||
|
if (videoCapture != null && videoCapture.isOpened()) {
|
||||||
|
stopPlayback(); // 停止现有的播放线程
|
||||||
|
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_FRAMES, 0); // 重置帧位置到起始
|
||||||
|
if (!success) {
|
||||||
|
throw new Exception("无法重置视频到起始位置。");
|
||||||
|
}
|
||||||
|
currentTimestamp = 0;
|
||||||
|
logger.info("视频已重播到起始位置。");
|
||||||
|
playVideo(); // 重新开始播放
|
||||||
|
} else {
|
||||||
|
throw new Exception("视频未加载或未打开。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 rewind 和 fastForward 方法,使用帧索引而非毫秒
|
||||||
|
public void rewind(long millis) throws Exception {
|
||||||
|
synchronized (captureLock) {
|
||||||
|
if (videoCapture != null && videoCapture.isOpened()) {
|
||||||
|
double fps = videoCapture.get(Videoio.CAP_PROP_FPS);
|
||||||
|
if (fps <= 0 || Double.isNaN(fps)) {
|
||||||
|
fps = 25; // 默认帧率
|
||||||
|
}
|
||||||
|
long framesToRewind = (long) (millis * fps / 1000);
|
||||||
|
long currentFrame = (long) videoCapture.get(Videoio.CAP_PROP_POS_FRAMES);
|
||||||
|
long newFrame = currentFrame - framesToRewind;
|
||||||
|
if (newFrame < 0) newFrame = 0;
|
||||||
|
logger.info("Rewinding " + millis + " ms (" + framesToRewind + " frames) from frame " + currentFrame + " to frame " + newFrame);
|
||||||
|
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_FRAMES, newFrame);
|
||||||
|
if (!success) {
|
||||||
|
throw new Exception("无法后退视频到指定帧。");
|
||||||
|
}
|
||||||
|
currentTimestamp = (long) (newFrame * 1000 / fps);
|
||||||
|
} else {
|
||||||
|
throw new Exception("视频未加载或未打开。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fastForward(long millis) throws Exception {
|
||||||
|
synchronized (captureLock) {
|
||||||
|
if (videoCapture != null && videoCapture.isOpened()) {
|
||||||
|
double fps = videoCapture.get(Videoio.CAP_PROP_FPS);
|
||||||
|
if (fps <= 0 || Double.isNaN(fps)) {
|
||||||
|
fps = 25; // 默认帧率
|
||||||
|
}
|
||||||
|
long framesToFastForward = (long) (millis * fps / 1000);
|
||||||
|
long currentFrame = (long) videoCapture.get(Videoio.CAP_PROP_POS_FRAMES);
|
||||||
|
long newFrame = currentFrame + framesToFastForward;
|
||||||
|
double frameCount = videoCapture.get(Videoio.CAP_PROP_FRAME_COUNT);
|
||||||
|
if (frameCount > 0 && newFrame > frameCount) {
|
||||||
|
newFrame = (long) frameCount;
|
||||||
|
}
|
||||||
|
logger.info("Fast forwarding " + millis + " ms (" + framesToFastForward + " frames) from frame " + currentFrame + " to frame " + newFrame);
|
||||||
|
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_FRAMES, newFrame);
|
||||||
|
if (!success) {
|
||||||
|
throw new Exception("无法快进视频到指定帧。");
|
||||||
|
}
|
||||||
|
currentTimestamp = (long) (newFrame * 1000 / fps);
|
||||||
|
} else {
|
||||||
|
throw new Exception("视频未加载或未打开。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 暂停视频
|
// 暂停视频
|
||||||
public void pauseVideo() {
|
public void pauseVideo() {
|
||||||
if (!isPlaying) {
|
synchronized (captureLock) {
|
||||||
return;
|
if (!isPlaying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isPaused = true;
|
||||||
}
|
}
|
||||||
isPaused = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置是否启用目标跟踪
|
// 设置是否启用目标跟踪
|
||||||
|
@ -359,23 +482,35 @@ public class VideoPlayer {
|
||||||
|
|
||||||
// 停止视频
|
// 停止视频
|
||||||
public void stopVideo() {
|
public void stopVideo() {
|
||||||
isPlaying = false;
|
synchronized (captureLock) {
|
||||||
isPaused = false;
|
isPlaying = false;
|
||||||
|
isPaused = false;
|
||||||
|
|
||||||
if (frameReadingThread != null && frameReadingThread.isAlive()) {
|
if (frameReadingThread != null && frameReadingThread.isAlive()) {
|
||||||
frameReadingThread.interrupt();
|
frameReadingThread.interrupt();
|
||||||
|
try {
|
||||||
|
frameReadingThread.join(); // 等待线程终止
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inferenceThread != null && inferenceThread.isAlive()) {
|
||||||
|
inferenceThread.interrupt();
|
||||||
|
try {
|
||||||
|
inferenceThread.join(); // 等待线程终止
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCapture != null) {
|
||||||
|
videoCapture.release();
|
||||||
|
videoCapture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
frameDataQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inferenceThread != null && inferenceThread.isAlive()) {
|
|
||||||
inferenceThread.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoCapture != null) {
|
|
||||||
videoCapture.release();
|
|
||||||
videoCapture = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
frameDataQueue.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除推理引擎
|
// 删除推理引擎
|
||||||
|
@ -387,49 +522,6 @@ public class VideoPlayer {
|
||||||
this.inferenceEngines.add(inferenceEngine);
|
this.inferenceEngines.add(inferenceEngine);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加重播方法
|
|
||||||
// 添加重播方法
|
|
||||||
public void replayVideo() throws Exception {
|
|
||||||
if (videoCapture != null && videoCapture.isOpened()) {
|
|
||||||
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_FRAMES, 0);
|
|
||||||
if (!success) {
|
|
||||||
throw new Exception("无法重置视频到起始位置。");
|
|
||||||
}
|
|
||||||
currentTimestamp = 0;
|
|
||||||
} else {
|
|
||||||
throw new Exception("视频未加载或未打开。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加 rewind 和 fastForward 方法
|
|
||||||
public void rewind(long millis) throws Exception {
|
|
||||||
if (videoCapture == null || !videoCapture.isOpened()) {
|
|
||||||
throw new Exception("视频未加载或未打开");
|
|
||||||
}
|
|
||||||
long newTimestamp = currentTimestamp - millis;
|
|
||||||
if (newTimestamp < 0) newTimestamp = 0;
|
|
||||||
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_MSEC, newTimestamp);
|
|
||||||
if (!success) {
|
|
||||||
throw new Exception("无法后退视频。");
|
|
||||||
}
|
|
||||||
currentTimestamp = newTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void fastForward(long millis) throws Exception {
|
|
||||||
if (videoCapture == null || !videoCapture.isOpened()) {
|
|
||||||
throw new Exception("视频未加载或未打开");
|
|
||||||
}
|
|
||||||
long newTimestamp = currentTimestamp + millis;
|
|
||||||
if (videoDuration > 0 && newTimestamp > videoDuration) {
|
|
||||||
newTimestamp = videoDuration;
|
|
||||||
}
|
|
||||||
boolean success = videoCapture.set(Videoio.CAP_PROP_POS_MSEC, newTimestamp);
|
|
||||||
if (!success) {
|
|
||||||
throw new Exception("无法快进视频。");
|
|
||||||
}
|
|
||||||
currentTimestamp = newTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载并处理图片
|
// 加载并处理图片
|
||||||
public void loadImage(String imagePath) throws Exception {
|
public void loadImage(String imagePath) throws Exception {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue