弱网丢帧策略:丢弃原始队列未编码的数据帧,丢弃编码队列的数据帧。
视频移动端播放内核技术;视频加载时长,秒播的体验.流畅度和清晰度都是用户最根本的诉求和最基础的产品体验。播放器的主要工作原理类似于编码的逆运算。
在做音频播放的时候,使用的是开源的ijkplayer播放器,ijkplayer解码使用的是ffmpeg,声音输出使用的是audiotrack,在某机型上面播放遇到锁屏、返回后台、点击home键的时候会出现声音卡顿的现象。
直播技术总结(三)ijkplayer的一些问题优化记录- https://blog.csdn.net/hejjunlin/article/details/57075026
视频直播技术(四):使用Ijkplayer播放直播视频- https://www.cnblogs.com/renhui/p/6420140.html
IJKPlayer问题集锦之不定时更新- https://www.jianshu.com/p/220b00d00deb
直播技术总结(三)ijkplayer的一些问题优化记录- http://blog.csdn.net/hejjunlin/article/details/57075026
- 视频播放中可能有的bug:
1.红蓝反色——YUV or YVU
2.黑屏、大花屏——SPS、PPS数据错误
3.绿屏——MX4,获取截屏宽未设置为32倍数
4.局部花屏——丢帧
5.不支持该手机——编码器参数
直播视频秒开及视频优化- https://blog.csdn.net/ShareUs/article/details/79115816
网易云音乐在视频播放优化上的实践- https://zhuanlan.zhihu.com/p/56086941
-- 视频的痛点与局限性
从最开始的MV,到后来的短视频,再到现在的直播,网易云音乐在视频业务上不断的进行尝试和扩展。
视频的优化从下面几个方面切入:首帧时间、带宽、播放错误、缓存、流畅性等。
首帧时间:用户从发起网络请求到视频第一帧渲染到屏幕上的时间,视频播放快慢最直观的指标。
带宽:不仅需要为用户节省流量,也需要考虑节省云音乐的带宽,毕竟都是真金白银买来的。
播放错误:播放错误对于用户体验的影响不言而喻,对用户活跃和用户留存都会产生不利影响。在降低播放错误率方面,我们也做了很多的努力。
缓存:缓存的主要目的也是提升播放的体验,能够让用户像播放本地视频一样播放在线视频,这个算是投入产出比比较高的一项优化了。
流畅性:既包含页面滑动的流畅性也包括播放的流畅性,尤其对于直播,流畅的体验显得尤为重要。
Android本身提供了MediaPlayer来播放媒体文件。通常只需要使用原生播放器,通过一些简单的接口调用就可以实现视频播放功能。但是,实践中我们发现原生播放器有很大的局限性。
从客观方面,原生播放器的兼容性比较差,由于Android的碎片化、不同厂商的ROM定制、芯片的差异等原因,播放失败、花屏、黑屏等问题层出不穷,并且当问题出现的时候,我们通常束手无策。另外,播放出错的时候,播放器提供的错误信息也不够明确,很多时候很难清楚的知道到底是因为什么原因导致的播放错误。因此,无论从播放问题的排查还是处理上,原生播放器都有其局限性。
主观方面来看,原生播放器的灵活性有限。播放器数据的请求是由播放器内部发起的,我们只是提供了一个url,而不能控制数据的请求过程。如果我们有特殊需求(比如需要网络请求走代理服务器),需要去控制数据请求过程,就比较麻烦了。另外,如果需要实现一些缓存相关的功能,比如视频的预加载、边播边存等功能,也是无法直接实现的。
mp4文件的组成。mp4文件主要由ftyp、mdat、moov三部分组成,ftyp记录编码格式之类的信息,moov则如同检索表一样,记录了每一帧对应的数据在哪里,mdat记录视频的数据信息。播放器在播放视频的时候需要先拿到ftyp和moov信息,才能继续往下解析视频数据,开始视频的播放。
使用自研播放器的目的之一,是解决播放错误问题。 即使使用了自研播放器,依然需要面临软解和硬解选择的问题。软解兼容性比较好,可以解决掉大部分我们开始提到的解码错误问题,但软解的解码效率比硬解要低,而且会占用CPU资源,可能会导致在性能比较差的机器上播放卡顿。硬解码直接使用GPU解码,解码效率高,但是兼容性较差,原生播放器就是因为用硬解才会有很多播放错误问题。
既然软解和硬解都不是完美的,那我们就需要有个策略来尽可能的利用两者的优势来降低播放错误。大部分情况下,低分辨率视频使用软解,机器的压力并不会很大,反倒是使用硬解的话,可能会存在兼容性问题;高分辨率的视频如果使用软解的话,有些性能比较差的手机可能会出现卡顿或音画不同步的现象。结合这些情况,在实际中,我们使用了这样的策略,大于等于720P的视频使用硬解,小于720P的视频使用软解。
-- 网络优化
对于播放体验和播放错误的改进,网络的优化也是不可或缺的一部分。
首先,域名解析的耗时本身会影响视频首帧时间,也可能会由于解析失败导致视频播放出错。因此,云音乐使用了HttpDns来代替默认的域名解析,并且使用了批量域名预取,预埋一些默认的域名地址,当域名解析出错时,可以通过默认ip访问网络。
其次,对于视频数据的请求,需要先建立tcp连接,为了能够避免tcp连接建立的时间消耗,底层网络库里启用了tcp连接复用,在域名收敛的情况下,可以通过复用tcp连接来减少视频请求的时间。从而提升视频首屏速度。
最后,弱网也是我们无法避开的问题。弱网环境会存在请求超时,针对这种情况,我们会尝试多次重连。而且在网络库底层,在超时的情况下,也会超时连接其他ip。另外,对于多分辨率视频的情况,如果明确知道网络比较差的话,可以尝试提示用户切换到低分辨率来播放。
-- 直播,首帧优化和滑动流畅性方面的一些策略。
1)多进程
对于视频播放器,通常的实现方式是在SurfaceView或TextureView中包含一个MediaPlayer,控制逻辑也都直接写在View中,而且通常一个View与一个MediaPlayer绑定。在此基础上,我们首先对播放器做了进程隔离,播放器放到单独进程,主进程和播放器进程通过binder进行通信,然后对播放器进行复用,多个View绑定到同一个Player,然后通过Manager进行统一管理和调度。这样做主要有以下优点:复用播放器,节省资源;播放器独立进程,减少主进程内存的占用;进程隔离,播放进程崩溃后可以通过主进程恢复。当然,这样需要增加一些额外的控制逻辑和程序的复杂度。
2)首帧优化
首帧时间,即从开始播放到视频第一帧渲染到屏幕上的时间。要对首帧耗时进行优化,我们需要搞清楚这其中包含哪些步骤。这个过程依次分为以下几步:
1. DNS解析,通常一个App内视频资源的域名是固定的一个或几个,所以只有第一次播放需要请求域名服务器解析域名,后面都只需要直接到缓存里面去取就可以了。
2. 域名解析完之后,首先要发起TCP连接,这个时间受网络状况影响。
3. 然后发起Http请求,等待Http数据的返回,这个也会受到网络情况的影响。
4. 数据返回之后,播放器首先要对视频格式进行探测,而且需要足够的数据量才能探测成功。
5. 格式探测完也是不能直接播放的,播放器需要拿到足够多的数据缓存才能开始播放视频,开始播放的缓存量有一个阈值是可以设置的。
6. 最后一步就是对视频数据的解码,这个耗时由播放器本身的性能决定。
通过上面的分析,首帧耗时主要由DNS耗时+TCP连接耗时+Http请求耗时+视频探测耗时+buffer缓存耗时+播放器解析耗时累加而成。其实,从整个过程来看,App层面可以做的并不多。我们考虑从其他角度来优化首帧时间。
云音乐的直播间采用的是上下滑动切换,可以考虑在滑动的时候提前启动数据的加载过程
首先,我们需要在打开直播间的时候就直接将直播地址的列表传进去,直播间切换的时候就可以直接开始播放,而不需要等待接口返回了。
其次,考虑将视频数据的请求时间尽可能的提前,就还需要一定程度的预加载。我们考虑设计一个播放器池,包含三个播放器实例A、B、C。当前使用播放器B播放视频,当直播间开始滑动,滑动距离达到一个阈值,就认为已经切换到了下一个房间,可以提前取下一个播放器A(或C)加载数据。等到滑动完全停止,B停止播放,A(或C)如果已经加载完数据进入prepared状态则可以直接播放,否则就继续数据的加载过程。这样就充分利用了滑动切换的这个时间来减少了视频首帧的渲染时间。
对两个App的播放器框架层进行统一,对上提供统一的接口封装,对下提供对播放器sdk的可插拔设计,对内实现多业务多场景的一致性的支持。最终目标是能够实现一个可复用、易扩展、功能强大的视频播放组件。播放体验的优化是一个持续探索的过程,我们后面也会在这方面做出更多的努力和尝试。后续云音乐也会在播放框架的统一、视频播数据的监控等方面做进一步的改进和优化。
> ijkplayer rtmp秒开
ijkplayer设置rtmp秒开,可以让rtmp加载时间从5~10秒缩短到1s以内,以达到秒开且低延迟的目的:
IjkMediaPlayer ijkMediaPlayer = null;
ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.setOption(1, "analyzemaxduration", 100L);
ijkMediaPlayer.setOption(1, "probesize", 10240L);
ijkMediaPlayer.setOption(1, "flush_packets", 1L);
ijkMediaPlayer.setOption(4, "packet-buffering", 0L);
ijkMediaPlayer.setOption(4, "framedrop", 1L);
-- ijkplayer实时, 播放rtmp等实时性要求很高的流媒体时候,会出现10S左右的延迟,原因是因为加了缓冲区处理,可以把其缓存设置变小,达到实时的效果:
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);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 8);
ijkMediaPlayer.setOption(1, "analyzemaxduration", 100L);
ijkMediaPlayer.setOption(1, "probesize", 10240L);
ijkMediaPlayer.setOption(1, "flush_packets", 1L);
ijkMediaPlayer.setOption(4, "packet-buffering", 0L);
ijkMediaPlayer.setOption(4, "framedrop", 1L);
-- 快速起直播流:
直播技术总结(五)如何快速起播直播流- http://blog.csdn.net/hejjunlin/article/details/72860470
这里优化后者,主要修改两个参数,一个是 probesize,一个是 analyzeduration,分别用来控制其读取的数据量大小和时长。减少 probesize 和 analyzeduration 可以降低avformat_find_stream_info 的函数耗时,达到起播快
ijkMediaPlayer.setOption(1,"analyzemaxduration",xxx);
ijkMediaPlayer.setOption(1,"probesize",xxx);
-- 卡顿优化和秒开,弱网优化
ijkplayer 解决rtmp 延迟长的问题,达到秒开的结果- https://blog.csdn.net/yyhjifeng/article/details/71191950
ijkplayer直播播放器使用经验之谈-卡顿优化和秒开实现- https://blog.csdn.net/cmshao/article/details/80149176
ijkplayer丢帧的处理方案- https://www.jianshu.com/p/ecf51ee32589
直播的延迟,如果延迟过大,可以采取两种策略,一种是丢帧,一种是追帧。我们可以考虑丢音频包来实现,音频包不不在关键帧的问题,丢起来比较好操作,然后因为视频同步到音频,所以视频会追帧,也会跟上来。
> 使用Ijkplayer倍速变调问题解决方案- https://www.cnblogs.com/renhui/p/6510872.html
public void setSpeed(float speed) {
_setPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, speed);
public float getSpeed(float speed) {
return _getPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, .0f);
}
> ijkPlayer中的错误码:
IJKMEDIA: SDL_JNI_DetachThreadEnv
ijkplayer如何断掉握手 Android ? ijkplayer如何释放连接 Android ?
int MEDIA_INFO_UNKNOWN = 1;//未知信息
int MEDIA_INFO_STARTED_AS_NEXT = 2;//播放下一条
int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频开始整备中,准备渲染
int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;//视频日志跟踪
int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲中 开始缓冲
int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
int MEDIA_INFO_NETWORK_BANDWIDTH = 703;//网络带宽,网速方面
int MEDIA_INFO_BAD_INTERLEAVING = 800;//
int MEDIA_INFO_NOT_SEEKABLE = 801;//不可设置播放位置,直播方面
int MEDIA_INFO_METADATA_UPDATE = 802;//
int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;//不支持字幕
int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;//字幕超时
int MEDIA_INFO_VIDEO_INTERRUPT= -10000;//数据连接中断,一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频方向改变,视频选择信息
int MEDIA_INFO_AUDIO_RENDERING_START = 10002;//音频开始整备中
int MEDIA_ERROR_SERVER_DIED = 100;//服务挂掉,视频中断,一般是视频源异常或者不支持的视频类型。
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
int MEDIA_ERROR_IO = -1004;//IO 错误
int MEDIA_ERROR_MALFORMED = -1007;
int MEDIA_ERROR_UNSUPPORTED = -1010;//数据不支持
int MEDIA_ERROR_TIMED_OUT = -110;//数据超时
Error (-10000,0)
> ijkplayer 使用经验:
1、IJKPlayer 不像系统播放器会给你旋转视频角度,所以你需要通过onInfo的what == IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED去获取角度,自己旋转画面;或者开启硬解硬解码,不过硬解码容易造成黑屏无声,诸位慎重啊O__O "…。
mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
2、 IJKPlayer 出现黑色有声音没图像,看看你的视频编码是不是H264,pixel format是否存在,音频编码是不是AAC?默认IJKPlayer是不支持3pg(支持它干啥(?-?*)?),不支持mepg(比如这个库RecordVideoDemo ),不支持AMR。所以如果你真的想要支持,那么参考这个#1961,打开mpeg支持,重新编ffmpeg,然后通过硬解码播放mpeg;或者通过系统的录制VideoRecord;或者选另外的JAVACV录制封装FFmpegRecorder。
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec_mpeg4", 1);
3、 快进和慢放接口,只支持API23以上,23以下的支持,是需要自己配置ffmpeg支持avfilter,不过可能会出现声音颤抖等问题,官方说不稳定,参考#1690。
Tryavfilter but the audio sounds shaking = = For avfilter, only support software decoder.
4、暂停的时候,退到后台再回到前台,画面黑了?这时候个人处理方式是,可以在暂停的时候,通过TextureView.getBitmap(point.x, point.y);获取到暂停的画面,用ImageView显示它,在onSurfaceTextureUpdated的时候隐藏ImageView,来实现画面的衔接。
5、一些视频返回码
int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频准备渲染
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲
int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频选择信息
int MEDIA_ERROR_SERVER_DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
int MEDIA_ERROR_IJK_PLAYER = -10000,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
MediaPlayer Error (-10000,0), 视频播放过程中出错了,可能是视频的原因,播放器内部错误
6、某些视频在SeekTo的时候,会跳回到拖动前的位置,这是因为视频的关键帧的问题,通俗一点就是FFMPEG不兼容,视频压缩过于厉害,seek只支持关键帧,出现这个情况就是原始的视频文件中i 帧比较少,播放器会在拖动的位置找最近的关键帧,目前么,IJKPlayer无解。
7、下载速度可以通过IjkMediaPlayer的getTcpSpeed获取。
8、高分辨率开启硬解码,不支持的话会自动切换到软解,就算开启mediacodec,如果设备不支持,显示的解码器也是avcodec软解。
9、ijkMediaPlayer.setOption可配置的对应头文件参考:ff_ffplay_options。
10、缓冲进度条不到100,官方表示我就不保证都100,所以一般我都是:
//95这个数值可能不准确,有些时候可能还需要低一些
if (secProgress > 95) secProgress = 100;
11、上面1、2、6的问题,在IJK封装的EXOPlayer和MediaPlayer都不会有问题,兼容上确实强过IJKPlayer,但是它们在细节上,却没有IJK处理的好,如EXOPlayer:退到后再回到前台、切换渲染控件的黑屏一段时间问题,除了用seekto之外目前没发现其他办法,这样的体验让我最后还是选择IJKPlayer。
12、设置cookie 可以通过ijkPlayer的public void setDataSource(String path, Map<String, String> headers) 的header实现设置,参考ijkPlayer的issues-1150,headers也是在内部被转化为何issuses一样的setOption方法。
> ijkplayer常见问题以及解决方案
1.ijkplayer播放rtmp直播流,延迟明显- https://github.com/Bilibili/ijkplayer/issues/210
2.全屏播放
3.有时候会开始直播时出现黑屏
4.有时候会出现花屏
5.解码方式设置
6.如何区分点播直播
7.是否需要开启硬件加速
8.How to set up only listen to the sound does not show video?- https://github.com/Bilibili/ijkplayer/issues/1074
9.如何设置后台播放
10.视频加载速度慢
The traffic speed is mostly depending on the quality of video CDN, not player itself.
11.怎么静音 和非静音
mute/unmute system volume.There is no mute/unmute API in ijkplayer.
12.视频黑屏,但是有声音
确定下视频源的编码方式,ijk默认只带了h264解码code
13.适配问题,对于不同的cpu架构,需要编译不同的so库
14.播放视频有的设备声画不同步
15.如何查看m3u8时长
cat game05.m3u8 | grep EXTINF | wc -l 32
16.how to change the video quality?
Video quality is determined when being encoded.I don’t think it can be changed by player.
17.倍速播放
Not until Android 6.0
18.为什么往前拖动进度条后,还会往后退几秒
seek只支持关键帧,出现这个情况就是原始的视频文件中i 帧比较少,播放器会在拖动的位置找最近的关键帧。
19.how to change URL when ijkplayer is playing RTMP video
Create new player.
20.怎样添加字幕呢?
如果希望字幕时间精确,可以在native层做解析和时间同步,到了时间后回调给java层,一般字幕文件加载都是在java层做的,解析文件格式,然后按照时间区间来显示。
21.如何设置硬解?
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, “mediacodec”, 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http-detect-range-support", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "analyzeduration", "2000000");
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "probsize", "4096");
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 0);
22https://blog.csdn.net/ShareUs/article/details//http重定向到rtmp/Https,ijkplayer无法播放视频
ijkMediaPlayer.setOption( IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_clear", 1);