概述
移动多媒 体包括 使用移动 终端播放 音乐, 视频,拍 照,录制 视频, 和在线影 音 。 Bla ck Berry 支持 移动多媒 体,你 可以通 过 Bla ckB erry Java 或 Bla ck Berry 浏览器 来创建 自己 的媒体应 用。功 能包括 播放音频 ,视频 , SV G 数 据,录制 音频, 视频,流 媒体,以 及向蓝 牙设备发 送 媒体流。
Bla ckBe rr y Ja v a 提供 的 多 媒体支持 包括 MM A P I-J SR 1 3 5 和 P laz m ic M edi a Eng i n e 。本章 主要介 绍
MM A P I 在 Bla c kBerr y 平台 上的应用 。
创建第一 个 Bl ac k Be rr y 媒体播 放 程序
Bla ckBe rr y 为媒 体开发提 供了 M M A P I ,即 P lay er 和 jav a x.m icr o edi tio n . m edi a 包。它为 媒体资 源播 放,控制 播放, 和管理播 放状态提 供了一 些列方法 。
一个 P la y er 有五个 状态: UN REALIZE D , R EALIZE D , P R EFETCHE D , STA R TED , CL OS ED 。 设计这 些状态 目 的是为消 耗时间 和资源的 操作提供 更好的 控制。
P lay er 被创 建后的 初始状 态是 UN REA L IZED 。在这 个状态下 , P la y er 实例不 知道媒体 文件的 任何信 息,不能 播放媒 体文件 。
P lay er.realiz e() 方法会 把 P layer 的 状体从 UN REALIZE D 变为 R EALIZE D 状 态。 这个方法 会访问 媒体文 件或媒体 资源来 获取必要 的媒体信 息为播 放做准备 。
P lay er.p refet ch() 方 法会把 P lay er 的状 态变为 P REFET CH ED 。在 这个状 态下, P l aye r 会 进一步 获取媒 体资源必 要信息 并装载部 分媒体资 源为播 放媒体做 好准备。
P lay er.start () 方法 会把 P la y er 的 状态变 为 STA RT ED , P la y er 将 会立即 开始播 放媒体 资 源。
P lay er 如果 没有经 过 reali z e () 和 p ref et ch() 是不可能 开始播放 的。 sta rt() 方法 会间接的 呼叫 p refet ch( 0) , 也就是 r ealiz e() ,在 媒体播 放开始 之前 。直接呼 叫 star t() 来 播放 会对媒体 播放造 成延 迟。
当 P la y er 播 放媒体 内容完 毕或者 P lay er.st o p () 方法 被叫时, p layer 会从 STA RTE D 状态 变为
P REFE TECH E D 状态 。在 P REFECTE D 状态 的 P l aye r 可以随时 再次播 放或循环 播放。
在除 CL OSED 状态以 外的 任何状态 都可以 呼叫 cl o s e () 方法 , p lay er 状态变 为 CL OSE D ,这 时 p layer
释放媒体 资源, 不再可用 。
P lay er.d eallo cate () 只能 在 P REFE TCHED , REA L IZED 状态下呼 叫,它 使 p layer 返回前一 个状态 。
P lay er 的五 个状态 和状态 之间的转 换 如下 图 所示:
介绍了 P lay er ,它 的方法 和状态, 下面让 我们来 为 Bla ckB erry 创建第一个 媒体播放 程序。
1. 引入需要 的类
import javax.microedition.media.Manager;
import javax.microedition.media.Player;
import java.io.IOException;
2. 通过 Manager.createPla yer() 方法获得 Player 实例
3. 呼叫 Player.realize() 方法来为 播放做 准备, 应用 程序获取 需要的 媒体信 息。
4. 呼叫 Player.prefetch() 使 BlackBerry 应用程 序获 取必要的 媒体信 息,装 载部 分媒体内 容为 播放做好 准备。
5. 呼叫 Player.start() 开 始播放。
Player p = Manager.createPlayer( "http://www.test.rim.net/abc.wav" );
/**
* 指向媒体 文件的 ab c. w av 的 URL 作为一 个 URI 参数 传入 Manager.createPla yer() 。
*
* 最好规范 是先叫 re al i ze () , 然后 prefetch () , 再 start() 。按照 这个持 续可以减 少
* 播放延迟 。
*
* 直接呼叫 start() 其实 是先呼叫 prefetch (0) 也 就是 realize() ,在 开始 播放。
*/
p.start();
6. 呼叫 Player.stop() 来停 止播放。
7. 呼叫 Player.close() 使 Player 进入 CLOSED 状态 , player 实例不 再可用 。
B lack B er ry 音 频 播放编程
播放音频范例
本节介绍 如何在 Bl ackB err y 上创建 一个音 频播放器 播放 m p 3 , 并提供 播放, 暂停,停 止的 控 制。 播放器如 下图所 示:
播放器源代码 如下 :
public class MusicPlayerApp extends UiApplication{
public static void main(String[] args){
// 应用程序 启动入 口
getInstance ().enterEventDispatcher();
}
// MusicPlayerApp 的单类
private static MusicPlayerApp m_instance ;
private MusicPlayerScreen musicPlayerScreen ;
public MusicPlayerApp(){
// 创建播放 器屏幕
musicPlayerScreen = new MusicPlayerScreen();
// 推出屏幕 堆栈, 显示播 放器屏幕
pushScreen( musicPlayerScreen );
}
// 实现 MusicPlayerAp p 单类,运 行环境 中只有一 个 MusicPlayerApp 实例
public static synchronized MusicPlayerApp getInstance(){
if ( m_instance == null ){
m_instance = new MusicPlayerApp();
}
return m_instance ;
}
以上代码 表示使 用 Bl ac kB err y 提供的 U I 来 组建 界面。 g e tI ns t an ce( ) 使用 了单例, 即程序 运行环
境 中 只 有 一 个 M usi cPl a y erAp p 的 实 例 , 统 一 通 过 g e tI ns t an ce( ) 来 调 用 , 这 样 可 以 避 免 多 个 M us i cPl a y erAp p 实例而导致对象状态不稳定。 g e tI ns t an ce( ). en t erEv en t D i spa t che r ( ) , 这行代码 创建了 M usi cPl a y erAp p 的实例并 调用 en t erEv entDi spa t che r ( ) 来启 动应用 。
// 播放器屏 幕
class MusicPlayerScreen extends MainScreen implements
FieldChangeListener{
public static final String SONG_NAME = "All Rise" ;
// 指向歌曲 的 URI
public static final String URI = "/sounds/all_rise.mp3" ;
// 歌曲的格 式, audio/m peg 支持 mp3
public static final String AUDIO_FORMAT = "audio/mpeg" ;
// 播放器的 所有状 态
public static final int PLAYING = 0;
public static final int READY_FOR_PLAY = 1;
public static final int PAUSE = 2;
public static final int STOP = 3;
// 播放器的 当前状 态
private int m_currentStatus = READY_FOR_PLAY ;
// 播放器实 例
private Player m_player ;
// 播放器标 签 Layout
private VerticalFieldManager m_labelManager ;
// 播放器标 签
private LabelField m_songNameField ;
private LabelField m_durationField ;
private LabelField m_statusField ;
// 播放按钮 Layout
private HorizontalFieldManager m_buttonManager ;
// 播放器按 钮
private ButtonField m_pauseButton ;
private ButtonField m_playButton ;
private ButtonField m_stopButton ;
public MusicPlayerScreen(){
super (Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL );
// 创建播放 器并做 好播放 准备
createPlayer();
// 创建界面
initializeUI();
}
M us i cPl a y erSce en 构造函数描述 启动 M usi cPl a yerScreen 要执 行的任务 ,创建播 放器和 初始化 界面。
private void initializeUI(){
setTitle( new LabelField( " 黑莓播放 器 " , Fi el d. FIELD_VCENTER |Field. FIELD_HCENTER ));
// 歌曲名称 标签
m_songNameField = new LabelField( " 歌曲名 称 : " + SONG_NAME , Fi e ld . USE_ALL_WIDTH );
// 歌曲时长
m_durationField = new LabelField( " 播放时 长 : "
+ m_player .getDuration()/1000000/60+ " 分钟 " , Fi e ld . USE_ALL_WIDTH );
// 播放状态
m_statusField = ne w LabelField( " 播放状 态 : " +getStatus(), Field. USE_ALL_WIDTH );
m_labelManager = new
VerticalFieldManager(Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL ); m_labelManager .add( m_songNameField ); m_labelManager .add( m_durationField ); m_labelManager .add( m_statusField );
add( m_labelManager );
// 暂停按钮
m_pauseButton = new ButtonField( "||" ,
Field. FIELD_VCENTER |Field. FIELD_HCENTER );
m_pauseButton .setChangeListener( this );
// 播放按钮
m_playButton = new ButtonField( ">" , Field. FIELD_VCENTER |Field. FIELD_HCENTER );
m_playButton .setChangeListener( this );
// 停止按钮
m_stopButton = new ButtonField( "O" , Field. FIELD_VCENTER |Field. FIELD_HCENTER );
m_stopButton .setChangeListener( this );
m_buttonManager = new
HorizontalFieldManager(Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL
);
m_buttonManager .add( m_pauseButton ); m_buttonManager .add( m_playButton ); m_buttonManager .add( m_stopButton );
add( m_buttonManager );
}
函数 i ni t i ali z e U I( ) 创 建所 有 U I 组建, 并按一 定结 构组装标 签和控 制按钮 。
// 获取当 前播放状 态
private String getStatus(){
switch ( m_currentStatus ){
case PLAYING :
return " 正在播放 " ;
case READY_FOR_PLAY :
return " 准备完毕 " ;
case PAUSE :
return " 暂停 " ;
case STOP :
return " 停止 " ;
}
return null ;
}
函数 g etS t atus ( ) 根绝当前 播放器状 态来获 取状态消 息。
// 根据设 置的播放 状态更 新状态 标签
private void setPlayerStatus( int status){
m_currentStatus = status;
m_statusField .setText( " 播放状态 : " +g et S ta tu s( )) ;
}
setPl ay erStat us(in t ) 通过 当前播放 器状态 来更新播 放器状态 标签。
// 创建播放 器
private void createPlayer(){
try {
// 关闭当前 播放 器
if ( m_player != null ){
m_player .close();
m_player = null ;
}
// 创建新播 放器
InputStream is = this .getClass().getResourceAsStream( URI );
m_player = javax.microedition.media.Manager. createPlayer (is,
AUDIO_FORMAT );
// 预装媒体 资源 准备播放
m_player .prefetch();
} catch (Exception ex){
ex.printStackTrace();
}
}
函数 cr ea t ePl ay er( ) 创 建 Pl ay er 实例,并预装载媒 体内容为 播放做 好准备。 如果当前 Pl ay er 实例 已经存在 ,它会 先销毁当 前 Pl ay er 然后再创建新 Pl ay er 。
public void fieldChanged(Field field, int context) {
if (field == m_playButton ){
// 当播放按 钮按下 时
// 根据播放 器当前 状态进 行处理
switch ( m_currentStatus ){
case PLAYING :
this .setPlayerStatus( PLAYING );
return ;
case READY_FOR_PLAY :
try {
m_player .start();
} catch (Exception ex){}
setPlayerStatus( PLAYING );
return ;
case PAUSE :
try {
m_player .start();
} catch (Exception ex){}
setPlayerStatus( PLAYING );
return ;
case STOP :
createPlayer();
try {
m_player .start();
} catch (Exception ex){}
setPlayerStatus( PLAYING );
return ;
}
} else if (field == m_pauseButton ){
// 当暂停按 钮按下 时
// 根据播放 器当前 状态进 行处理
switch ( m_currentStatus ){
case PLAYING :
try {
m_player .stop();
} catch (Exception ex){}
setPlayerStatus( PAUSE );
return ;
case READY_FOR_PLAY :
return ;
case PAUSE :
return ;
case STOP :
try {
m_player .close();
} catch (Exception ex){}
setPlayerStatus( STOP );
return ;
}
}
else if (field == m_stopButton ){
// 当停止按 按钮按 下时
// 根据播放 器当前 状态进 行处理
switch ( m_currentStatus ){
case PLAYING :
try {
m_player .stop();
m_player .close();
} catch (Exception ex){}
setPlayerStatus( STOP );
return ;
case READY_FOR_PLAY :
try {
m_player .close();
} catch (Exception ex){}
setPlayerStatus( STOP );
return ;
case PAUSE :
try {
m_player .close();
} catch (Exception ex){}
setPlayerStatus( STOP );
return ;
case STOP :
return ;
}
}
}
}
}
f i el dC ha n g ed ( Fi el d f i el d, i nt con t ex t) 做为按钮监 听接口,根据按钮事件判断执行哪一段按钮事件。
三个按钮 分别是 :播放, 暂停,停 止。在 每个按钮 的事件响 应根据 播放器当 前状态来 执行不 同任 务,详细 描述请 查看注释 。
Bl ac kB erry 视 频 播放编程
Bla ckBe rr y 平 台支 持 M M A P I ,支 持 P la y er 在 Bla ckB erry 界 面框架 上播放 视频 。视频格 式包括 m pg ,
3 gp , av i 等等 。以下代码 片段分别 解释在 M ID P 和 Bla ckBe rr y UI 两 种框架下 的视频播 放。
对于提供 了 L C D UI 支持 的 M I D P 实 现:
public void playVideo() {
try {
// 创建 Player ,媒 体资 源为网络 上的视 频,并 获取 视频信息 Player p = Manager. createPlayer ( "http://abc.mpg" ); p.realize();
// 界面控制 器
GUIControl gc;
// 获取界面 控制器
// 使用 Item 播放 视频
// 把 Item 添加到 Form , 再显示 Form
// 实现了在 Form 播放视 频
if ((gc = (GUIControl) p.getControl( "GUIControl" )) != null ) {
Form form = new Form( "My GUI" );
form.append((Item) gc.initDisplayMode( GUIControl. USE_GUI_PRIMITIVE , null ));
Display.getDisplay ( this ).setCurrent(form);
}
// 开始播放
p.start();
} catch (MediaException pe) {
} catch (IOException ioe) {
}
}
范例 ( 1 )
在 M I D P 框 架下界 面方式 有两种, F o r m 和 Can v as 。 For m 界面 是 M I D P 框架 下的高级 UI 组件, 容 器与组件 的结构 。上述代 码是在 M ID P 高级 UI 框架 中播放视 频。 Can v as 是 M ID P 框架 中的低 级界 面,程序 员需要 在 Can v a s 上绘制自 己的 UI 组 件。 范例 ( 2 ) 为 在 Can v as 上播 放视频。
M ID P 框架 下 Can v a s 组件 上播放视 频 :
public void playVideo( Canvas canvas ) {
try {
// 创建 Player ,媒 体资 源为网络 上的视 频,并 获取 视频信息 Player p = Manager. createPlayer ( "http://abc.mpg" ); p.realize();
// 视频控制 器
VideoControl vidc;
// 获取视频 控制器
// 传入 Canvas ,配 置在 C an vas 里播放视 频
// 使用视频 控制配 置播放 参数
if ((vidc = ( VideoControl ) p.getControl( "VideoControl" )) !=
null ){
vidc.initDisplayMode( VideoControl. USE_DIRECT_VIDEO , canvas);
vidc.setDisplayFullScreen( true );
vidc.setVisible( true );
}
// 开始播放
p.start();
} catch (MediaException pe) {
} catch (IOException ioe) {
}
}
范例 ( 2 )
在使用 Can v as 播 放视频 时,需要 获取 Vid e o C o n tr o l ,它 可以把 C an v as 作为 播放组件 ,同时 提供一 些方法调 整视频 显示位置 和尺寸。
以上是 M ID P 框架 下高级 UI 和低级 UI 播 放视频的 方法。在 Bla ckB erry 平台 上如果应 用程序 使用的
是 M I D P 框 架开发 视频播 放,可以 直接使 用这两个 范例。 Bla c kBerr y 平台提 供 Bla ckB erry 特有的 UI
框架,框 架结构 与 J 2 SE 比较类似 , Co n tain er-L a y o u tM an ag er -Co m p o n ent 的结构。 Bla ckB erry 最基 本的 UI 组件 是 Fi eld ,视 频需要在 Fi eld 里进行播 放,如范 例 ( 3 ) 所 示。
Bla ckBe rr y UI 框 架下播放 视频 :
public void playVideo(){
try {
// 创建新播 放器
InputStream is = this .getClass().getResourceAsStream( URI );
m_player = javax.microedition.media.Manager. createPlayer (is,
VIDEO_FORMAT );
// 获取视频 文件信 息,这 一步一定 要有, 否则获 取的 VideoControl 将无效
m_player .realize();
// 获取视频 控制
m_videoControl =
(VideoControl) m_player .getControl( "VideoControl" );
if ( m_videoControl != null )
{
// 从视频控 制获取 UI 组件,这 个组件 用来显 示视 频,常用 组件比如 Field
m_videoField = (Field) m_videoControl .initDisplayMode
(VideoControl. USE_GUI_PRIMITIVE ,
"net.rim.device.api.ui.Field" );
// 设置显 示尺 寸
m_videoControl .setDisplayFullScreen( true );
// 使视频 可见
m_videoControl .setVisible( true );
}
if ( m_videoField != null ){
// 添加视频 组件到屏 幕并开始 播放
add( m_videoField );
m_player .start();
}
} catch (Exception ex){
ex.printStackTrace();
}
}
范例 ( 3 )
创建播放 器和播 放流程 与 M I D P 框 架下的 播放器 相同,但 在获取 显示视频 组件时有 所不同 ,对于
Bla ckBe rr y 风格 的界面要 求在 Fi eld 里显 示视频 :
Field videoField = (Field)
m_videoControl .initDisplayMode(VideoControl. USE_GUI_PRIMITIVE ,
"net.rim.device.api.ui.Field" );
要注意的 是获取 Vi d eo C on tro l 之前 一定要 让播放 器获取媒 体信息 , P la y er.r ealiz e() , 否则获 取的
Vid eo C o n tro l 无效。
以上是在 三种界 面框架下 的播放方 式。针 对 Bla ckB erry 平 台 Bla ck Berry 风格 界面的视 频播放 ,以 下是一个 完整的 代码例子 :
import java.io.InputStream;
import javax.microedition.media.Player;
import javax.microedition.media.control.VideoControl;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.container.MainScreen;
public class VideoPlayerApp extends UiApplication{ private static VideoPlayerApp m_instance ; private VideoScreen m_videoPlayerScreen ; public static void main(String[] args){
// 应用程序 启动入 口
getInstance ().enterEventDispatcher();
}
public VideoPlayerApp(){
// 创建播放 器屏幕
m_videoPlayerScreen = new VideoScreen();
// 推出屏幕 堆栈, 显示播 放器屏幕
pushScreen( m_videoPlayerScreen );
}
// 实现 VideoPlayerAp p 单类,运 行环境 中只有一 个 VideoPlayerApp 实例
public static synchronized VideoPlayerApp getInstance(){
if ( m_instance == null ){
m_instance = new VideoPlayerApp();
}
return m_instance ;
}
// 播放器屏 幕
class VideoScreen extends MainScreen{
// 指向本地 视频的 URI
public static final String URI = "/video/song.3GP" ;;
// 播放器实 例
private Player m_player ;
// 视频播放 控制
private VideoControl m_videoControl ;
private Field m_videoField ;
public VideoScreen(){
super (Manager. NO_HORIZONTAL_SCROLL |Manager. NO_VERTICAL_SCROLL );
// 在 VideoScreen 上播 放视频
playVideo();
}
// 创建播放 器
private void playVideo(){
try {
// 创建新播 放器
InputStream is = this .getClass().getResourceAsStream( URI );
m_player = javax.microedition.media.Manager. createPlayer (is,
VIDEO_FORMAT );
// 获取视频 文件信 息,这 一步一定 要有, 否则获 取的 VideoControl 将无效
m_player .realize();
// 获取视频 控制
m_videoControl = (VideoControl) m_player .getControl( "VideoControl" );
if ( m_videoControl != null )
{
// 从视频控 制获取 UI 组件,这 个组件 用来显 示视 频,常用 组件比如 Field
m_videoField = (Field) m_videoControl .initDisplayMode
(VideoControl. USE_GUI_PRIMITIVE , "net.rim.device.api.ui.Field" );
// 设置显示 尺寸
m_videoControl .setDisplayFullScreen( true );
// 使视频可 见
m_videoControl .setVisible( true );
}
if ( m_videoField != null ){
// 添加视频 组件到屏 幕并开始 播放
add( m_videoField );
m_player .start();
}
} catch (Exception ex){
ex.printStackTrace();
}
}
// 阻止屏幕 弹出 save 菜单
public boolean onSavePrompt(){
return true ;
}
}
}