音视频播放过程当中的问题解决(播放质量优化)

1、概述

本文主要介绍音视频播放过程当中的一些问题,以及针对具体问题的优化方法。前端

2、异常状况及缘由简介

  • 卡顿,上行链路或者下行链路网络带宽不足播放,设备性能不足,视频流时间戳问题
  • 花屏,有可能出现整幅画面的模糊或马赛克,sps/pps参数设置错误,或者P帧丢失或解码失败致使局部画面花屏。
  • 绿屏,sps/pps获取失败或错误,没法渲染的画面有些用黑色填充,有些用绿色填充,有些用上一帧画面填充。视频参数改变, 而解码端的SPS&PPS信息未及时从新获取更新,会致使画面没法正常渲染,继而致使绿屏的现象出现
  • 跳播,体现为播放过程当中音频或视频的不连续——>即时间戳的不连续。主要因为缓冲管理、音画同步、一些主动的丢帧策略或性能瓶颈形成。
  • 拖拽无效,seek的位置不合法,seek位置计算错误(进度条错误、时间戳错误或者整个视频文件时长错误)
  • 拖拽偏差过大,GOP太大
  • 黑屏,有声音但画面为黑屏。
  • 切码率异常(无痕切换),通常表现为没法渲染,或花屏,或跳播,或黑屏之类的。其实都是在不一样码流切换时一些信息设置错误/没有重置。
  • 断流,除了设置断流阈值,超过阈值重连,没想到啥好办法。但重连的实现过程有许多细节要处理。
  • 音画不一样步,同步算法处理不当,致使音频时间戳和视频时间戳差值过大。但也有物理意义上的音画不一样步,如音频源距离麦克风太远,而音速也比光速慢不少(离麦克风别太远)。或者采集过程当中的音频处理模块耗时过长(没啥办法)。
  • 声音异常,好比变声,噪声,回声,声音不连续这些现象。首先除了噪声抑制、回声消除、自动增益、重采样等这些模块的处理不当,还可能有采集或播放的编解码、渲染参数错误问题。
  • 播放失败,缘由有不少。分发流中入口错误、播放后端接口返回错误、业务层建立播放container失败、建立播放器错误、流状态错误、播放url无效、DNS解析错误,以上都属于业务(第三方)相关的错误。播放层面可能致使播放失败的错误包括CDN节点链接失败、读packet错误、解码错误、播放端网络中断、流服务器宕机或负载太高、源流中断等。咱们在实际工业场景中通常认为阈值时间内能够恢复的错误,不算播放失败。
  • 首帧时间长,播放器初始化时间,后端接口耗时,dns解析耗时,cdn节点调度,metadata解析时间,解码器初始化时间,播放队列开始往渲染器送数据的缓冲阈值,都会致使首帧时间过长。
  • 延迟大,主要是采集端编码耗时、上行传输链路耗时(若是是tcp会有ack和重传机制,耗时更多)、转码耗时、分发到cdn节点耗时、下行链路耗时。
  • 累积延迟,通常指上行或下行采用基于TCP协议的流媒体协议传输,因为网络抖动以及TCP的重传机制,会致使延迟不断累加增大。
  • 手机发热,采集端较为常见,播放端码率较高时也会常见。其实是CPU的使用率太高,或内存使用率太高,全景和VR场景也有可能显卡使用率太高。好比缓冲设计不合理、频繁复制数据、算法时间复杂度太高、编解码过于依赖CPU计算、渲染数据量过大、渲染过程当中的转换计算量过大、编码算法与机器配置不匹配、有一些数据精度太高等。
  • 录制视频播放异常,通常是录制时音视频编码参数设置错误,或者时间戳异常。
  • 播放闪屏,频繁出现稳定画面和融合后的画面的切换,给用户一种画面抖动的感受。

3、优化方法

针对每一种异常状况或优化点,逐个解决及优化:android

1. 卡顿

  • 上行优化,解决源流的卡顿。好比推流端设备性能配置优化、推流端性能优化、硬编、推流边缘CDN节点就近选择。
  • 抖动缓冲,在卡顿和延迟中作平衡。
  • 下载链路优化,选择更近更优的CDN边缘节点。
  • 针对播放严重卡顿的用户,或CDN负载太高的状况,直播后端下发消息,强制客户端降码率。客户端自己在检测到持续卡顿后也能够强制降码率。
  • 播放端采用硬解,优化解码性能。
  • 底层预估网络变化,从新创建与CDN的链接,并没有痕切换(链路切换)。
  • TCP协议和UDP协议的挑选和结合。TCP协议可靠,但在网络抖动时会大量重传,形成更严重的拥塞。UDP协议对网络抖动的适应更好,但可能会有包乱序和包丢弃的问题
  • 可变码率,如转码平台和播放端能够支持真正的码率自适应流,应重点考虑。不然也能够采用折中方案,客户端预估网络变化,播放器底层创建与CDN低清码流的链接,获取低清晰度数据并解码,提供给上层渲染(假的分层编码实现方式),待网络恢复后又切换回源码流。这样不用从协议或转码、解码方式上进行大改,只是有可能加大CDN负载,播放SDK的下载和解码、同步逻辑会较为复杂。
  • 分层编码,直播场景下实现难度较大,但也值得考虑,在点播播放时应重点考虑。主要是对转码和解码算法的要求不一样。
  • 插入假数据,好比插入少许平稳的音频帧,以及在画面变化较小时插入帧。实现难度较大,首先对执行插帧的阈值须要及时判断,不然会形成体验不好,或来不及插帧,须要考虑时间戳的修改及音画同步,判断在哪些点能够插音频帧,哪些点能够插视频帧,在下载链路恢复正常后如何切换,再次校准时间戳。这部分能够参考回声消除时,远端信号与近端信号不一样步时,如何为远端信号插帧。
  • 时间戳错乱的问题(好比视频时间戳回退),这种状况下若是直接按照严格的音画同步策略,可能会直接丢弃一些视频帧不进行渲染,这样画面就暂停(卡顿)了,直到时间戳恢复正常。这时候能够增大缓冲区,调整时间戳为单调递增。

2. 花屏

  • 码率太低致使的马赛克,除了给当前分辨率匹配合适的码率,没别的办法
  • 显卡性能瓶颈,优化渲染模块的代码逻辑,好比全景和VR之类的播放,能够局部更新
  • 获取sps/pps解码信息失败或者错误,对于可伸缩编码或多码流的状况下,若是分辨率等数据变化但未及时刷新,就可能花屏(有的机型表现为绿屏)。解码信息的修改主要来源于转码服务,(好比在flv中,默认只有刚开始播放时下载的video header中才带有sps/pps信息,中途修改了编码信息,默认并不会再次发送video header),感受服务器若是只是转封装而不是转码,很容易有这样的问题,若是服务器作了转码,能够在源流切换解码信息时加入AVCDecorderConfigurationRecord这种帧。
  • 部分参考帧丢失,有多是采集端/播放端存在丢帧策略,并且丢弃的帧被参考,也有多是流媒体服务器的接收缓冲区SO_SNDBUF过小,网络抖动恢复时接收的一些帧被丢弃,且这些帧被参考,致使局部画面花屏。在UDP场景下,固然也有多是传输的不可靠性致使丢包。
  • 解码器初始化参数错误或处理逻辑错误,致使解码失败,且解码失败的帧被参考,推流端编码器初始化参数错误也会致使编码异常。
  • 有些android机型的硬编硬解兼容性不够好,当编码/解码异常时未检测出来,这个只能根据历史经验设置黑名单
  • 给渲染模块传递的参数不是实际参数,这个是bug了
  • 拖拽或从新设置解码器参数时,没有清空解码队列

3. 绿屏

  • 获取sps/pps解码信息失败或者错误,就可能绿屏。(同花屏)
  • 硬解时ios videotoolbox没法解析被切分为多个NALU单元的帧,由于ios硬解码器认为这样的帧是不完整的。播放端能够在送入videotoolbox前将本帧的多个NALU中的data(具体参考H264中nalu格式)所有复制到AVPacket中,再塞给videotoolbox。注意:当采集端开启mutli-slice会致使源流中部分视频帧切分为多个slice,存储在不一样的NALU中,因此有这个问题

4. 跳播

  • 服务端缓冲管理和性能瓶颈致使,缓冲已满、流媒体服务器来不及处理缓冲中的数据,就会丢弃部分源流中的数据,致使播放端出现跳播的体验。若是是视频或直播回放录制过程当中硬盘读写跟不上CPU处理产生的数据,也有可能会直接丢弃部分帧,致使跳播。
  • 播放端CPU占用太高,好比使用率太高或访问频率太高,出现性能瓶颈,可能直接丢弃部分数据。
  • 音画同步致使,若是出现时间戳回退,播放器默认会直接丢弃回退过程当中的帧。或音频和视频时间戳差别过大,为了同步参考时钟,也可能丢弃部分帧。
  • 主动丢帧策略致使,为了平衡编解码/网络传输/播放速度/延迟采起丢帧策略,可能致使跳播,好比连续丢弃有关键声音信号的音频帧,或者连续丢弃视频帧。最为明显的是丢弃GOP时。
  • UDP传输不可靠致使乱序和丢包,若是外部参考时钟对应的帧已经到了,就会丢弃中间的帧。能够开发UTCP的机制,参考TCP作验证包序,以及像SRT那样客户端主动发送NACK请求重传

5. 拖拽无效

ffmpeg中seek的实现能够参考最后一部分中的文件how to seek in mp4/mkv/ts/flv。 【注:我的经验在没有关键帧列表的flv中拖拽会很慢很慢很慢。】一般seek是由用户拖动前端显示的进度条,播放器上层计算出seekTime=(进度条位置/总进度条长度)*视频总时长,而后传递到播放内核。若是seekTime不在实际的视频总时长内,就会致使seek无效,没有bug通常不会有这问题。向前seek时,因为数据未缓冲,若是下载速度过慢,某个时间段内一直没有缓冲到数据,也有可能出发播放器内部的重启机制,将本次seek取消。ios

6. 拖拽偏差过大

seek的原理是,找seekTime位置最近的I帧。GOP=n*fps,这个n值越大,seekTime距离实际定位点越远(平均来讲)。【注意seek操做后要清空解码缓冲队列】
若是是视频总时长较长,那么精准seek的需求不是很迫切,只要GOP别极其大就行。若是视频总时长较小,或者是画面切换很频繁的场景(好比户外运动),那么可能须要精准seek。
能够先定位到seekTime的前一个I帧,解码I帧及其后的数据,直到seekTime的时间戳所属的帧出现,才放入播放队列。(这样可能从体验上会感受seek耗时过长,但通常还好,由于这种场景GOP也不用设特别大)web

7. 黑屏

  • 源流没有图像,多是推流端采集图像失败或者编码失败,能够在编码模块增长检测逻辑。
  • 持续解码失败,播放端有可能对特定的格式不支持,这是须要完善回调机制,通知上层解码失败。底层能够自行尝试重置解码器,或从新创建链接。
  • 部分推流端编码和封装格式不标准,这也是源流问题。
  • 推流端业务层切换纯音频推流/音视频推流的模式,若是一开始是纯音频流,后来切成音视频流,由于播放器默认不会从新初始化解码器,则会致使播放端黑屏。能够在播放器内部增长是否有新的帧格式出现的探测,加入播放过程当中从新初始化解码器的逻辑。或者服务端下发通知。

8. 切码率异常

通常表现为没法渲染,或花屏,或跳播,或黑屏之类的。其实都是在不一样码流切换时一些信息设置错误/没有重置。算法

9. 断流

断流没法避免地会对播放体验形成影响,只能尽量快速恢复,并保证恢复后的正常播放后端

  • 断流检测的周期设置(针对源流中断或播放端下载链路等不一样状况)
  • 关闭原有stream链接,清除一些没必要要的数据,关闭avformat
  • 从新打开流、获取音视频解码数据
  • 重建stream链接并开始读取音视频数据
  • 此过程当中find_stream_info重置解码参数可能致使恢复后倍速播放等,最好保持原解码参数不变。具体原理未知 todo

10. 音画不一样步

参考音画同步原理及实现性能优化

11. 声音异常

  • 音频预处理部分的工做,可使用webrtc的音频处理模块,比speex效果要好不少,sdk体积的增长也能够接受。参考连接: 噪声抑制 回声消除 混音 静音检测
  • 采集或播放的编解码、采样率、渲染参数错误问题。工程实现上须要注意这些问题的处理。
  • 跳播致使的声音不连续,参考跳播的处理。
  • 丢帧致使的声音不连续或变调,主要是丢帧策略的调整不当致使。好比丢帧频率很高,那么会有一顿一顿的体验,若是一次性丢弃不少时序相连的音频帧,会有跳播的体验,若是不丢音频只丢视频,声音可能变调,使用音频变速不变调算法如WSOLA。

12. 播放失败

  • 由于致使播放失败的缘由可能有不少,因此最最重要的是日志收集的完善。
  • 对于上层业务(第三方)相关的错误,经过日志分析推动相关方修改。
  • CDN节点链接失败,能够尝试一次DNS解析返回多个ip,客户端在某个ip重试失败后,使用其余ip创建链接
  • 读packet错误,若是不是AVERROR_EOF这种,通常都直接丢弃packet了但频率较高或连续的读packet错误会致使用户播放体验受严重影响,而导致用户在播放异常状态下退出直播间(这种咱们认为是播放失败),当读packet错误累计必定量时能够从新创建链接,防止没法恢复正常播放。出现AVERROR_EOF错误就直接重连。
  • 解码错误,通常的解码错误会体现为渲染异常,处理方式参考读packet错误
  • 播放端网络中断,其实和前面的读packet错误是部分重合,仍旧是有超时重连,加大超时时间,达到阈值就换链路重连的策略
  • 流服务器宕机或负载太高,日志上报只能起到过后分析的做用。主要是经过流服务自己的监控来实现整个集群的稳定性
  • 源流中断,有多是推流端异常关闭,或上行链路中断,能够在推流端作一些错误处理。

13. 首帧时间长

  • 预设播放格式,若是url中已经带有音视频的一些解码参数,能够预设iformat,提早开始解码
  • DNS预解析,能够由业务层在初始化播放器的同时,向调度服务器发送请求
  • CDN调度优化,下载链路优化
  • flv metadata解析简化,由于flv的audio和video tag中其实已经带有不少信息了,上层也能够在初始化播放器时预设一些信息,因此能够一边read_frame一边解析metadata,而无需等待。
  • 播放起始过程当中等待和同步的优化,好比减少packets buffer队列长度,prepare过程当中不进行任何等待(start-on-prepared),强制刷新

14. 延迟大(直播)

  • 采集端网络自适应,网络环境极其差时丢帧,设计要求高,须要考虑对源流数据连续性的影响。关于采集端和播放端的丢帧,其实能够单独写一篇。若是丢弃I帧,则需丢整个GOP,若是丢弃P帧,考虑GOP中靠后的P帧,B帧能够直接丢弃。注:网络直播app中的采集端通常不编码B帧。
  • 采集端编码性能优化及代码逻辑优化,减小编码延时,能够设置mutli slice
  • 采集端采用实时性更高的协议,好比RTP或自定义UDP
  • 采集端使用可变码率VBR,当画面变化较少或网络带宽不够时,下降码率,其余时候保持或升高码率
  • 转码平台CDN节点调度优化,尽量保证与采集端创建链接时间短,网络传输高效稳定
  • 转码平台转码性能优化,减小解码、再编码形成的延迟,拆分实时性和压缩比需求不一样的任务
  • CDN分发后下载节点调度优化,性能和负载优化
  • 播放端网络自适应,丢帧,相似采集端的丢帧策略。通常丢弃音频帧后,用视频帧与音频帧比对时间戳,再基于解码渲染正常的前提,抉择丢弃的P帧编号。若是是已知解码所需的分辨率等信息,那么能够考虑直接丢弃解码前的帧(解码前丢帧若是操做不当,会形成跳播和花屏/绿屏)
  • 播放端倍速播放,音频会变调,须要进行变速不变调的处理
  • 播放端解码性能的优化
  • 播放端抖动缓冲(实现与卡顿的平衡)
  • 播放端采用实时性更高的协议
  • 播放端采用可变码率
  • 播放端采用分层编码流

15. 累积延迟(直播)

  • 平滑丢帧和快速丢帧两种方式

16. 手机发热

目前记录到的:服务器

  • CPU使用率太高,主要是计算复杂度太高,数据精度过大,算法要求机器配置较高,软编的时候很常见,矩阵转换之类的操做多,全景的时候很常见
  • CPU使用频率太高,核数较少,线程切换极为频繁。在核少的机型上,有时候单线程解码会比多线程解码效率更高。
  • 内存使用率太高,缓冲队列设计不合理,频繁复制数据之类的
  • 显卡使用率太高,超清或全景/VR之类的会存在显卡使用率太高的状况,能够下降部分画面的刷新率

17. 录制视频播放异常

  • pts写入时校准
  • 一些关键的解码信息不能写错

18. 闪屏

这部分我没有遇到过,网上资料提到是指视频图像融合,或者播放器卡顿时中加入一些默认的图片,致使画面抖动(也就是闪屏)。连麦时的画面合成,或者加logo,或者播放队列插帧,都要注意平滑过渡。markdown

4、检测和监控

  • 播放端日志上报
  • 针对streamid实现从推流、转码、分发、下载、播放的全链路监控

5、参考

  1. 直播问题分析总结
  2. ffmpeg-how to seek in mp4/mkv/ts/flv
相关文章
相关标签/搜索