Java音视频播放器
可以实现播放暂停,快进快退,下一曲,打开文件,不循环,单曲循环,列表循环,播放列表和播放历史等功能(可以播放视频)
/*主程序*/
package videoplayer;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.io.File;
import java.util.Vector;
import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JSlider;
import javax.swing.SwingWorker;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.platform.win32.WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION;
import uk.co.caprica.vlcj.binding.LibVlc;
import uk.co.caprica.vlcj.player.embedded.DefaultAdaptiveRuntimeFullScreenStrategy;
import uk.co.caprica.vlcj.runtime.RuntimeUtil;
public class PlayerMain {
static Window frame;
static DefaultListModel dlm= new DefaultListModel();
static DefaultListModel dlm1= new DefaultListModel();
//private static final String NATIVE_LIBRARY_SEARCH_PATH = "D:\\Program Files\\VideoLAN\\VLC\\sdk\\lib";
public static void main(String[] args) {
VideoTime videoTime =new VideoTime() ;
NativeLibrary.addSearchPath(
RuntimeUtil.getLibVlcLibraryName(), "D:\\\\Program Files\\\\VideoLAN\\\\VLC"); //导入的路径是vlc的安装路径
Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(),LibVlc.class);
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try{
frame=new Window();
frame.setVisible(true);
dlm.addElement("F:\\迅雷下载\\大侦探皮卡丘.720p.1080p.HD中英双字\\大侦探皮卡丘.mp4");
dlm.addElement("F:\\CloudMusic\\梦飞船 - 不值得.flac");
dlm.addElement("F:\\CloudMusic\\Crazy Up!.mp3");
dlm.addElement("F:\\\\CloudMusic\\\\Right Now.mp3");
frame.getList().setModel(dlm);
new SwingWorker<String, Integer>() {
@Override
protected String doInBackground() throws Exception {
// TODO Auto-generated method stub
while (true) { //获取视频播放进度并且按百分比显示
long total=frame.getMediaPlayer().getLength();
long curr=frame.getMediaPlayer().getTime();
//System.out.println(total+" "+curr);
videoTime.timeCalculate(total, curr);
String string1=videoTime.getMinitueCurrent() + ":" + videoTime.getSecondCurrent();
String string2=videoTime.getMinitueTotal() + ":" + videoTime.getSecondTotal();
frame.getCurrentLabel().setText(string1);
frame.getTotalLabel().setText(string2);
float percent=(float)curr/total;
publish((int)(percent*100));
Thread.sleep(200);
if(frame.c==2) {
if (curr==-1&&total==-1) {
frame.Next();
}
}else if (frame.c==1) {
if (curr==-1&&total==-1) {
frame.OneCircu();
}
}{
}
}
}
protected void process(java.util.List<Integer> chunks) {
for(int v:chunks){
frame.getProgressBar().setValue(v);
}
}
}.execute();
}catch(Exception e){
e.printStackTrace();
}
}
});
}
//打开文件
public static void openVideo() {
JFileChooser chooser=new JFileChooser();
int v=chooser.showOpenDialog(null);
if(v==JFileChooser.APPROVE_OPTION){
File file=chooser.getSelectedFile();
String playname=file.getName();
dlm.addElement(file.getAbsoluteFile());
frame.getList().setModel(dlm);
frame.getMediaPlayer().playMedia(file.getAbsolutePath());
}
}
//退出播放
public static void exit() {
frame.getMediaPlayer().release();
System.exit(0);
}
//实现播放按钮的方法
public static void play() {
frame.getButton().setText("||");
frame.getMediaPlayer().play();
}
//实现暂停按钮的方法
public static void pause() {
frame.getButton().setText(">");
frame.getMediaPlayer().pause();
}
//实现停止按钮的方法
public static void stop() {
frame.getMediaPlayer().stop();
}
//实现点击进度条跳转的方法
public static void jumpTo(float to) {
frame.getMediaPlayer().setTime((long)(to*frame.getMediaPlayer().getLength()));
}
//实现控制声音的方法
public static void setVol(int v) {
frame.getMediaPlayer().setVolume(v);
String string=String.valueOf(v);
frame.getLabel().setText(string);
}
public static void ListPlay() {
if (frame.getList().getSelectedValue()!=null) {
frame.getplayorpauseButton().setText("||");
int index = frame.getList().getSelectedIndex();
String string=String.valueOf(frame.getList().getModel().getElementAt(index));
frame.getMediaPlayer().playMedia(string);
}
}
}
/*视屏播放器主界面*/
package videoplayer;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSlider;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.sun.jna.platform.win32.NtDll;
import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
import javax.swing.JLabel;
import java.awt.FlowLayout;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListModel;
import java.awt.Color;
import javax.swing.JScrollPane;
public class Window extends JFrame{
private JPanel contentPane; //顶层容器,整个播放页面的容器
private JMenuBar menuBar; //菜单栏
private JMenu mnFile; //文件菜单
private JPanel panel; //控制区域容器
private JPanel controlPanel; //控制按钮容器
private JButton PlayOrPause; //控制按钮,停止、播放、暂停
public JSlider slider; //声音控制块
EmbeddedMediaPlayerComponent playerComponent; //媒体播放器组件
private JProgressBar progress;
private JLabel totalLable , lblNewLabel,voiceLabel;
private JLabel CurrentLable;
private static JList list;
private JButton btnNewButton_1;
private JButton btnNewButton_2;
private JPanel panel_1;
HistoryFrame historyFrame=new HistoryFrame();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
private JScrollPane scrollPane;
boolean bfls=true;
boolean voice=true;
int c=0;
Icon icon=new ImageIcon("C:\\Users\\15050\\Desktop\\音量.png");
Icon icon1=new ImageIcon("C:\\Users\\15050\\Desktop\\静音.png");
Icon icon2=new ImageIcon("C:\\Users\\15050\\Desktop\\不循环1.png");
Icon icon3=new ImageIcon("C:\\Users\\15050\\Desktop\\单曲循环1.png");
Icon icon4=new ImageIcon("C:\\Users\\15050\\Desktop\\列表循环1.png");
Icon icon5=new ImageIcon("C:\\Users\\15050\\Desktop\\图片1.jpg");
public static void main(String[] args) {
}
//MainWindow构造方法,创建视屏播放的主界面
public Window(){
setResizable(false);
setTitle(" VideoPlayer ");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(200,80,1020,674);
contentPane=new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0,0));
setContentPane(contentPane);
/*视频播放窗口中的菜单栏*/
menuBar=new JMenuBar();
setJMenuBar(menuBar);
mnFile=new JMenu("\u6253\u5F00\u6587\u4EF6"); //设置菜单名
mnFile.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent arg0) {
PlayerMain.openVideo();
}
});
menuBar.add(mnFile);
JMenu mnNewMenu = new JMenu("\u9000\u51FA");
mnNewMenu.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
PlayerMain.exit();
}
});
menuBar.add(mnNewMenu);
/*视屏窗口中播放界面部分*/
JPanel videoPane=new JPanel();
contentPane.add(videoPane, BorderLayout.CENTER);
videoPane.setLayout(null);
playerComponent=new EmbeddedMediaPlayerComponent();
playerComponent.setBounds(0, 0, 745, 529);
videoPane.add(playerComponent);
/*视屏窗口中控制部分*/
panel=new JPanel();
panel.setBounds(0, 553, 745, 47);
videoPane.add(panel);
panel.setLayout(null);
controlPanel=new JPanel(); //实例化控制按钮容器
controlPanel.setBounds(112, 5, 535, 37);
panel.add(controlPanel);
//添加播放按钮
PlayOrPause=new JButton(">");
PlayOrPause.setBounds(100, 5, 41, 27);
PlayOrPause.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (PlayOrPause.getText()==">") {
PlayerMain.play();
} else {
PlayerMain.pause();
}
}
});
btnNewButton_2 = new JButton("<<");
btnNewButton_2.setBounds(46, 5, 49, 27);
btnNewButton_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PlayerMain.jumpTo((float)(progress.getPercentComplete()*progress.getWidth()-5)/progress.getWidth());
}
});
controlPanel.setLayout(null);
controlPanel.add(btnNewButton_2);
controlPanel.add(PlayOrPause);
btnNewButton_1 = new JButton(">>");
btnNewButton_1.setBounds(146, 5, 49, 27);
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PlayerMain.jumpTo((float)(progress.getPercentComplete()*progress.getWidth()+5)/progress.getWidth());
System.out.println(progress.getPercentComplete()*getMediaPlayer().getLength());
}
});
controlPanel.add(btnNewButton_1);
//添加声音控制块
slider=new JSlider();
slider.setBounds(288, 6, 200, 26);
slider.setValue(80);
slider.setMaximum(100);
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// TODO Auto-generated method stub
PlayerMain.setVol(slider.getValue());
voiceLabel.setIcon(icon);
controlPanel.add(voiceLabel);
controlPanel.add(slider);
voice=true;
}
});
JButton btnNewButton_4 = new JButton(">|");
btnNewButton_4.setBounds(200, 5, 49, 27);
btnNewButton_4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Next();
}
});
controlPanel.add(btnNewButton_4);
voiceLabel = new JLabel(icon,JLabel.CENTER);
voiceLabel.setBounds(261, 10, 34, 16);
voiceLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int a=slider.getValue();
if (voice==true) {
voiceLabel.setIcon(icon1);
controlPanel.add(voiceLabel);
controlPanel.add(slider);
voice=false;
getMediaPlayer().setVolume(0);
} else {
voiceLabel.setIcon(icon);
controlPanel.add(voiceLabel);
controlPanel.add(slider);
voice=true;
getMediaPlayer().setVolume(a);
}
}
});
voiceLabel.setIcon(icon);
controlPanel.add(voiceLabel);
controlPanel.add(slider);
lblNewLabel = new JLabel("80");
lblNewLabel.setBounds(491, 9, 16, 18);
controlPanel.add(lblNewLabel);
JLabel circu = new JLabel(icon2);
circu.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent arg0) {
if (c==0) {
circu.setIcon(icon3);
panel.add(circu);
c=1;
} else if (c==1) {
circu.setIcon(icon4);
panel.add(circu);
c=2;
}else {
circu.setIcon(icon2);
panel.add(circu);
c=0;
}
}
});
circu.setBounds(647, 5, 72, 37);
panel.add(circu);
JButton btnNewButton_5 = new JButton(">>1");
btnNewButton_5.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PlayerMain.jumpTo((float)(progress.getPercentComplete()*progress.getWidth()+40)/progress.getWidth());
}
});
btnNewButton_5.setBounds(35, 15, 113, 27);
panel.add(btnNewButton_5);
panel_1 = new JPanel();
panel_1.setBounds(0, 528, 745, 26);
videoPane.add(panel_1);
progress = new JProgressBar();
progress.setBounds(144, 5, 476, 20);
progress.setStringPainted(true);
progress.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int x=e.getX();
PlayerMain.jumpTo((float)x/progress.getWidth());
}
});
totalLable = new JLabel("00:00");
totalLable.setBounds(634, 4, 40, 18);
panel_1.setLayout(null);
panel_1.add(progress);
panel_1.add(totalLable);
CurrentLable = new JLabel("00:00");
CurrentLable.setBounds(82, 4, 48, 18);
panel_1.add(CurrentLable);
scrollPane = new JScrollPane();
scrollPane.setBounds(759, 31, 233, 333);
videoPane.add(scrollPane);
list = new JList();
scrollPane.setViewportView(list);
list.addMouseListener(new MouseAdapter() {//双击播放
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount()==2) {
PlayerMain.ListPlay();
AddToLisst1();
}
}
});
JButton btnNewButton = new JButton("\u5220\u9664");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PlayerMain.dlm.removeElement(list.getSelectedValue());
}
});
btnNewButton.setBounds(759, 377, 73, 27);
videoPane.add(btnNewButton);
JButton btnNewButton_3 = new JButton("\u6E05\u7A7A");
btnNewButton_3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PlayerMain.dlm.removeAllElements();
}
});
btnNewButton_3.setBounds(915, 377, 63, 27);
videoPane.add(btnNewButton_3);
JLabel lblNewLabel_1 = new JLabel("\u64AD\u653E\u5217\u8868");
lblNewLabel_1.setBounds(759, 13, 72, 18);
videoPane.add(lblNewLabel_1);
JButton Historybut = new JButton("\u64AD\u653E\u5386\u53F2");
Historybut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (bfls==true) {
historyFrame.setVisible(true);
bfls=false;
} else {
historyFrame.setVisible(false);
bfls=true;
}
}
});
Historybut.setBounds(759, 417, 109, 27);
videoPane.add(Historybut);
JLabel tupian = new JLabel(icon5);
tupian.setBounds(759, 457, 245, 143);
videoPane.add(tupian);
}
//获取播放媒体实例(某个视频)
public EmbeddedMediaPlayer getMediaPlayer() {
return playerComponent.getMediaPlayer();
}
//获取进度条实例
public JProgressBar getProgressBar() {
return progress;
}
public JLabel getCurrentLabel() {
return CurrentLable;
}
public JLabel getTotalLabel() {
return totalLable;
}
public JList getList() {
return list;
}
public JButton getButton() {
return PlayOrPause;
}
public JLabel getLabel() {
return lblNewLabel;
}
public JButton getplayorpauseButton() {
return PlayOrPause;
}
public void Next() { //下一个播放的实现
int index = getList().getSelectedIndex();
if (index!=list.getLastVisibleIndex()) {
list.setSelectedIndex(index+1);
AddToLisst1();
String string=String.valueOf(getList().getModel().getElementAt(index+1));
getMediaPlayer().playMedia(string);
} else {
index=-1;
list.setSelectedIndex(index+1);
AddToLisst1();
String string=String.valueOf(getList().getModel().getElementAt(index+1));
getMediaPlayer().playMedia(string);
}
}
public void AddToLisst1() {
PlayOrPause.setText("||");
PlayerMain.dlm1.addElement(list.getSelectedValue()+" "+df.format(new Date()));
historyFrame.getList().setModel(PlayerMain.dlm1);
}
public void OneCircu() {
int index = getList().getSelectedIndex();
String string=String.valueOf(getList().getModel().getElementAt(index));
getMediaPlayer().playMedia(string);
AddToLisst1();
}
}
package videoplayer;
public class VideoTime {
private int secondTotal;
private int minitueTotal;
private int secondCurrent;
private int minitueCurrent;
public void timeCalculate(long total,long curr){
total=total/1000;
minitueTotal = (int) (total / 60);
secondTotal = (int) (total % 60);
curr = curr / 1000;
minitueCurrent = (int) (curr/ 60);
secondCurrent = (int) (curr % 60);
}
public int getSecondTotal() {
return secondTotal;
}
public void setSecondTotal(int secondTotal) {
this.secondTotal = secondTotal;
}
public int getMinitueTotal() {
return minitueTotal;
}
public void setMinitueTotal(int minitueTotal) {
this.minitueTotal = minitueTotal;
}
public int getSecondCurrent() {
return secondCurrent;
}
public void setSecondCurrent(int secondCurrent) {
this.secondCurrent = secondCurrent;
}
public int getMinitueCurrent() {
return minitueCurrent;
}
public void setMinitueCurrent(int minitueCurrent) {
this.minitueCurrent = minitueCurrent;
}
}
package videoplayer;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
import javax.swing.JList;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class HistoryFrame extends JFrame {
private JPanel contentPane;
private JList list1;
private JScrollPane scrollPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
HistoryFrame frame = new HistoryFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public HistoryFrame() {
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(1220, 80, 495, 457);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
scrollPane = new JScrollPane();
scrollPane.setBounds(0, 1, 477, 283);
contentPane.add(scrollPane);
list1 = new JList();
scrollPane.setViewportView(list1);
JButton btnNewButton_1 = new JButton("\u6E05\u7A7A");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PlayerMain.dlm1.removeAllElements();
}
});
btnNewButton_1.setBounds(10, 297, 113, 27);
contentPane.add(btnNewButton_1);
}
public JList getList() {
return list1;
}
}
Java音视频播放器相关推荐
- Linux系统的madplay、mplayer音视频播放器的制作
Linux系统音视频播放器的制作 madplay和mplayer的安装环境 一.Linux系统录音播放源码的下载和移植 1.需要下载alsa-lib-1.2.6.tar.bz2(声音驱动的内核组件库) ...
- java如何开发视频软件_使用JAVA编写视频播放器
Java因其跨平台优势而陷入困境. 当时,我以为"写一次,到处跑". 这听起来不错,它应该是所有语言开发的最终方法. Java必将统治世界. 事实证明当时我还太小. 所谓的鱼和熊掌 ...
- android音视频播放器开发百度云,Android 播放端 SDK
1 概述 PLDroidPlayer 是一个适用于 Android 平台的音视频播放器 SDK,可高度定制化和二次开发,为 Android 开发者提供了简单.快捷的接口,帮助开发者在 Android ...
- 德声科技代理M-Live音视频播放器
M-live于1987年在里米尼成立,30年来一直是意大利MIDI领域(软件和播放器)的领导者,音乐家.音响工程师和IT专家构成了其工作团队的核心. M-Live生产的乐器消除了个人与音乐体验之间的所 ...
- 开源安卓Android流媒体音视频播放器实现声音自动停止、恢复、一键静音功能源码
本文转自EasyDarwin团队John的博客:http://blog.csdn.net/jyt0551/article/details/60802145 我们在开发安卓Android流媒体音视频播放 ...
- QT + FFmpeg 5.x + x264 + x265 + SDL2 音视频播放器
QT + FFmpeg 5.x + x264 + x265 + SDL2 音视频播放器 使用了QT的QML设计界面,人机交互; 使用了FFmpeg 5.x + x264 + x265 + SDL2 完 ...
- 基于electron的音视频播放器
基于electron的音视频播放器 前言 选择做一个音视频播放器桌面应用程序原因 技术的选型 已经实现了的功能 音视频播放实现 右键菜单实现 总结 效果图 安装包下载: 最后如果大家觉得我这个音视频播 ...
- 基于Qt、FFMpeg的音视频播放器设计一
前言:整个项目的源代码 https://download.csdn.net/download/hfuu1504011020/10672140 最近刚完成基于Qt.FFMpeg的音视频播放器相关C++程 ...
- Qt FFmpeg 音视频播放器
使用FFmpeg库实现 本地和rtp 音视频播放器,使用qt绘制视频. 本demo环境为 qt5.12 vs2019-32位 .pro的qt工程 FFmpeg版本位3.4.8 vs2092-32位 本 ...
最新文章
- 求满足从1加到m的和大于1000的最小m值
- JS和C#访问遇到QueryInterface调用出错
- 四、Vue组件化开发学习笔记——父子组件通信,父级向子级传值(props),子级向父级传值(自定义事件),slot插槽
- java系统缓存应用_著名java开源缓存系统 【zz】
- 理解MapReduce计算构架
- Oracle-Linux安装配置python3.6环境
- easyui datagrid 遇到的坑 cannot read property ·· pageNum bug and so on
- bzoj 4556 字符串
- 类似地图比例尺钩子下边框实现
- c++实现串口功能之termios.h头文件研读<二>
- 群表示论之不可约表示的次数整除G的阶
- 正则表达式详解(贪婪与懒惰、前瞻与后顾、后向引用等)
- C#在VS2019中各种字体颜色的意思
- 学生信息管理系统之查:查询成绩信息流程
- 2k分辨率显示器 浏览器_如何使浏览器使用显示器的完整分辨率?
- 网站侵权服务器在国外,中国电影史上最大泄露事故 侵权网站服务器在境外
- 计算机毕业设计django基于python学校在线打印系统
- 清华大学计算机考研机试KY6 手机键盘
- NLP-文本处理:词性标注【使用成熟的第三方工具包:中文(哈工大LTP)、英文()】【对分词后得到的“词语列表”进行词性标注,词性标注的结果用于依存句法分析、语义角色标注】
- 分组器及事前控制相关系统介绍
热门文章
- 实验十五:数据恢复原理实验
- linux7 epel源,CentOS 7 安装EPEL源
- MySQL查看数据库连接数
- SCJP笔记_章七_泛型与集合
- 使用pm2启动node文件_使用pm2部署node生产环境的方法步骤
- android递归压缩上传多张图片到七牛
- java getcolumns_Java DatabaseMetaData.getFunctionColumns方法代碼示例
- WGCNA 简明指南|1. 基因共表达网络构建及模块识别
- windows全局热键_适用于Windows PC的20种最佳快捷方式和热键提示
- 滴滴也要虎口夺食支付市场?