【效果图】
【功能】
1、当窗口获取焦点后,按空格暂停/播放
2、原速播放键、暂停/播放键、倍速播放键(最高3倍速,每次递增0.5)
3、进度条可点击选择播放位置
4、时间显示、播放速度显示
【项目与工具】
maven、java8、vlc-3.8.0、log4j-1.2.17
【代码】
Window.java
import java.awt.*;
import java.awt.event.*;
import java.util.Comparator;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
import utils.FileUtils;
public class Window extends JFrame {
// private final Logger logger = LoggerFactory.getLogger(Window.class);
private static final int PROGRESS_HEIGHT = 10;
private static final int PROGRESS_MIN_VALUE = 0;
private static final int PROGRESS_MAX_VALUE = 100;
// 总时间
private static String TOTAL_TIME;
// 播放速度
private float speed;
// 播放器组件
private EmbeddedMediaPlayerComponent mediaPlayerComponent;
// 进度条
private JProgressBar progress;
// 暂停按钮
private Button pauseButton;
// 显示播放速度的按钮
private Label displaySpeed;
// 显示时间
private Label displayTime;
// 进度定时器
private Timer progressTimer;
// 继续播放定时器
private Timer continueTimer;
// 所有视频路径
private java.util.List videos;
// 当前播放视频的位置
private int videoIndex;
public Window(String videoFolder) {
initVideoFilesPath(videoFolder);
// 设置默认速度为原速
speed = 1.0f;
// 设置窗口标题
setTitle("VideoPlayer");
// 设置窗口焦点监听事件:窗口打开时、窗口获得焦点时设置默认焦点为暂停按钮
this.addWindowFocusListener(getWindowFocusListener());
// 窗口关闭事件:释放资源并退出程序
addWindowListener(closeWindowReleaseMedia());
// 设置默认窗口关闭事件
// setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 设置窗口位置
setBounds(100, 100, 800, 600);
// 最大化显示窗口
// setExtendedState(JFrame.MAXIMIZED_BOTH);
// 主面板
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
// ======播放面板======
JPanel player = new JPanel();
contentPane.add(player, BorderLayout.CENTER);
contentPane.add(player);
player.setLayout(new BorderLayout(0, 0));
// 创建播放器组件并添加到容器中去
mediaPlayerComponent = new EmbeddedMediaPlayerComponent();
player.add(mediaPlayerComponent);
// 视频表面焦点监听:表面获得焦点时设置默认焦点为暂停按钮
getVideoSurface().addFocusListener(videoSurfaceFocusAction());
// getMediaPlayer().setRepeat(true); // 重复播放
// ======底部面板======
JPanel bottomPanel = new JPanel();
BoxLayout boxLayout = new BoxLayout(bottomPanel, BoxLayout.Y_AXIS);
bottomPanel.setLayout(boxLayout);
contentPane.add(bottomPanel, BorderLayout.SOUTH);
// ------进度条组件面板------
JPanel progressPanel = new JPanel();
progress = new JProgressBar();
progress.setMinimum(PROGRESS_MIN_VALUE);
progress.setMaximum(PROGRESS_MAX_VALUE);
progress.setPreferredSize(getNewDimension());
// 设置进度条中间显示进度百分比
progress.setStringPainted(false);
// 进度条进度的颜色
progress.setForeground(new Color(46, 145, 228));
// 进度条背景的颜色
progress.setBackground(new Color(220, 220, 220));
// 点击进度条调整视频播放指针
progress.addMouseListener(setVideoPlayPoint());
// 定时器
progressTimer = getProgressTimer();
progressTimer.start();
progressPanel.add(progress);
progressPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
bottomPanel.add(progressPanel);
// contentPane.add(progressPanel, BorderLayout.SOUTH);
// ------按钮组件面板------
JPanel buttonPanel = new JPanel();
// contentPane.add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
bottomPanel.add(buttonPanel);
displayTime = new Label();
displayTime.setText(getTimeString());
buttonPanel.add(displayTime);
// 重置按钮:设置播放速度为原速
Button resetButton = new Button("reset");
resetButton.setFocusable(false);
resetButton.addMouseListener(mouseClickedResetSpeed());
buttonPanel.add(resetButton);
// 暂停/播放按钮
pauseButton = new Button("pause");
pauseButton.addKeyListener(spaceKeyPressMediaPause());
pauseButton.addMouseListener(mouseClickedMediaPause());
buttonPanel.add(pauseButton);
// 倍速播放按钮:每次递增0.5,最大为3倍速
Button fastForwardButton = new Button(">>>");
fastForwardButton.setFocusable(false);
fastForwardButton.addMouseListener(mouseClickedFastForward());
buttonPanel.add(fastForwardButton);
// 播放速度显示按钮
displaySpeed = new Label();
displaySpeed.setText("x" + speed);
displaySpeed.setFocusable(false);
displaySpeed.setEnabled(false);
buttonPanel.add(displaySpeed);
// 监听窗口大小,设置进度条宽度为窗口宽度(但是对于最大化和还原窗口无效,原因未知<-_->)
this.addComponentListener(windowResizedResetProgressWidth());
// 监听窗口最大化和还原,设置进度条宽度为窗口宽度
this.addWindowStateListener(windowStateChangedResetProgressWidth());
continueTimer = getContinueTimer();
continueTimer.start();
}
private void initVideoFilesPath(String videoFolder) {
videos = FileUtils.readFilePath(videoFolder);
videos.sort(Comparator.naturalOrder());
videoIndex = 0;
// System.out.println(videos);
}
public void play() {
getMediaPlayer().playMedia(videos.get(videoIndex));
setWindowTitle();
// System.out.println("play video..." + getMediaPlayer().getMediaMeta().getTitle());
}
private void setWindowTitle() {
String title = getMediaPlayer().getMediaMeta().getTitle();
setTitle("VideoPlayer-" + title);
}
private String getTimeString(long curr, long total) {
return formatSecond2Time(curr) + " / " + formatSecond2Time(total);
}
private String getTimeString() {
setTotalTime();
return formatSecond2Time(getMediaPlayer().getTime()) + " / " + TOTAL_TIME;
}
private void setTotalTime() {
if (TOTAL_TIME == null) {
long totalSecond = getMediaPlayer().getLength();
TOTAL_TIME = formatSecond2Time(totalSecond);
}
}
private String formatSecond2Time(long milliseconds) {
int second = (int) (milliseconds / 1000);
int h = second / 3600;
int m = (second % 3600) / 60;
int s = (second % 3600) % 60;
return String.format("%02d", h) + ":" + String.format("%02d", m) + ":"
+ String.format("%02d", s);
}
private Timer getContinueTimer() {
return new Timer(1000, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
long total = getMediaPlayer().getLength();
long curr = getMediaPlayer().getTime();
if (curr == total) {
videoIndex++;
if (videoIndex >= videos.size()) {
continueTimer.stop();
System.out.println("all videos finished...");
return;
}
getMediaPlayer().playMedia(videos.get(videoIndex));
setWindowTitle();
setProgress(getMediaPlayer().getTime(), getMediaPlayer().getLength());
progressTimer.restart();
}
}
});
}
private Timer getProgressTimer() {
return new Timer(1000, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (getProgress().getValue() >= PROGRESS_MAX_VALUE) {
// 结束定时器
progressTimer.stop();
return;
}
// 设置进度值
long total = getMediaPlayer().getLength();
long curr = getMediaPlayer().getTime();
setProgress(curr, total);
}
});
}
private void setProgress(long curr, long total) {
float percent = (float) curr / total;
int value = (int) (percent * 100);
getProgress().setValue(value);
displayTime.setText(getTimeString(curr, total));
}
private WindowAdapter windowStateChangedResetProgressWidth() {
return new WindowAdapter() {
@Override
public void windowStateChanged(WindowEvent state) {
// state=1或7为最小化,此处不处理
if (state.getNewState() == 0) {
// System.out.println("窗口恢复到初始状态");
setProgressWidthAutoAdaptWindow();
} else if (state.getNewState() == 6) {
// System.out.println("窗口最大化");
setProgressWidthAutoAdaptWindow();
}
}
};
}
private void setProgressWidthAutoAdaptWindow() {
getProgress().setPreferredSize(getNewDimension());
}
private ComponentAdapter windowResizedResetProgressWidth() {
return new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
setProgressWidthAutoAdaptWindow();
}
};
}
private Dimension getNewDimension() {
return new Dimension(getWidth(), PROGRESS_HEIGHT);
}
private MouseAdapter setVideoPlayPoint() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
long total = getMediaPlayer().getLength();
long time = (long) ((float) x / progress.getWidth() * total);
setProgress(time, total);
getMediaPlayer().setTime(time);
}
};
}
private FocusAdapter videoSurfaceFocusAction() {
return new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
setPauseButtonAsDefaultFocus();
}
};
}
private WindowAdapter closeWindowReleaseMedia() {
return new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
getMediaPlayer().stop();
getMediaPlayer().release();
System.exit(0);
}
};
}
private MouseListener mouseClickedResetSpeed() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (speed == 1.0f) {
return;
}
speed = 1.0f;
getMediaPlayer().setRate(speed);
displaySpeed.setText("x" + speed);
}
};
}
private MouseListener mouseClickedFastForward() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (speed >= 3.0f) {
speed = 1.0f;
} else {
speed += 0.5f;
}
getMediaPlayer().setRate(speed);
displaySpeed.setText("x" + speed);
}
};
}
private MouseAdapter mouseClickedMediaPause() {
return new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
getMediaPlayer().pause();
if (progressTimer.isRunning()) {
progressTimer.stop();
} else {
progressTimer.restart();
}
}
};
}
private WindowFocusListener getWindowFocusListener() {
return new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
setPauseButtonAsDefaultFocus();
}
@Override
public void windowGainedFocus(WindowEvent e) {
setPauseButtonAsDefaultFocus();
}
@Override
public void windowLostFocus(WindowEvent e) {}
};
}
private void setPauseButtonAsDefaultFocus() {
pauseButton.requestFocus();
}
private KeyListener spaceKeyPressMediaPause() {
return new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
getMediaPlayer().pause();
}
}
};
}
private JProgressBar getProgress() {
return progress;
}
private EmbeddedMediaPlayer getMediaPlayer() {
return mediaPlayerComponent.getMediaPlayer();
}
private Canvas getVideoSurface() {
return mediaPlayerComponent.getVideoSurface();
}
}
Main.java
import org.apache.log4j.BasicConfigurator;
import com.sun.jna.NativeLibrary;
import uk.co.caprica.vlcj.runtime.RuntimeUtil;
public class Main {
public static void main(String[] args) {
// 解决日志报错问题
BasicConfigurator.configure();
// 关闭日志
// Logger.getRootLogger().shutdown();
// 加载dll
NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), "vlc");
// 方式一:直接传文件夹
String videoFolder = "E:\\tmp\\test\\videoTest";
Window frame = new Window(videoFolder);
frame.setVisible(true);
frame.play();
// 方式二:传文件
// String filePath = "E:\\tmp\\test\\videoTest\\01_编程语言运行机制.avi";
// EmbeddedMediaPlayer mediaPlayer = frame.getMediaPlayer();
// mediaPlayer.playMedia(filePath);
}
}
FileUtils.java
package utils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Stack;
public class FileUtils {
public static List readFilePath(String folderPath) {
List list = new ArrayList<>();
File folder = new File(folderPath);
if (!folder.isDirectory()) {
return list;
}
File[] files = folder.listFiles();
if (Objects.isNull(files) || files.length <= 0) {
return list;
}
for (File file : files) {
if (!file.isFile()) {
continue;
}
list.add(file.getAbsolutePath());
}
return list;
}
public static List readAllFilePath(String folderPath) {
Stack stack = new Stack<>();
List list = new ArrayList<>();
File folder = new File(folderPath);
if (!folder.isDirectory()) {
return list;
}
stack.push(folder);
while (!stack.isEmpty()) {
File tmpFile = stack.pop();
File[] subFiles = tmpFile.listFiles();
if (subFiles == null) {
continue;
}
for (File subFile : subFiles) {
if (subFile.isDirectory()) {
stack.push(subFile);
} else {
list.add(subFile.getAbsolutePath());
}
}
}
return list;
}
}
依赖
uk.co.caprica
vlcj
3.8.0
org.slf4j
slf4j-api
1.8.0-beta0
org.slf4j
slf4j-log4j12
1.8.0-beta0
log4j
log4j
1.2.17
vlc
vlc安装目录下的:
libvlc.dll文件
libvlccore.dll文件
plugins文件夹
官网地址:https://www.videolan.org/vlc/
【项目结构】
【参考链接】
1、Java利用VLC开发简易的视频播放器-单向箭头
https://www.cnblogs.com/SnailsRunning/p/10251912.html
2、Java利用VLC开发简易视屏播放器-冷月枫寂
https://www.cnblogs.com/deng-c-q/p/7510418.html
3、使用java创建一个简易的视屏播放器-南柯一梦x
https://blog.csdn.net/nankeyimengxihe/article/details/50419449
4、Java Swing 图形界面开发(目录)-xietansheng
https://blog.csdn.net/xietansheng/article/details/72814492
5、Java制作进度条-晨曦之光Wing
https://blog.csdn.net/qq_36533690/article/details/106448207
6、MediaPlayerForJava(创建一个视频播放器)-FeiHuang
https://www.jianshu.com/p/2c61e3c6aa70
7、SLF4J: No SLF4J providers were found.-香山上的麻雀
https://www.jianshu.com/p/41e0a730347f
8、vlc官方文档
http://javadox.com/uk.co.caprica/vlcj/3.0.1/uk/co/caprica/vlcj/player/MediaPlayer.html
【说明】
没事做着玩一玩,其实也可以用一用,不过vlc会报几个错(好像也不会影响使用),目前暂时还没找到解决方法<-_->。错误比如这个[00000000222fd390] direct3d11 vout display error: SetThumbNailClip failed: 0x800706f4
【GitHub】
https://github.com/cxzgwing/simple-video-player
【后记】
2020.06.09
GitHub中的项目,截止到2020.06.09,简易播放器新增以下功能(共12个commits):
1、声音控制模块:音量百分比显示、点击音量进度条设置音量、鼠标轮滑滚动设置音量
2、选择文件按钮:支持多选文件(暂未判断文件类型)
3、查看播放文件列表(半透明效果):列表中文件名支持换行显示
4、播放/暂停按钮文案动态显示
5、升级了log4j版本,1.2.17升级到2.13.3
(最新版本与本文展示的源码版本差距有一丢丢大,嘻嘻(#^.^#),播放模式也改了,之前是写好路径,运行窗口就播放,最新版本是选择文件后点击播放按钮或按空格才播放)