作者:vivo 互联网服务器团队-Li Guolin
一、前言
二、推拉流模型
三、搭建步骤
搭建直播服务器 使用OBS进行推流 直播流如何观看 直播间消息的实现
docker run -p 1935:1935 -p 7001:7001 -p 7002:7002 -p 8090:8090 -d gwuhaolin/livego
8090:HTTP 管理访问监听地址
1935:RTMP 服务监听地址
7001:HTTP-FLV 服务监听地址
7002:HLS 服务监听地址
{
"status": 200,
"data": "rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk"
}
CBR(Constant Bitrate)恒定码率,一定时间范围内比特率基本保持恒定。使用该模式时,在视频动态画面较多的场景下,图像质量会变差,而在静态画面较多的场景下,图像质量又会变好。
VBR(Variable Bitrate)可变码率,其码率可以随着图像的复杂程度的不同而变化。使用该模式时,在图像内容比较简单的场景下,分配较少的码率,而在图像内容复杂的场景下,则分配较多的码率。这样既保证了质量,又兼顾到带宽限制,优先考虑到图像质量。
ABR(Average Bitrate)平均比特率,是VBR的一种插值参数。简单场景分配较低码率,复杂场景分配足够码率,这一点类似VBR。同时,一定时间内平均码率又接近设置的目标码率,这一点又类似CBR。可以认为ABR是CBR和VBR的折中方案。
CRF(Constant Rate Factor)恒定码率系数。CRF值可以理解为对视频的清晰度和流畅度期望的一个固定输出值,即无论是在复杂场景还是在简单场景下,都希望有一个稳定的主观视频质量。
I帧(intra coded picture):最完整的画面,自带全部信息,无需参考其他帧即可解码,每个GOP都是以I帧开始; P帧(predictive coded picture):帧间预测编码帧,需要参考前面的I帧或P帧,才能进行解码,压缩率较高; B帧(bipredictive coded picture):双向预测编码帧,以前帧后帧作为参考帧,压缩率最高。
RTMP
HLS
HTTP-FLV
long time = new Date().getTime();
try {
// redis中插入消息数据
jedisTemplate.zadd(V_UNIQUE_ROOM_ID, time, JSON.toJSONString(roomMessage));
// 按照概率性的去删除redis中过期的消息数据
if (probability()) {
deleteOverTimeCache(V_UNIQUE_ROOM_ID);
}
} catch (Exception e) {
log.error("message save error", e);
}
private void deleteOverTimeCache(String roomId) {
Long totalCount = jedisTemplate.zcard(roomId);
log.info("deleteOldTimeCache size is {}", totalCount);
if (totalCount < 600) {
return;
}
// 倒序删除过期数据
Set
tuples = jedisTemplate.zrangeWithScores(roomId, -601, -1);
if (CollectionUtils.isNotEmpty(tuples)) {
for (Tuple tuple : tuples) {
// 这是第一个-600条的那个score
double score = tuple.getScore();
jedisTemplate.zremrangeByScore(roomId, 0d, score);
break;
}
}
}
@Override
public RoomMessage queryRoomMessages(MessageMessageReq messageMessageReq) {
RoomMessage result = new RoomMessage();
long timestamp = messageMessageReq.getTimestamp();
Set
tuples = ; if (timestamp == 0) {
// 如果传递是0,说明这个客户端终端是第一次来轮询,我们只要返回一个最近最新的消息返回即可
tuples = jedisTemplate.zrevrangeWithScores(UNIQUE_ROOM_ID, 0, 0);
} else
// 加上一毫秒,返回后续的消息,每次返回5个,防止客户端因为低端手机原因,过多的消息渲染不出来
tuples = jedisTemplate.zrangeByScoreWithScores(UNIQUE_ROOM_ID, timestamp + 1, System.currentTimeMillis(), 0, 5);
}
List
eachRoomMessages = new ArrayList<>(); long lastTimestamp = 0L;
if (!CollectionUtils.isEmpty(tuples)) {
for (Tuple tuple : tuples) {
//最后一次循环后,会把最后一条消息产生的时间戳,返回给客户端,这样下次客户端就可以拿着这个时间戳来进行查询
lastTimestamp = new Double(tuple.getScore()).longValue();
eachRoomMessages.add(JSON.parseObject(tuple.getElement(), EachRoomMessage.class));
}
}
result.setTimestamp(lastTimestamp);
result.setEachRoomMessages(eachRoomMessages);
return result;
}
四、小结
技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。