本此总结主要内容的有
1.视频播放器的原理
2.Andriod系统自带的Mediaplayer 及其 状态机
3.如何使用MediaPlayer播放音频与视频
4.视频播放器(VideoView和SurfaceView) 和 音乐播放器Demo
视音频技术主要包含以下几点:封装技术,视频压缩编码技术以及音频压缩编码技术。如果考虑到网络传输的话,还包括流媒体协议技术。
视频播放器播放一个互联网上的视频文件,需要经过以下几个步骤:解协议,解封装,解码视音频,视音频同步。如果播放本地文件则不需要解协议,为以下几个步骤:解封装,解码视音频,视音频同步。他们的过程如图所示。
(注意解协议步骤仅在播放网络视频时才包含,播放本地视频直接进行接封装)
解协议的作用,就是将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。
解封装的作用,就是将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。
解码的作用,就是将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。音频的压缩编码标准包含AAC,MP3,AC-3等等,视频的压缩编码标准则包含H.264,MPEG2,VC-1等等。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P,RGB等等;压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。
视音频同步的作用,就是根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。
Idle(闲置)状态与End(结束)状态
MediaPlayer 对象声明周期 : 从 Idle 到 End 状态就是 MediaPlayer 整个生命周期;
生命周期开始 : 进入 Idle (闲置) 状态;
生命周期结束 : 进入 End (结束) 状态;
Idle 和 End 状态转换 :
进入 Idle 状态 : new MediaPlayer() 或者 任何状态调用了 reset() 方法之后, 进入 Idle (闲置) 状态;
进入 End 状态 : 在 Idle 状态调用 release() 方法后, 会进入 End (结束) 状态(涉及到资源的释放),不能转换为其他状态;
注意:create()初始化的MediaPlayer直接进入Prepared状态
Error(错误)状态
Error状态转换:
进入Error状态:检测到异常,系统回调onError()进入Error状态
离开Error状态:可以使用reset()回到Idle状态
注册监听 : 注册一个 OnErrorListener 监听器重写OnError(), 用于获取 播放器引擎 内部发生的错误;
注册方法 : 调用 MediaPlayer.setOnErrorListener(OnErrorListener) 方法, 注册 OnErrorListener;
Initialized(初始化)状态
Initialized 状态转换 : 在 Idle 状态调用 setDataSource() 方法, MediaPlayer 会迁移到 Initialized 状态;
注意 : 只能在 Idle 状态调用该方法, 如果在其它状态调用该方法, 会报出 IllegalStateException 异常;
Prepared(就绪)和Preparing(准备中)状态
Prepared状态转移(两种方式)
Initialized状态 调用 prepared()进入Prepared状态 (同步操作,若数据量较大则容易造成主线程阻塞甚至ANR)
Initialized状态 调用prepareAsync()进入Preparing状态,注册OnPreparedListener.OnPrepared(),在将准备就绪后的操作放置OnPrepared()中(异步操作,便于操纵数据量大的情况,避免主线程阻塞)
Started(开始)状态
Started状态转移:
Prepared状态调用start()进入Started状态
判断MediaPlayer是否在Started状态:isPlaying():boolean
跟踪缓冲状态 : 在 Started 状态, 调用 OnBufferingUpdateListener.onBufferingUpdate() 方法, 可以获取视频音频流的缓冲状态;
Paused(暂停)状态
Paused状态转移:
Started状态调用paused()进入Paused状态
Paused状态调用start()进入Started状态
Stop状态
Stop状态转移
在 Prepared, Started, Paused, PlaybackCompleted 状态下 调用 stop() 方法, MediaPlayer 会迁移到 Stopped 状态;
注意Stop状态不能直接start(),要回到prepared状态(prepare()或prepareAsyn()),才能start
播放位置调整seekTo()
在 Prepared, Started, Paused, PlaybackCompleted 状态下 调用 stop() 方法, MediaPlayer 会迁移到 Stopped 状态;
seekTo() 方法说明 : 该方法异步, 调用后 播放器引擎还需要进行其它操作, 跳转才能完成;
进行的操作 : 播放器引擎会回调 OnSeekComplete.onSeekComplete()方法, 该方法通过 setOnSeekCompleteListener() 方法注册;
获取播放位置 : 调用 getCurrentPosition() 方法, 可以获取当前播放的位置, 可以帮助播放器更新进度条;
PlaybackCompleted (播放完毕) 状态
PlaybackCompleted 状态转移 : 如果设置了循环模式SetLooping(), 那么播放完毕之后会重新进入Started状态;若没设置循环,则调用 OnCompletion.onCompletion() 回调方法, MediaPlayer 会进入 PlaybackCompleted 状态;
也可以在该状态直接调用start()进入Started状态
MediaPlayer的工作流程是这样的:
1,首先创建MediaPlaer对象; *
2,然后调用setDataSource()方法来设置音频文件的路径;**
3,再调用prepare()方法使MediaPlayer进入到准备状态;
4,调用start方法就可以播放音频。
(1)创建MediaPlaer对象有两种方式
a 直接new出来
MediaPlayer mp = new MediaPlayer();
b 使用create方式
MediaPlayer mp = MediaPlayer.create(this, R.raw.test); //这时就不用调用setDataSource了
当然上面首先得在res文件夹下新建raw文件夹,并放置一个test文件
(2) 设置播放的文件
MediaPlayer要播放的文件主要包括3个来源:
a. 用户在应用中事先自带的resource资源
例如:MediaPlayer.create(this, R.raw.test);
b. 存储在SD卡或其他文件路径下的媒体文件
例如:mp.setDataSource("/sdcard/test.mp3");
c. 网络上的媒体文件
例如:mp.setDataSource(“http://www.citynorth.cn/music/confucius.mp3”);
MediaPlayer的setDataSource一共四个方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
其中使用FileDescriptor时,需要将文件放到与res文件夹平级的assets文件夹里,然后使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd(“rain.mp3”);
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
来设置datasource
(3) prepare()
(4) start()
VideoView是包装过的MediaPlayer,所以使用起来很相似
1.new一个MediaPlayer
2.只是调用setVideoPath()去设置视频文件路径(非setDataSource)
3.然后prepare
4.再start
1.一个SurfaceView组件去加载视频画面
2.为SurfaceView.getHolder()添加回调函数
@Override
public void surfaceCreated(SurfaceHolder holder) {
mPlayer.setDisplay(mSurfaceView.getHolder()); //将我们要程序视频的那个surfaceView的holder
//设置(set)到我们的MediaPlayer中
}
3.setDataSource()
4.prepare()
5.start()
https://github.com/WanZhiZheng/MediaPlayerTest
参考博客:
http://blog.csdn.net/shulianghan/article/details/38487967
https://blog.csdn.net/leixiaohua1020/article/details/18893769