直播技术(一) —— 直播间的上下切换(一)
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.07.30 |
前言
最近在两家公司任职做的都是直播相关的业务,音视频的门槛现在是越来越低,很多的三方视频云等都支持直播相关的业务,从推流拉流到美颜、滤镜、贴纸甚至人体识别等都有很多的第三方公司支持。但是,好一点的公司还是自己研发自己的视频云SDK,这样可以不受三方束缚,定制性也更好些,相信看过我写过的文章的人,发现了我写过和直播相关的技术,这里我另起一个模块和大家继续分享直播相关技术,感兴趣的就给个赞支持一下。
设计初衷
大家要明白一点,那就是为什么要在直播间里面实现上下切换?首先,和大家说一下,这个不是产品设计的必选项,但是一定是一个拉分项,为什么这么说?因为当我们不能上下切换直播间的时候,那我们怎么操作,无非就是点击X按钮退出直播间,在Feed流列表里面继续找想看的主播然后再进去,时间久了这样的操作就很繁琐了。
这个设计不是脑袋发热自己瞎想的,有很多主流直播APP都能在直播间内切换主播(上下或者左右切换直播间),比如花椒和抖音都是上下切换直播间。如下所示。
抖音短视频主页 花椒Feed流页面 花椒直播间设计实现
1. 视图层次问题
首先要面对的问题就是视图层次的问题,我们本来的播放器的根View上有一个视频层,视频层上面是一个礼物渲染层,在往上是其他几个视图,比如H5活动视图,上面是操作层容器视图,在操作层容器视图中是分享、关闭、送礼还有公屏等其他操作。
但是要加上直播间上下切换视图,那么就需要添加滚动控件,滚动的控件scrollView、UITableView、UICollectionView等都可以满足需求,但是从集成容易度等几个方面考虑,UICollectionView都是最佳的选项。
控件选好了,那么应该放在哪个层次上呢?这个就和产品业务需求相关了。这里给大家提供两个方法供大家选择。
-
另外实例化一个VC,在这个VC中放一个collectionview,然后将直播间的控制器VC添加掉这个collectionview的cell上,让cell持有一个播放器,这样滚动cell就切换了直播间。但是这个在实践上有问题,那就是cell复用带来的,cell复用会实例化三个播放器实例,在这三个播放器实例中循环复用,所以有点卡顿,效果不是很好。
-
就用一个播放器VC,在插入一个自定义view放在index=1的位置,这样除了根view,它就是在最底层的,然后在这个自定义view上放一个collectionview,并将直播间的容器视图add到collectionview的cell上,这样也可实现滚动。这个是经过实践证明可行的。
-
还有一个方法也是实例化一个VC,在这个VC中放一个collectionview,让这个VC持有一个播放器对象,这样我们在滑到对应的cell的时候,可以只用这一个播放器,那样,如果是第一个就实例化播放器并拉流,然后在滚动collectionview的时候找到对应的cell并将播放器根视图添加到cell上,停止上一个cell播放的内容并重新给播放器地址重新拉流。这个是我们采取的技术方案。
以上只是初步的视图层次的方案,其实还有很多数据源以及播放器等很多初始化的问题,下面我们继续。
2. 数据源问题
这里就看产品的需求了,是可以“无限”滚动,即使是第一个cell也可以向上滚动还是到第一个cell就不用滚动了,这个和产品需求相关了。
我们一般常见的情况就是如果数据源只有一个,那么就不要滚动了,如果数据源中对象个数大于1,那么就可以无限滚动,假如是index = 0的直播间向上滚动可以直接滚动到index = count - 1的直播间,这个如何实现呢?其实最简单的一个方法就是collectionView的数据源item的个数设置一个很大的数目,比如说10000个,然后将当前的直播间cell置于5000个的位置,那么就可以上下5000个滚动了,近似无限上下切换。
这里,还有个问题,就是直接用那个数组在直播间内进行切换,那么是否需要下拉下一页数据呢?这个是要看产品需求的,如果第一页数据拉的很多的话,比如100个数据,那么其实可以不用下拉下一页数据,很少有人一刷上下切换100个直播间的。这个是可选项不是必选项,具体依项目而定。
3. 播放器问题
由于VC持有了播放器,那么滚动cell的时候就需要重新给url进行拉流和播放,这样就可以保证进入直播间后只需要实例化一次播放器,当上下切换的时候可以直接给播放器一个新的Feed流重新拉流即可。
4. 消息问题
由于不同直播间的消息是不同的,所以快速滑动直播间进行切换的时候,其实很容易窜消息,这个应该如何处理呢?
其实,在我们切换直播间的时候,对于消息首先要做的就是将VC持有的消息数据清空。这样切换到另一个直播间后就不会引起消息的混乱和窜直播间。
其实有一个终极大招,那就是直播间消息返回roomId,这样我们切换直播间就可以用消息返回的roomId和当前Feed数据带的roomId进行比较是一个就使用,不是一个就扔掉。
5. 接口问题
这个也是需要考虑的一个点,前面说过我们控制器VC只实例化一次播放器,切换到别的直播间只更换Feed流并不重新实例化,那么这里就有一个问题,如果切换到下一个直播间,那么我们上一个直播间的进入房间拉取的一些接口才返回我们应该怎么办?
这个是很大的问题,因为我们发送出请求后,具体什么时候回来就不是我们控制的了,极大的概率就是我们都已经切换到下一个直播间了,但是上一个直播间接口数据才返回。
这个有下面几个方案,可以选择:
-
可以每切换一个直播间都重新实例化一个对象去请求,这样我们就算上一个直播间的请求结果在切换直播间后才返回我们就不用介意了,因为它可以返回,但是我们可以不用,只需要在新的直播间重新实例化一个干净的对象去重新请求即可,这个是我们Android端的方案。
-
很多时候由于我们是复用的播放器对象,有时候请求写的还是单例,不能重新实例化去请求数据。这样我们可以这么做那就是每一个请求都给个标记identify,在返回结果的地方,如果该task的任务和标记的是同一个那么就说明是我们这个直播间想要的请求,如果不是一个就说明可能是上一个直播间的请求才回来,我们直接舍弃即可。
另外,一个方案可以参考消息带roomId的机制,这个用于判断直播间是最好的。
6. 手势问题
这个也是需要处理的问题,由于我选择了collectionviewCell嵌套播放器VC的这种设计,所以设计到很多手势的交互。比如说:公屏信息是大家聊天的地方,有时候我们喜欢看历史消息,这个时候就要注意了,不能滑动公屏这个tableview让后面的collectionview发生滚动。但是,很不幸的是这个问题是存在的,当我们将tableview一直拖动到底部的时候,系统就会将手势传递给后面的视图进行响应,这就引起了后面的collectionview发生了滚动切换,很明显这个不是我们希望的。
同样,礼物面板、关注面板、私聊面板以及其他视图等都需要处理。不能让他们也跟着滚到别的播放器里面。
这里一个可行方案就是自定义collectionview并重写其hitTest
方法,判断下一个响应者,如果是上面涉及到的那几个控件,那么就将collectionview的滚动enabled = NO,后面的collectionview就不会滚动了,这样点击或者拖动其他地方都可以切换直播间,操作上面那几个视图就没问题了。
类似的代码如下所示:
// JJSwitchCollectionView就是自定义的负责滚动的视图,继承自UICollectionView
@implementation JJSwitchCollectionView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL canBeScrolled = YES;
UIView *view = [super hitTest:point withEvent:event];
UIResponder *debugResponder = (UIResponder *)[super hitTest:point withEvent:event];
// JJPlayingSwitchCell就是JJSwitchCollectionView的cell
while (debugResponder != nil && ![debugResponder.nextResponder isKindOfClass:[JJPlayingSwitchCell class]]) {
// 这里JJChatTableView就是公屏的tableView
if ([debugResponder isKindOfClass:[JJChatTableView class]]) {
canBeScrolled = NO;
break;
}
debugResponder = debugResponder.nextResponder;
}
self.scrollEnabled = canBeScrolled;
return view;
}
@end
7. 礼物渲染问题
这个需要注意我们渲染大礼物时间比较长或者有人连续送小礼物的时候,这样切换直播间的之后就会将渲染的礼物带到下一个直播间内部。
对于这种情况我们需要:
- 清空待渲染的数组对象。
- 将当前正在渲染的图层都停掉并移除。
后记
本篇主要就讲述了切换直播间的一些设计及问题的解决,感兴趣的给个赞或者关注~~~~