机视频播放系统
池瑞楠
摘 要
本文提出了一个手机视频播放系统,在对系统功能模块进行分析的基础上,详细讨论了系统的设计和实现方法,并给出了系统的实现结果,对手机多媒体开发具有很好的学习借鉴和应用价值。
关键词 J2ME,视频播放系统,MMAPI
一、引言
J2ME是Sun公司为小型的消费电子设备(如手机、PDA、电视机顶盒等)而推出的一项Java技术,由于其良好的可移植性、资源占用少和较高的安全性等特点,在移动设备开发领域特别是手机应用软件开发方面得到了广泛的应用。
另一方面,随着人们生活水平的提高,彩屏手机已经在手机市场上确立了主流地位;同时,手机用户在娱乐方面要求也越来越高。视频品质越来越高的移动设备已经
成为一种消费时尚,各大移动服务提供商也开始向手机用户提供视频服务功能,因此基于J2ME技术的手机视频开发也就不可避免地成为当前手机开发中的热点。
本文将详细讨论如何在支持J2ME的手机上开发一个视频播放系统。
二、系统的功能
本文所提出的手机视频播放系统,要求能够实现在支持J2ME技术的手机上播放本地视频文件和网络视频文件,并实现一些基本的视频播放的控制功能,例如暂
停、重播、全屏、黑屏、音量大小调节、静音等。运行本系统后,用户通过菜单选择是播放本地视频还是网络视频,如果用户选择播放本地视频,则系统会给出本地
可以播放的视频列表供用户选择,用户选择并确认后,开始播放本地视频文件;如果用户选择播放网络视频,则用户需要输入网络视频文件的地址,成功连接网络视
频服务器后,则可以开始播放网络视频文件。
根据系统的功能,将其划分成以下几个功能模块:
界面设计模块:主要考虑人性化,当用户第一次使用时,可以很清楚地明白如何操作。
本地视频播放模块:通过列表选项,供用户选择所要播放的视频,并通过一个Canvas类来实现视频的播放。
网络视频播放模块:通过输入网络地址,来访问网络中的视频资源,将视频文件的显示作为一个Item控件添加到Form类屏幕上进行播放。
视频控制模块:提供一些基本的视频播放控制功能,例如暂停、重播、全屏、黑屏、音量大小调节、静音等。这些功能可以通过编写一个视频播放的屏幕类来实现。
事件处理模块:对用户的各种按钮、按键动作进行响应。这部分是通过编写相应的事件处理方法来实现的。
三、系统的设计与实现
1.手机视频开发的基本方法
要在手机上进行多媒体文件的播放,需要用到J2ME提供的MMAPI(Moblile Media
API),它为各种不同格式的多媒体文件提供了一套规范的播放和录制音频/视频文件的统一接口。其数据流图如图1所示。
图1 MMAPI规范的数据流图
使用MMAPI进行多媒体播放的基本流程是:首先从数据源上读取多媒体数据内容(这些数据内容的格式可能各不相同),然后将这些多媒体数据内容传输给
DataSource类进行处理,DataSource类读取并将它们转换为统一的格式,最后由Player类负责播放这些数据。这对于手机音频开发和手
机视频开发都是适用的。
对于手机视频开发,其具体的开发体系如图2所示。
图2 手机视频播放的开发体系图
具体来说,其基本的开发过程可以分成下面的几个步骤来进行:
首先,MMAPI使用了Manager类来创建一个Player对象,然后由Player对象读取视频数据,视频数据既可以是本地视频文件的数据,也可以是网络视频文件的数据。
如果读取的是本地视频文件的数据,方法如下:
InputStream ins = getClass().getResourceAsStream("/test1.mpg"); //
创建本地视频数据的输入流
Player player = Manager.createPlayer(ins, "video/mpeg"); //
创建读取本地视频数据的Player对象
如果读取的是网络视频文件中的数据,则在createPlayer()方法中直接指定网络视频文件的URL即可:
Player player = Manager.createPlayer("http://127.0.0.1/test2.mpg");
// 创建读取网络视频数据的Player对象
创建了Player对象之后,通过Player对象的getControl()方法创建一个VideoControl对象来对需要播放的数据进行控制。VideoControl是一个用来播放视频的控件接口,视频的播放必须通过这个接口才能正常播放。
VideoControl vc;
vc = (VideoControl) player.getControl("VideoControl");
创建了VideoControl对象之后,就必须把VideoControl对象播放的视频图像显示到手机屏幕上。将VideoControl对象播放的
视频图像显示到手机屏幕上的方法有两种,一种是通过一个Canvas类来实现,一种是作为一个Item控件添加到Form类手机屏幕上。本文将在本地视频
的播放中采用第一种方法,在网络视频的播放中采用第二种方法来实现。
2.基本视频播放的实现
(1)本地视频的播放
本地视频的播放是通过一个Canvas类来实现的。在程序中,通过继承一个Canvas类,并使用VideoControl类的
initDisplayMode()方法指定第一个参数的值为VideoControl.USE_DIRECT_VIDEO,指定第二个参数为具体的
Canvas对象(在本程序中即为this)。另外,可以通过VideoControl对象的setDisplayLocation()方法设置视频屏幕
在手机屏幕上的坐标位置,通过setDisplaySize()设置视频屏幕的大小。
在本系统的程序中设计了一个本地视频播放方法areaplay(),将该方法放在自定义的视频屏幕类CanvasVideo类中,实现的核心代码如下:
void areaplay(String url) {
try {
InputStream ins = getClass().getResourceAsStream("/"+url);
player = Manager.createPlayer(ins, "video/mpeg");
player.realize(); // 准备播放
vc2 = (VideoControl) player.getControl("VideoControl");
if (vc2 != null) {
vc2.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, this);
// 获取视频的原始大小
int frameW = vc2.getSourceWidth();
int frameH = vc2.getSourceHeight();
// 计算视频屏幕在手机屏幕上的坐标
if (frameW > videoW)
frameW = videoW;
if (frameH > videoH)
frameH = videoH;
int frameX = (videoW - frameW) / 2 + VB_W;
int frameY = (videoH - frameH) / 2 + VB_H;
// 设置视频屏幕坐标,使视频在手机屏幕的中间显示
vc2.setDisplayLocation(frameX, frameY);
// 设置视频屏幕大小
vc2.setDisplaySize(frameW, frameH);
vc2.setVisible(true);
this.addCommand(AStopCmd);
this.addCommand(AExitCmd);
this.setCommandListener(this);
}
player.start(); // 播放视频
} catch (Exception err10) {
display.setCurrent(areaform);
areaform.append("打开视频时可能出错..请重试!");
reset();
}
}
程序中,设置播放的本地视频屏幕在手机屏幕的中间,大小为视频的原始大小。如果视频的原始大小变大了,以致在手机屏幕上显示不了,则让视频按手机屏幕的大小显示。
网络视频的播放采用了将视频播放作为一个Item控件添加到Form类手机屏幕上的方法来实现。程序中,需要使用VideoControl类的 initDisplayMode()方法指定第一个参数的值为VideoControl.USE_GUI_PRIMITIVE,指定第二个参数为 null。
同样,在本系统的程序中设计了一个网络视频播放方法netplay(),将该方法也是放在自定义的视频屏幕类CanvasVideo类中,具体的代码如下:
// 网络视频播放方法
void netplay(String url) {
try {
player = Manager.createPlayer(url);
player.realize(); // 准备播放
vc = (VideoControl) player.getControl("VideoControl");
if (vc != null) {
Item video;
video = (Item) vc.initDisplayMode(VideoControl.USE_GUI_PRIMITIVE, null);
v.deleteAll();
v.append(video);
v.addCommand(VStopCmd);
v.addCommand(VExitCmd);
v.setCommandListener(this);
display.setCurrent(v);
}
player.start(); // 播放视频
} catch (Throwable err11) {
urlform.append("连接超时或服务器地址不正确");
reset();
}
}
利用上述方法进行的本地/网络视频播放能完成从头到尾播放视频中的每个画面,播放完成后自动停止播放,不能重新播放。
3.视频播放的改进
手机视频播放往往对手机资源要求很高,同时由于无线网络的带宽往往很小,所以应该改进前面给出视频播放的程序,只有视频播放的Player对象准备就绪 后,才开始播放视频,不应该让视频播放程序占用太多的资源。为此,本文将对上面提出的基本视频播放程序进行改进,增加了对Player对象的各种状态的判 断。
在当前世面上存在着大量的不同媒体格式,并且还有许多新的媒体格式即将被建立。为了存储和传输这些不同的媒体格式,存在着了许多不同格式的存储设备和传输 协议,例如大家常使用的媒体存储设备(如CD、VCD以及DVD),有线传输协议(如UDP、HTTP),无线传输协议(如WAP)。
为了使移动装置能够访问这些不同格式的媒体数据,必需为其设计一个规范化的、强大的和可扩充的应用接口。J2ME中就提供了一套为规范的播放和录制音频或视频接口,即Mobile Media API (MMAPI)。
二、MMAPI体系结构
一般情况下,可以将媒体处理过程分解为两个过程:
● 处理媒体数据的传输协议的过程。
● 处理媒体数据内容的过程。
1、 处理媒体数据的传输协议的过程
处理传输协议的过程是指从数据源(如一个文件、一个捕获装置或一个流服务)上读取媒体数据内容后,将其传送到媒体数据内容处理的过程。
MMAPI使用Data Source来处理媒体数据的传输协议的过程。一个Data Source知道如何从它的原始位置读取媒体数据并传送给媒体数据处理(Player)。媒体数据可以被保存在不同的位置,从远程服务器到资源文件或者RMS数据库。媒体数据可以从原始位置通过HTTP,像RTP一样的流式传输协议,或者其他机制传输到媒体数据处理(Player)。图一展示了Data Source的工作过程。
图一:Data Source工作过程
javax.microedition.media.protocol.DataSource提供了MMAPI的Data Source的支持。
2、 处理媒体数据内容的过程
处理媒体数据内容的过程通常需要对媒体数据进行解释和解码,并且还需要认别该媒体的输出设置的类型,如是音频设置或者视频设置。举个例子,当 DataSource从某媒体数据源上获取一MP3媒体数据,并将其传送给处理媒体数据内容过程,此过程首先对这个MP3媒体数据进行解析和解码,同时检 测该MP3媒体为音频数据,打开装置的音频设备,将解析和解码后的媒体数据直接传送到装置的音频设备缓冲中,由该音频设备根据缓冲区中的数据内容产生音频 信号。
MMAPI使用Player来处理媒体数据内容。一个Player是javax.microedition.media.Player接口的一个实现实例,它从Data Source中读取媒体数据、解析和解码数据以及识别媒体输出设备和传送媒体数据到输出设备等。Player提供了一套方法去控制媒体的重放和同步。
MMAPI还提供了一个或多个Controls来调整player的行为,可以在player从媒体转换数据的时候从一个player实例取得并且使用 Controls。我们可以通过Player中提供的一些特殊的Controls访问一些特殊的媒体类型。Controls由 javax.microedition.media.Control接口实现。
3、 Manager
J2ME为了实现对DataSource和Player有效管理,使用了工厂机制,由Manager来负责创建Player和 DataSource。这样的机制在JAVA中到处可见,如JDBC中的DriverManager,这里我就不对这种机制进行介绍了。Manager不 仅可以从DataSource中创建Player,而且还可以从本地或InputStream中创建Player。图二展示了MMAPI整体结构图。
图二:MMAPI整体结构图
三、 使用MMAPI
MMAPI提供的类和接口都在javax.microedition.media中,在程序中使用MMAPI时,首先应该引用这些包,否则程序无法编译。
每个MMAPI程序都需要创建一个Player对象,前面我们已经介绍过,MMAPI使用Manager的CreatePlayer函数来创建Player对象,该函数有三个版本,其格式如下:
public static Player createPlayer(String locator)
throws IOException, MediaException
public static Player createPlayer(DataSource source)
throws IOException, MediaException
public static Player createPlayer(InputStream stream, String type)
throws IOException, MediaException
第一个版本实现通过URL字符串指定的协议和数据位置的信息创建一个Player对象,Manager将对createPlayer函数中提供的URL字符串参数进行分析,创建一个Data Source对象,由该对象完成对媒体数据的传输工作,并从数据中获取该媒体的数据内容类型,Manager将根据这个媒体数据类型创建相应的 Player对象,如果Manager无法确定DataSource的内容类型,它将抛出一个MediaException异常。
例:创建一个控制某网站MP3音频的Player对象。
Player pMP3 = Manager.createPlayer( "http://www.XXX.com/111.mp3");
第二版本实现通过已知的DataSource对象创建Player对象。
第三版本实现通过InputStream流创建Player对象。
我们根据应用实际的情况选择使用那种版本来创建Player对象。有了一个Player对象以后,接下来要通过这个Player对象提供的方法去控制媒体流。下面我们列出常用的方法:
Player.start():重放媒体流。
Player.stop():停止媒体流。
Player.setMediaTime(long now):设置媒体时间。
Player.close():关闭媒体流并释放资源。
Player.getState():获取Player的当前状态。
每个Player对象中都存在着一个状态变量,用于表示该Player对象的生命周期。当Player第一次被建立时处于UNREALIZED状态;当为 该Player设置了媒体数据的位置后,它处于REALIZED状态(如Player正在从一个服务器的HTTP连接下载并解释数据的时或Player 在Http请求已经发送到服务器,收到HTTP响应后,而且DataSource准备好接收媒体数据的时);当该Player已经读到足够的数据而开始解 释和运算时,处于PREFETCHED状态;当数据在运算完毕后,该Player的状态变成了STARTED。我们在使用Player对象的方法对媒体流 进行控制时,应注意它们可能影响到Player状态的改变,可以使用getState函数获取当前Player的状态。
四、 播放常见媒体的方法
1.播放单音与序列音
如果要播放一个单音一次,可使用:
Manager.playTone(note,duration,volume);
如果要播放序列音,则必须使用ToneControl。使用一个特别的定位器来创建一个
Player对象,获得ToneControl,设置其命令序列,然后启动播放器,如:
Player p = Manager.createPlayer(Manger.TONE_DEVICE_LOCATOR);
p.realize();
ToneControl tc =
(ToneControl)(player.getControl("ToneControl"));
tc.setSequence(new byte[] {ToneControl.C4,8
ToneControl.C4+2,8});
p.start();
2.播放声讯和MIDI
声讯是指如WAV这样的声音格式,在这种格式中,数据是各种声讯样本的一个流,它代表着该声讯每一秒钟的片断。MIDI则是一系列的命令,用作多乐器的某种"虚拟合成器"。
如要播放一段能够通过HTTP访问的声音文件,请使用:
Player p = Manager.createPlayer(http://something.com/somefile.wav);
p.start();
如要播放一段已经被置于MIDlet的JAR文件中的声音文件,需要先了解其MIME类型(如,"audio/x-wav"),然后,使用:
InputStream is = getClass().getResourceAsStream("/somefile.wav");
Player p = Manager.createPlayer(is,"audio/x-wav");
p.start();
如要播放一段置于RMS中的声音文件,请使用:
RecordStore rs = RecordStore.open("name");
byte[] data = rs.getRecord(id);
ByteArrayInputStream is = new ByteArrayInputStream(data);
Player p = Manager.createPlayer(is,"audio/x-wav");
p.start();
3.播放视频
播放视频类似于音频播放。然而,需要告诉视频播放器在哪里显示视频信号,因此,需要从视频播放器处得到一个"视频控件",然后在Form或Canvas中显示视频内容。下面展示一个从Canvas中显示视频的例子:
InputStream is =
getClass().getResourceAsStream("/somefile.avi");
Player p = Manager.createPlayer(is,"video/avi");
p.realize();
VideoControl vc = (VideoControl)p.getControl("VideoControl);
if( vc != null )
{
vc.initDisplayMode(VideoControl.USE_DIRECT_VIDEO,cav);
//cav为Canvas对象。
vc.setVisible(true);
p.start();
}
浙公网安备 33010602011771号