直播推流/视频上传的优化

2,604 阅读9分钟

一、概述

本文主要介绍直播推流过程中可能存在的一些问题,以及可以尝试的优化方法。(可能也会提到视频上传的一些问题)这里我们不讨论整套方案怎么实现,就说问题。

流程图:

二、主要问题及其原因

直播推流中的问题,其实最后都是以播放端问题的形式,呈现给用户,参考播放问题及优化,但我们也列举一下因为采集端处理不当导致播放端异常的情况。

  • 推流失败:就是推流链路根本没成功建立,这个一般是业务方url生成规则的问题、推流鉴权服务未通过、推流节点不可用造成。
  • 音画不同步:编码后封装成packet时,需要进行音画同步的操作。具体可以参考音画同步的原理及实现至于音频源距离麦克风过远的情况了,软件上没想到什么办法计算固定的音频和视频时间差。
  • 声音异常:声音异常有可能是音频处理模块算法处理不够好,有可能是音频帧时间戳异常,有可能音频解码失败,有可能部分音频数据被丢弃,有可能音频渲染加速或减速导致。
  • 画面异常:画面异常有可能是格式不兼容、解码失败、部分数据丢弃、播放侧渲染分辨率不兼容等原因。
  • 延迟大:采集和推流过程中会产生延迟。采集端主要是预处理延迟和编码延迟,推流主要是DNS解析、连接建立、链路传输耗时、丢包重传导致的延迟。
  • 卡顿:一段时间内采集端没有数据包传输抵达推流服务器,源流卡顿导致播放端也同样卡顿。原因可能由硬件采集失败(概率低)、编码失败(概率低)、数据包发送失败(概率高)造成。
  • 断流:原来的推流链路断开了,有可能是链路不可用了,也有可能是用户主动断开连接,重连。
  • 手机发热:CPU/GPU及内存、显卡使用率过高,纯软编的话会造成CPU使用率很高,部分机型可以使用硬编。由于推流端会做预处理,这部分(尤其是图像预处理)会导致GPU(有时候也用CPU)占用率过高。推流端的渲染同样会造成显卡资源的消耗。
  • 机型适配:ios还好,android机型适配是个大问题,如果要做硬编的话就更坑了。主要就是采集格式、硬编模块、软硬解的支持,需要有一个测试过的白名单列表。
  • sdk体积太大:采集端涉及的逻辑很多,但可以跟播放sdk复用一些库,可以大大减小整体体积。
  • 录制文件保存失败:如保存为mp4文件,录制过程中出现异常退出(如app crash或断电、关机等),则文件会写入失败。

三、优化方法

1).推流失败

主要是业务层和服务端、客户端的各种错误,除了设计和代码上的问题需注意,也有一些错误兼容的方法。我们主要关注服务端请求失败时的交互、重试,此外还有推流地址不可用时的兼容(比如dns解析返回多个ip)。

2).音画不同步

编码后封装成packet时,需要进行音画同步的操作。具体可以参考音画同步的原理及实现。对于声源距离麦克风过远的情况,暂时没想到软件上有何方法可以计算出音视频帧固定的时间差。

3).声音异常

对采集后的声音进行预处理,包括NS/AEC/AGC。预处理使用的库常用的有speex和webrtc,speex一般都打包在ffmpeg里面了,不用增加额外体积,但效果一般,webrtc大约增加700K~1M体积,但效果很不错。此外,如果弱网条件下需要做丢帧操作,直接丢弃音频帧会有音频断断续续的情况,只丢视频不丢音频会导致音频变速变调。可以使用音频变速不变调的算法进行平滑丢帧,参考网络抖动适应中的音频变速/变调算法

4).画面异常

有些机型采集出来的画面就有异常,尤其注意对android camera api的调用。原始数据采集和存储格式不同,需要对采集的原始数据进行格式转换,此外图像预处理模块不要存在bug。有一些机型不支持某个格式的视频编码,如某些ios机型videotoolbox硬解时不支持H264 multi-slice,会有黑屏现象,所以推流端设置multi-slice时需要考虑该问题。同步及发送过程中,如果有延迟优化逻辑,需要注意不丢弃被参考的帧(否则应该将参考该帧的所有帧都丢弃),防止花屏现象。部分机型渲染时使用某些分辨率会有边缘绿屏现象,推流端设置分辨率时也可以关注这些因素。

5).延迟大

采集和推流过程中会产生延迟。

  • 采集端主要是预处理延迟和编码延迟。一般来说,音频预处理相对视频预处理消耗的时间短,但超低延迟的纯音频流场景时同样需要考虑音频预处理带来的延迟增加,图像处理SDK部分对性能要求较高,大部分都使用第三方SDK,但低端机型仍需考虑是否加入复杂的图像预处理功能。编码延迟主要是软编时会比较严重,软编相对硬编会有更高的压缩率,但消耗的CPU和时间会更多,当然比起推流的延迟,这都不算啥。
  • 推流主要是DNS解析、连接建立、链路传输耗时、丢包重传导致的延迟。DNS可以预解析,是起始延迟,这部分时间节省。连接建立和链路传输耗时也是起始延迟,这部分可以通过推流边缘节点调度、域名解析优化、开始推流前的多个节点rtt检测(这部分比较麻烦)来尽可能缩短。丢包重传是累积延迟,这部分有很多工作,建议增加独立的网络优化模块。传输层可以设置TCP_NODELAY,禁掉nagle算法(rtmpdump默认禁掉)、可以修改rtmp chunk size(rtmpdump客户端没有这个选项,需要自己增加,见参考文档2),应用层可以增加对rtmp_sendpacket状态的检测以及缓冲区缓存数据量的检测,当连续多次发送失败时尝试重连,在发送速度低于采集速度时平滑丢帧,当缓冲数据过多时可以考虑快速丢帧(一般不建议),此外可以修改分辨率、码率和帧率,以及其他编码参数来缓解网络抖动时的数据堆积(也就是延迟增加)。
  • rtmp推流场景延迟是个大问题,当然,如果能用UDP推流,或者QUIC,这样最好了。

6).卡顿

一段时间内采集端没有数据包传输抵达推流服务器,源流卡顿导致播放端也同样卡顿。。

个人感觉采集失败部分是机型适配的问题,编码失败更多是性能问题,而发送失败是网络问题(参考延迟部分的网络优化)。这些跟其他模块重合了。

7).断流

原来的推流链路断开了,有可能是链路不可用了,也有可能是用户主动断开连接,重连。

如果服务端缓冲不够,推流客户端断流肯定会造成整个拉流侧都卡顿,关键是两点——1.断流后如何尽快恢复连接(是发送端上行网络问题/推流节点资源受限/链路网络抖动?发送端是否需切换网络?是对同一个ip重新建连?还是请求推流服务分配新的推流节点?),此外对于发送端源流中断的情况,流服务是否有切备播文件或备播流的兼容方案?2.断流多长时间后,认为直播结束?

8).手机发热

CPU/GPU及内存、显卡使用率过高,纯软编的话会造成CPU使用率很高,部分机型可以使用硬编。由于推流端会做预处理,这部分(尤其是图像预处理)会导致GPU(有时候也用CPU)占用率过高。推流端的预览渲染同样会造成显卡资源的消耗。以上原因各个击破吧。

9).机型适配

ios还好,android机型适配是个大问题,如果要做硬编的话就更坑了。主要就是采集格式、硬编模块、软硬解的支持,需要有一个测试过的白名单列表。

10).sdk体积太大

跟播放sdk复用一些库,可以大大减小整体体积。

11).录制文件保存失败

如保存为mp4文件,录制过程中出现异常退出(如app crash或断电、关机等),则文件会写入失败。

录制文件分为两个环节,一是保存成一个本地文件,二是上传到网络。(相当于实现了简单的视频录制和上传)。对于本地文件写入失败的情况,可以在再次打开app时,使用之前默认的编码参数(或业务后台保存的录制编码参数记录)尝试解码已经写入的部分,然后生成一个moov box,最后写入moov;上传网络部分可以根据需求决定是否让用户再次上传。

【暂时写到这里,回头再来补坑】

四、参考