关于视频播放的一些总结
以前做过一些视频播放器相关的功能,当时从零开始学习,后来项目结束就放下了,时间一长都忘了,整理一下备忘吧。
生命周期
Android 平台自带了播放器 Mediaplayer,使用比较方便,只需要了解下它的生命周期,就能应对些基本的功能了。
(时间太长,忘了当时从哪学的了,只留下这张图片)
注意几点:
- 如果运行过程中,出现了IllegalStateException异常,那就说明调用某个方法的时候状态不对,需要检查流程。
- 通过reset()进入 Idle state 后,如果调用错误的方法,会触发 Error state。而如果是刚 new完,就不会触发。
- 如果调用release进入End state了,就真end了,想再使用只能重新new。
- 注意prepare 和 prepareAsync 的区别,异步的需要等待 callback。
- 进入 Error state 后,只能通过 reset 来重置为 Idle state。
- Looping 代表是否循环播放。
容器与压缩标准
平常所见的那种.rm .rmvb .mkv .avi 视频文件都是多媒体容器文件格式。
所谓容器就是将不同的多媒体数据流(多条音频流,字幕流和视频流)联合起来加到一个文件(载体)里面,多媒体容器 (Multimedia Container)也称为多媒体封装格式,它只是为多媒体编码提供了一个“外壳”。
与之不同的是H.264、H.265、DivX、 MP3这类叫做编码格式,也称为压缩标准。
多媒体容器文件格式 |
---|
文件头部分(压缩标准,规范信息) |
索引部分 |
多媒体数据部分 |
文件头部分 说明了多媒体数据符合的压缩标准及规范信息,常见的多媒体数据的压缩标准有:MPEG系列,H.26x系列;多媒体数据符合的规范信息可以包括视频的分辨率、帧率,音频采样率等。
索引部分 多媒体数据通常被分成若干块不连续存储,需要建立存储位置索引
多媒体数据部分 就是经过压缩的多媒体数据,包括视频数据、音频数据、文本数据等。
ijkplayer
之前实现功能用的框架就是ijkplayer,感谢小破站的开源。
这里放一张图,当时在网上搜的,基本说明了播放视频的完整流程,很有用:
要在工程里使用 ijkplayer 的话,可以直接在 gradle 里引用 dependencies;也可以自己下载源码编译。推荐自己编译吧,可以在编译的时候裁剪一些功能,选择注重轻量级或者兼容性。
集成之后的使用比较简单,下面放上一段示例代码:
private void initPlayer(){
mediaPlayer = new IjkMediaPlayer();
try {
if (videoPath != null){
mediaPlayer.setDataSource(videoPath);
}else if (videoURI != null){
mediaPlayer.setDataSource(this,videoURI);
}else{
return;
}
} catch (IOException e) {
e.printStackTrace();
return;
}
IjkMediaPlayer ijkMediaPlayer = (IjkMediaPlayer)mediaPlayer;
ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_WARN);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", Setting.getInstance().getDecodeType());
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV16);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
mediaPlayer.setDisplay(surfaceView.getHolder());
mediaPlayer.setOnPreparedListener(listener);
mediaPlayer.setOnBufferingUpdateListener(listener);
mediaPlayer.setOnCompletionListener(listener);
mediaPlayer.setOnErrorListener(listener);
mediaPlayer.setOnInfoListener(listener);
mediaPlayer.setOnSeekCompleteListener(listener);
mediaPlayer.prepareAsync();
}
完整的演示程序可以访问:https://gitee.com/aslgd/Pplay
Ffmpeg结构体
ijkplayer 也是基于 ffmpeg,这里是一些关键的结构体:
a) 解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)
b) 解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。
c) 解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。
d) 存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧
解码前数据:AVPacket
解码后数据:AVFrame