java制作视频播放器

来源:互联网 发布:上瘾网络剧第二季泰国 编辑:程序博客网 时间:2024/04/29 23:26

前言

国庆前几天,无意中看到关于VLCJ包的文章,可以导入java工程制作自己的java视频播放器,感觉有意思,就自己捣鼓几天,做出了个功能较为齐全的跨平台的视频播放器,高级功能之后有时间再弄,我把项目的源码放在自己的Github上,大家感兴趣的可以下载。我会在之后的博客里分享制作这个视频的过程,把重要的代码贴在博客里,分享一下项目过程遇到的问题,及如何解决。本文主要介绍完成后的视频的界面和功能,以及制作视频的一些想法,具体的编码干货在接下来的博客给出。

视频软件整体面板

整体效果

包括播放面板和播放记录列表面板。
播发面板具有:

  • 进度条 播放进度时间和视频总时长 鼠标点击可直接跳到目标时间点
  • 开始/暂停按钮 鼠标点击 或 空格键 即可触发
  • 快进/快退按钮 鼠标点击 或 左右键 即可触发
  • 全屏/退出全屏 鼠标左点击 或 ‘ESC’即可触发
  • 音量控制 鼠标点击直接设置音量 或 上下键逐渐式加大/减小音量 最大音量为120
  • 列表按钮 点击即可弹出播放列表
  • 全屏时 鼠标移动即可出现半透明控制面板 功能有非全屏状态下控制面板完全一样
  • 支持鼠标左键击 暂停播放,再次点击继续播放, 鼠标双击全屏切换
  • -

这里写图片描述

播放器面板

面板具有播放器基本的功能,如图示:具体的功能说明图片给出!
这里写图片描述

播放记录列表

主要实现了 1、清除历史纪录功能,2、点击列表纪录即可播放视频,而且记录列表动态变化。3、历史记录搜索功能目前未实现,等有再实现。

这里写图片描述

全屏状态

鼠标移动即可出现半透明控制面板 功能有非全屏状态下控制面板完全一样
这里写图片描述

VLCJ介绍

原文关于VLCJ介绍:http://capricasoftware.co.uk/#/projects/vlcj

译文:vlcj项目是一个开源项目,它提供了Java 架包和应用程序框架的VLC VideoLAN媒体播放器。

架包可用于构建媒体播放器客户端和服务器软件使用Java——从简单的播放本地媒体文件到一个完整的视频点播流媒体服务器是可以实现。

vlcj被应用在不同的应用程序中,在海洋研究船和定制软件以及在使用IPTV(Internet Protocol Television)和家庭影院的提供视频功能解决方案。

vlcj还在Elphel被用来创建开源软件摄像机和视频公开街道地图项目映射。

这是对vlcj的基本介绍,详细介绍和相关应用可查看其官网!
对于如何利用VLCJ开发自己player,可以参考官网tutorials
http://capricasoftware.co.uk/#/projects/vlcj/tutorial/prerequisites

下面关于介绍如何制作player,主要参考官网的tutorials的,再加一些自己的idea。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
05.10

准备工作

在准备开发自己的视频播放器,需要做些准备工作:
1. 下载 vlc播放器,因为在开发过程中需要调用vlc内核,需要用到libvlc.dll libvlccore.dll, 具体后面博客说明!32位/64位依自己系统下载
下载地址官网:http://www.videolan.org/vlc/index.html
下载地址32位csdn:
http://download.csdn.net/detail/github_27609763/9152603
2. 下载vlcj jar包,将jna-3.5.2.jar、platform-3.5.2.jar、vlcj-3.8.0、slf4j-api-1.7.12.jar 导入你的工程项目里,需要在工程新建文件夹如:lib,将其复制文件夹下,
下载地址官网:http://capricasoftware.co.uk/#/projects/vlcj
下载地址csnd:
http://download.csdn.net/detail/github_27609763/9152633
3. 除此外,还需导入其它包,不然会报错,需要下载 slf4j-1.7.12文件,并找到slf4j-simple-1.7.12.jar导入,因为slf4j-api-1.7.12.jar依赖slf4j-simple-1.7.12.jar,而vlcj jar包里没有
下载地址官网:http://www.slf4j.org/
下载地址csnd:
http://download.csdn.net/detail/github_27609763/9152629
4. 完成之后你的工程里文件夹如截图所示:
5. 这里写图片描述
6. 将这些包添加进你的项目路径,前期准备完成。

开始创建自己的项目,首先建两个包package,com.mian 和com.views,com.main下新建MyMain(或其他名字)主类,com.views(或其他名字)下新建DisplayFrame类。
这里写图片描述

第一种情况:本地默认库位置
当开发vlcj应用程序的第一步是确保vlcj可以找到LibVLC本地库。这些本地库提供公共API LibVLC,就是使您能够将VLC播放器嵌入到您自己的应用程序。

在大多数情况下,vlcj能够找到安装的本地库,如果没有你需要在自己的代码中做添加包所在地址。这种情况很可能是当你的VLC安装到默认位置在您的计算机上。如果是这样,vlcj能够找到利用你的电脑正常索路径找到本地库。

然而,新的开发人员使用vlcj时最常见的问题是如何正确定位这些库。
vlcj提供了一个类,通过搜索电脑的本地路径,可以找到本机的库所在的位置。
代码如下:

package tutorial;import uk.co.caprica.vlcj.binding.LibVlc;import uk.co.caprica.vlcj.discovery.NativeDiscovery;public class Tutorial {    public static void main(String[] args) {        boolean found = new NativeDiscovery().discover();        System.out.println(found);        System.out.println(LibVlc.INSTANCE.libvlc_get_version());    }}

这个类实例化一个NativeDiscovery类的实例,并调用discover(),返回一个结果显示本地库是否确实是被发现。

检查本地库,下一行的代码打印出找到的LibVLC版本,或者如果没有发现本地库就会抛出一个异常。

如何第一种情况无法实现,则可以按第二种情况提供方式。

第二种情况:显式地设置库路径
如果本机发现不工作,可以显式地设置库路径:

public static void main(String[] args) {        try {            listView.setList(listHistory.readHistory());            listView.setMap(listHistory.readHistoryMap());        } catch (ClassNotFoundException e1) {            // TODO Auto-generated catch block            e1.printStackTrace();        } catch (IOException e1) {            // TODO Auto-generated catch block            e1.printStackTrace();        }        // Decide the platform        if (RuntimeUtil.isWindows())            filePath = "D:\\VideoLAN\\VLC";        else if (RuntimeUtil.isMac())            filePath = "/Applications/VLC.app/Contents/MacOS/lib";        else if (RuntimeUtil.isNix())            filePath = "/home/linux/vlc/install/lib";        NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), filePath);        System.out.println(LibVlc.INSTANCE.libvlc_get_version());    }

该块代码摘自我的项目代码,这块代码实现的是在找不到本地默认的库路径,自己显示设置LibVLC路径。而且代码也给出如何实现跨平台的方法。调用RuntimeUtil判断当前平台,并对filePath赋相应的值。

RuntimeUtil.getLibVlcLibraryName()代码是必需的,因为根据您的操作系统命名不同的本地库。

注意:
你设置的目录搜索路径必须同时包含libvlc和libvlccore所在的目录(在linux 两个文件是 .so 结尾,在Windows以 .dll 结尾)。

完成准备工作的内容,你就可以直接运行你的项目为Java Apllication, 测试下是否包和库路径设置正确。

组件框架

vlcj提供了两种编程模型:一个旨在涵盖常见的用例的高级扩展媒体播放器组件框架,一个给你更多的控制权限,创建你的媒体播放器的低级API。

媒体播放器组件框架提供了一个综合一体化的媒体播放器,并且视频面板可以直接添加到用户界面的布局。这些组件提供各种模板方法,你可以选择覆盖定制组件的行为以适应您自己的应用程序。模板方法也被用来提供一个简单的方法来实现媒体播放器的事件处理程序。

相比之下,对于程序开发人员而言,较低级别的API需要更多的工作。您必须创建一个媒体播放器工厂,然后创建一个媒体播放器,视频,将视频表面自己添加到您的用户界面和添加自己的事件监听器。

尽管你使用低级API,但媒体播放器组件框架提供了访问底层媒体播放器和工厂对象。低层API实际上并没有那么复杂,但毫无疑问,组件框架更容易使用和更快的上手。如果你想直接使用底层API,你应该参考Javadoc

开始创建播放器

在DiaplayFrame利通过Swing编程知识创建新的Frame,可以通过WindowBuilder快速创建JFrame,不需要写额外代码,提高编程效率!
创建完的DisplayFrame加入播放器面板,通过导入vlcj库提供的
uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;包,调用EmbeddedMediaPlayerComponent类,实例化对象playerComponent,并将该component添加如JFrame,既完成加载视频第一步。
我的代码设置JFrame的布局为boardLayout,家center加入Jpanel,再设置JPanel为Boardlayout布局,这样是为之后添加控制控件做铺垫,你也可以按自己的喜好设置自己的布局。在JPanel的center添加playerComponent
主要代码如下:

playerComponent = new EmbeddedMediaPlayerComponent();videoPanel.add(playerComponent, BorderLayout.CENTER);

WindowBuilder下design模式:
这里写图片描述

在添加完内置播放面板,进一步调用
mediaPlayerComponent.getMediaPlayer().playMedia(“path”),播放自己的特定路径的视频。我的代码是将playMedia(“path”),放到后面调用。而是先在MyMain里实例化DisplayFrame对象实例,创建frame,调用DisplayFrame的getMediaPlayer获得之前添加的playComponent,先调用prepareMedia函数,将预播放的视频加载于面板,再调用playMedia(“path”)播放视频。
主要代码:
这里写图片描述

播放视频代码:
这里写图片描述

至此为止,通过调用vlcj高级接口,一个最简单播放器视频已经完成,最关键和主要是之后的功能面板的编写。如开始/暂定、快进/快退、等。
今天写到这,明天再继续。

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
07/10/2015

添加按钮实现相应功能

在之前基础上,为视频播放器添加相应的控制功能,实现基本功能:进度条、开始\暂停、停止、快进\快退、音量控制、全屏功能、播放列表。

进度条功能:

显示视频播放的进度
这里写图片描述

如上图所示:在design模式下添加JProgressBar控件,然后鼠标点击JProgressBar添加事件监听,监听视频当前播放的进度。进度条显示的是当前时间/总时长的百分数。为此,需要获取视频当前播放的时间和视频的总时长。
获取视频当前时间currentTime可获取当前播放的视频并调用EmbeddedMediaPlayer类下的函数.getTime();
获取视频总时totalTime长与之类似,需调用EmbeddedMediaPlayer类下的.getLength()。
获取视频播放百分比可得出:percent = currentTime/totalTime,之后再将percent通过调用publish函数在进度条打印显示。
如下图截图中红色区域所示,
绿色区域是附加的显示时间相关代码
这里写图片描述
实现鼠标点击,视频跳转至目标时间点
实现该功能,不难,进度条显示进度的原理与上面提到在进度条显示进度是一样,同时要设置视频播放时间点为目标时间点,对进度条JProgressBar添加鼠标点击监听,获取鼠标的点击时的坐标。
如下截图示,在获取完鼠标的横坐标,将坐标传入自己写的函数jumpTo(),该函数实现视频的播放点设置。
这里写图片描述
这里写图片描述
至此,进度条的基本功能实现。

开始\暂停功能:

在design下添加JButton控件,将变量命名为startBtn,并为该按钮添加鼠标点击监听事件,点击按钮时,程序触发事件,播放视频需调用EmbeddedMediaPlayer类下的.star()函数
暂停按钮添加及添加监听同上,暂停视频需调用EmbeddedMediaPlayer类下的.pause()函数,我是将两个按钮二合一,添加个if语句判断,使两个按钮互相切换。
监听事件代码:
这里写图片描述
监听触发事件时调用的相应函数代码:
这里写图片描述

停止功能:

在design下添加JButton控件,将变量命名为stopBtn,并为该按钮添加鼠标点击监听事件,点击按钮时,程序触发事件,播放视频需调用EmbeddedMediaPlayer类下的.stop()函数
监听事件代码:
这里写图片描述
监听触发事件时调用的相应函数代码:
这里写图片描述

快进\快退功能:

具体添加控件的方法与之前一样,添加完控件对相应控件添加鼠标点击事件监听,快进\快退本质原理和进度条控制原理是一样的,两个功能调用同一个子函数实现,只是快进\快退是视频播放时间前进或后退某个设定的跳跃值,传入给jumpTo()函数的参数,即currentTime = 进度条长度占 进度条总长度百分比 * 视频事件长度。具体如下截图所示:
快进代码:
这里写图片描述
快退代码
这里写图片描述

音量控制

添加控件JSlider,具体同上。在添加完控件,添加JSlider状态变化监听事件,即当滑块滑动,滑块横坐标相应变化,同时将此时滑块的坐标作为音量设置参数。除此之外,为了使程序功能能更完善,还可以给JSlider添加鼠标点击事件。其实这个事件如果开发者自己不添加,系统是会自己默认添加,只是默认点击事件监听是滑块随鼠标点击的次数,渐进式前进或后退,无法实现滑块直接跳跃到鼠标点击的位置,故需自己添加鼠标点击事件监听,JSlider可以自己设定最大最小值,即为音量的变化范围,在鼠标点击事件监听,不能直接将鼠标e.getX()值直接设为最终音量,因为音量最大值和JSlider控件本身长度在数值上市不相等的,需要换算,即鼠标的横坐标(相对JSlider起始点横坐标而言)占JSlider的比例 乘上 最大音量 。具体的两个事件监听见如下代码截图:
这里写图片描述

全屏功能

添加相应的按钮控件,设置鼠标点击事件监听。实现全屏涉及到跨平台的,不同系统调用的本地系统库函数不一样,最后程序兼容性不是那么好,故
对此使用EmbeddedMediaPlayer库下DefaultAdaptiveRuntimeFullScreenStrategy实现。Linux(或更一般的X11)使用全屏策略(使用XFullScreenStrategy的实例),和Windows使用全屏策略(Win32FullScreenStrategy的实例)不同。前面提到的全屏独占模式,他们是调用本地操作系统API函数。但对OSX系统却没有特定的实现策略。
具体实现全屏有多种方式,其中一种如下代码截图所示,其中画红线即为实现全屏一种策略,在进入全屏之后需要将原来窗口下的其它控件设置为隐藏。
这里写图片描述
其它的实现全屏方式可以是:
mediaPlayerComponent.getMediaPlayer().toggleFullScreen();

mediaPlayerComponent.getMediaPlayer().setFullScreen(true);
可以效果是一样的,可以自己选择。
EmbeddedMediaPlayer下.getMediaPlayer().isFullScreen()函数是用来判断窗口是否进入全屏状态,之后会用到。

播放列表按钮功能

添加相应的按钮控件,设置鼠标点击事件监听。重新建立一个JFrame窗口,用来并列显示播放列表,当点击按钮,播放器窗口右边会弹出列表窗口。具体列表窗口功能实现,之后再写,明天继续更新如何对这些按钮添加键盘快捷键和双击进入\退出全屏。
直至目前,功能较为完整视频播放器完成,可以实现对视频进行一系列操作。

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
08/10/2015

为按钮添加键盘监听事件,添加AWT的低级键盘监听事件,具体见代码:

package com.views;import java.awt.AWTEvent;import java.awt.Toolkit;import java.awt.event.AWTEventListener;import java.awt.event.KeyEvent;import com.main.MyMain;/** * Key board listener * @author ganyee * */public class KeyBordListenerEven {    public void keyBordListner(){        Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {            @Override            public void eventDispatched(AWTEvent event) {                // TODO Auto-generated method stub                if(((KeyEvent)event).getID()==KeyEvent.KEY_PRESSED){                    switch (((KeyEvent)event).getKeyCode()) {                    case KeyEvent.VK_RIGHT:{                        int a = MyMain.getFrame().getVolumControlerSlider().getValue();                        MyMain.getFrame().getVolumControlerSlider().setValue(a);                        MyMain.forword((float)(((MyMain.getFrame().getProgressBar().getPercentComplete() * MyMain.getFrame().getProgressBar().getWidth() + 10)) / MyMain.getFrame().getProgressBar().getWidth()));                    }                        break;                    case KeyEvent.VK_LEFT:{                        MyMain.jumpTo((float)((MyMain.getFrame().getProgressBar().getPercentComplete() * MyMain.getFrame().getProgressBar().getWidth() - 5) / MyMain.getFrame().getProgressBar().getWidth()));                    }                        break;                    case KeyEvent.VK_ESCAPE:{                        if(!MyMain.getFrame().getMediaPlayer().isFullScreen())                            MyMain.fullScreen();                        else                            MyMain.originalScreen();                    }                        break;                    case KeyEvent.VK_UP:{                        MyMain.getFrame().getVolumControlerSlider().setValue(MyMain.getFrame().getVolumControlerSlider().getValue() + 1);                        MyMain.getControlFrame().getVolumControlerSlider().setValue(MyMain.getFrame().getVolumControlerSlider().getValue());//                      MyMain.getFrame().getVolumLabel().setText("" + MyMain.getFrame().getVolumControlerSlider().getValue());                    }                        break;                    case KeyEvent.VK_DOWN:                        MyMain.getFrame().getVolumControlerSlider().setValue(MyMain.getFrame().getVolumControlerSlider().getValue() - 1);                        MyMain.getControlFrame().getVolumControlerSlider().setValue(MyMain.getFrame().getVolumControlerSlider().getValue());                        break;                    case KeyEvent.VK_SPACE:{                        if(MyMain.getFrame().getMediaPlayer().isPlaying()){                            MyMain.pause();                            MyMain.getControlFrame().getPlayButton().setText(MyMain.getFrame().getPlayButton().getText());                        }                        else{                            MyMain.play();                            MyMain.getControlFrame().getPlayButton().setText(MyMain.getFrame().getPlayButton().getText());                        }                    }                        break;                    }                }            }        }, AWTEvent.KEY_EVENT_MASK);    }}

鼠标单击\双击事件监听:
鼠标左键单击,视频停止播放再次点击继续播放视频,鼠标左键双击,切换为全屏模式,再次双击退出全屏模式。
其中实现鼠标双击是比较麻烦些,不是单纯考虑鼠标点击次数就可以,需要将双击和单击区分,不然程序会出现执行判断错误,为此,我是通过设置时间间隔来判断鼠标是否连续点击,如果两次点击在规定时间内,这认为是双击,触发相应时间,其它情况,认为是单击事件。
代码:

......                @Override                public void mouseClicked(MouseEvent e) {                int i = e.getButton();                if (i == MouseEvent.BUTTON1) {                    if (e.getClickCount() == 1) {                        mouseTime = new Timer(350, new ActionListener() {                            @Override                            public void actionPerformed(ActionEvent e) {                                if (playButton.getText() == ">") {                                    MyMain.play();                                    btnText = "||";                                    playButton.setText(btnText);                                } else {                                    MyMain.pause();                                    btnText = ">";                                    playButton.setText(btnText);                                }                                mouseTime.stop();                            }                        });                        mouseTime.restart();                    } else if (e.getClickCount() == 2 && mouseTime.isRunning()) {                        mouseTime.stop();                        if (flag == 0) {                            MyMain.fullScreen();                        } else if (flag == 1) {                            MyMain.originalScreen();                        }                    }                }            }        });......
3 0
原创粉丝点击