最近在Android游戏开发中,需要处理WAV和OGG的播放。其中背景音乐需求为一路WAV或者OGG。在Android.media Package中。选中了MediaPlayer作为背景音乐的播放。
0. 简介:
1. Playback状态机:
可以将播放文件和流看作状态机管理。下图详细描述了各状态以及如何在各状态间转移。
注:单箭头表示同步调用。双箭头表示异步调用。
现在详细分析每个状态以及状态间转移。
1.1:Idle状态:
当MediaPlayer object刚被使用new 创建,或者调用reset()后,此MediaPlayer object处于Idle状态。
两种方法进入Idle状态,有个细小却重要的区别:当程序在Idle状态错误的调用了类似getCurrentPosition(),GetDuration(),getVideoight(),setAudioStreamType(), setLooping(),setVolume(),pause(),start(),stop(),seekTo(),prepare()等方法。
如果MediaPlayer object刚被new 出来,处于idle状态。用户使用OnErrorListener.onError()注册的callback函数不会被内在palyer Engine调用,且状态不会发生改变,继续为idle.
如果是reset() 后处于idle状态,则注册的callback函数被调用。且MediaPlayer object会转为Error状态。
1.2:End状态:
当MediaPlayer object调用release() 后,它处于End 状态。
如果MediaPlayer object不再使用时,请立即调用release(),这样,内部player engine可以释放掉他们所占用的资源。 这类资源,不光包括内存资源,还包括Audio,Video硬件解码设备。
当用户进入End状态后,没有任何渠道去转回其它状态。
1.3:Initialized状态:
当MediaPlayer Object为Idle状态时,调用setDataSource(FileDescriptor), or setDataSource(String), or setDataSource(Context, Uri), or setDataSource(FileDescriptor, long, long)转变其状态到Initialized状态。
如果setDataSource()在非Idle状态调用,
所以,setDataSource()时必须捕获IllegalArgumentException
1.4:Prepared状态:
MediaPlayer Object 必须首先进入Prepared状态,之后才能开始start.
有两种方式进入Prepared状态:
调用prepare() 则以同步方式进入Prepared状态。当prepare()返回时,MediaPlayer Object 已经进入Prepared状态。
调用prepareAsync()则以异步方式进入Prepared状态。
当在错误状态调用prepare() or prepareAsync() ,则抛出异常IllegalStateException。
1.5: Started状态:
要播放,则需要调用start(),当start()返回successfully时,MediaPlayer Object则进入Started状态。
isPlaying()可以测试是否位于Started 状态。
在MediaPlayer Object处于Started状态时,调用start()没有任何效果。
1.6:Paused状态:
调用pause(),当其返回时,MediaoPlayer Object进入Paused状态。从started状态到pasued状态,Play Engine有缺陷。所以导致isPlaying()...
MediaPlayer进入Paused状态后,调用start()重新开始播放(Resume).但Position是从刚才暂停处开始播放,而不是从头。且状态变化为Started。
在Paused状态,再次调用pause()没有反应 。
1.7 Stop状态:
在Started,Paused,Prepared或者PlaybackCompleted状态,调用stop(),则进入Stopped状态。
在Stopped状态,MediaPlayer Object不能再次调用start()去播放,除非调用prepare()或prepareAsync()使其进入Prepared状态。
在Stopped状态下再次调用stop.无效。
1.8
当Video/Audio播放完毕后,进入PlaybackCompleted模式。
但如果使用setLooping(true),则播放完毕后,立刻重新播放。且状态继续为started.
如果setLooping(false).播放完毕后,MediaPlayer Object进入PlaybackCompleted模式,并调用setOnCompletionListener(OnCompletionListener)注册的callback函数。
如在
2. 回调机制:
MediaPlayer使用一些方法来设置回调函数,当发生某种特定情况时,内部palyer Engine会调用之。
使用方法,可以在稍后的例子中看到。
3. 播放指针:
可以调用
下表列出了各种方法在哪些状态下调用合法,在哪些状态下非法以及导致的后果:
Valid and invalid states
Method Name
|
Valid Sates
|
Invalid States
|
Comments
|
attachAuxEffect
|
{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}
|
{Idle, Error}
|
This method must be called after setDataSource. Calling it does not change the object state.
|
getAudioSessionId
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
getCurrentPosition
|
{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
getDuration
|
{Prepared, Started, Paused, Stopped, PlaybackCompleted}
|
{Idle, Initialized, Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
getVideoHeight
|
{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
getVideoWidth
|
{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
isPlaying
|
{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
pause
|
{Started, Paused}
|
{Idle, Initialized, Prepared, Stopped, PlaybackCompleted, Error}
|
Successful invoke of this method in a valid state transfers the object to thePaused
|
prepare
|
{Initialized, Stopped}
|
{Idle, Prepared, Started, Paused, PlaybackCompleted, Error}
|
Successful invoke of this method in a valid state transfers the object to thePrepared
|
prepareAsync
|
{Initialized, Stopped}
|
{Idle, Prepared, Started, Paused, PlaybackCompleted, Error}
|
Successful invoke of this method in a valid state transfers the object to thePreparing
|
release
|
any
|
{}
|
After release() , the object is no longeravailable.
|
reset
|
{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}
|
{}
|
After reset() , the object is like beingjust created.
|
seekTo
|
{Prepared, Started, Paused, PlaybackCompleted}
|
{Idle, Initialized, Stopped, Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
setAudioSessionId
|
{Idle}
|
{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}
|
This method must be called in idle state as the audio session ID must be known before calling setDataSource. Calling it does not change the object state.
|
setAudioStreamType
|
{Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method does not change the state. In order for the target audio stream type to become effective, this method must be called before prepare() or prepareAsync().
|
setAuxEffectSendLevel
|
any
|
{}
|
Calling this method does not change the object state.
|
setDataSource
|
{Idle}
|
{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}
|
Successful invoke of this method in a valid state transfers the object to theInitialized
|
setDisplay
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setSurface
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setLooping
|
{Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method in a valid state does not change the state. Calling this method in an invalid state transfers the object to the
|
isLooping
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setOnBufferingUpdateList
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setOnCompletionListener
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setOnErrorListener
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setOnPreparedListener
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setOnSeekCompleteListene
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setScreenOnWhilePlaying |
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
setVolume
|
{Idle, Initialized, Stopped, Prepared, Started, Paused, PlaybackCompleted}
|
{Error}
|
Successful invoke of this method does not change the state. |
setWakeMode
|
any
|
{}
|
This method can be called in any state and calling it does not change the object state.
|
start
|
{Prepared, Started, Paused, PlaybackCompleted}
|
{Idle, Initialized, Stopped, Error}
|
Successful invoke of this method in a valid state transfers the object to theStarted
|
stop
|
{Prepared, Started, Stopped, Paused, PlaybackCompleted}
|
{Idle, Initialized, Error}
|
Successful invoke of this method in a valid state transfers the object to theStopped
|
例子程序如下:
MediaPlayer所支持格式:
这个问题,看到网上有不少人回答。但个人觉得回答并不正确。
MediaPlayer底层具体实现决定了所支持格式。毕竟JAVA层不太可能直接操作硬件去播放Video/Audio. 它也是通过JNI与底层C打交道播放Audio/Video. 那么底层的实现才是决定MediaPlayer支持格式的关键。
例如:Sam使用Hi3716C来测试,发现其底层是用HiPlayer来实现,支持WAV,OGG等。
但MTK5502平台,则只支持WAV.(可能OGG没来的及加入)