iOS AI视频互动课的实现

2,480 阅读4分钟

前言

今年疫情原因,学校好长时间都不开学,本来在线教育就很火,这下更是火山爆发,线上教育APP多如牛毛,成长更是惊人。

成人的教育视频依然是常规视频,没有太大变化,可是主打低龄儿童市场的视频教学,为了让小朋友感兴趣,确是玩出了很多花样,比如 斑马AI课,小火花思维等主打视频互动课,比如 洪恩识字 直接是个游戏。

视频互动课 直接颠覆了我对视频交互的认知,在看到它之前,我接触过的视频交互,也紧紧是 在视频上发个弹幕,抖音点个赞。视频互动课在我们开发者看,就是跳出视频进行一些操作,完成在回到视频。给常人或小朋友们的感觉是 我在和视频中的人物在交互。一般套路是播放一个动画,动画中的人物需要做一些事情,需要小朋友们帮助,小朋友们需要完成一些小游戏,在小游戏中学习了知识,或是播放一段音乐,让小朋友跟着唱跳。这个产品设计真的太棒了。小朋友真的粘性超高,父母也乐意,终于能消停会了,还能学习。每天看一节视频课在加上课后练习的游戏和别的互动也就20分钟至半个小时。对小朋友眼睛损伤并不大。我来看下视频互动课的录制

banmaGIF.gif

IMG_1494.PNG 首先看到 视频下的进度条上有4个 时间点图标,代表着 这4个事件。可见在视频开始时或之前 就已经知道了时间点的位置。而每节课的时间点是不一样的,所以我们设计进度条上的图标,是根据时间点的数据而确定位置的。

接下来 仿着视频互动课 ,自己试着实现

使用的播放器 是ZFPlayer 先创建 节点数据模型

@interface ZBGameModel : NSObject
@property (nonatomic,assign)NSInteger seconds;//游戏时间
@property (nonatomic,assign)NSInteger gameId;//游戏id
@property (nonatomic,assign)NSInteger gameType;//游戏类型 1为游戏  
@end

给模型赋值对应的时间和类型

   ZBGameModel *model1=[[ZBGameModel alloc]init];
    model1.gameId=1001;
    model1.gameType=1;
    model1.seconds=100;
    
    ZBGameModel *model2=[[ZBGameModel alloc]init];
    model2.gameId=1002;
    model2.gameType=1;
    model2.seconds=221;
    
    ZBGameModel *model3=[[ZBGameModel alloc]init];
    model3.gameId=1003;
    model3.gameType=1;
    model3.seconds=303;
    
    ZBGameModel *model4=[[ZBGameModel alloc]init];
    model4.gameId=1004;
    model4.gameType=2;
    model4.seconds=318;

    [self.timeArray addObject:model1];
    [self.timeArray addObject:model2];
    [self.timeArray addObject:model3];
    [self.timeArray addObject:model4];

根据上图我们可以看到 对号标志 代表游戏,最后一个音乐标志代表儿歌时间。可是这个图标,我们怎么算位置坐标呢。

下面就要靠计算了, (进度条的总长度/视频的总时间)* 对应的时间点 - 图标的宽度的一半
得到的就是 每个图标的位置了

  ///当播放器准备开始播放时候调用
    self.player.playerReadyToPlay = ^(id<ZFPlayerMediaPlayback>  _Nonnull asset, NSURL * _Nonnull assetURL) {
         @strongify(self)
        [self.timeArray enumerateObjectsUsingBlock:^(ZBGameModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
            UIImageView *icon=[[UIImageView alloc]init];
            if (model.gameType==1) {
                icon.image=[UIImage imageNamed:@"gift_icon_0"];
            }else{
                icon.image=[UIImage imageNamed:@"douyin"];
            }
            icon.backgroundColor=[UIColor whiteColor];
            [self.controlView.landScapeControlView.slider addSubview:icon];
           [icon mas_makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(@(0));
                make.width.equalTo(@(14));
                make.height.equalTo(@(14));
                //7为宽的一半
                make.left.equalTo(@(model.seconds*(self.controlView.landScapeControlView.slider.frame.size.width/self.playerManager.totalTime)-7));
            }];
        }];
    };

ED11C40C-9B24-4799-9C31-3197AB0F0FA8.png 因为只是 demo,我并没自定义 视频控制层和视频互动课一样,大家应该能看到,图标和原图基本一致 图标指示UI 层面的,那么 到了游戏图标的时间我们怎么能够进入游戏 呢 创建一个 游戏队列类 把我们刚才的 数据传进去

 self.gameQueue=[[ZBGameQueue alloc]init];
 self.gameQueue.delegate=self;
 [self.gameQueue loadGameList:self.timeArray]

里面是这样的,把模型数据存在字典里,以节点时间为key

- (void)loadGameList:(NSArray *)gameList{
    if (gameList.count<=0) {
        return;
    }
   for (int i = 0; i < gameList.count; i ++) {
       ZBGameModel *gameModel = gameList[i];
       [self.cacheDict setObject:gameModel forKey:@(gameModel.seconds).stringValue];
   }
}

在视频时间回调内 游戏时间 和 视频时间进行匹配,找到对应的时间点,证明可以进入游戏了

__block NSInteger tempTime;
    self.player.playerPlayTimeChanged = ^(id<ZFPlayerMediaPlayback>  _Nonnull asset, NSTimeInterval currentTime, NSTimeInterval duration) {
        @strongify(self)
        //因为此回调是0.1秒一次,所以做了此判断,
        tempTime=currentTime;
        if (self.currentTime!=tempTime) {
            NSLog(@"tempTime:%ld",tempTime);
            //游戏列队 和 视频时间对比
            [self.gameQueue startQueueWithCurrentTime:tempTime];
        }
        self.currentTime=tempTime;
    };
- (void)startQueueWithCurrentTime:(NSInteger)currentTime{
    if ([self.cacheDict.allKeys containsObject:@(currentTime).stringValue]) {
        ZBGameModel *gameModel =[self.cacheDict objectForKey:@(currentTime).stringValue];
        if ([self.delegate respondsToSelector:@selector(gameQueueGetGameModel:)]) {
            [self.delegate gameQueueGetGameModel:gameModel];
        }
     }
}

看到这,如果之前看过我写的 历史弹幕绑定视频时间的朋友,应该了解。逻辑都一样。 之后就是 代理触发,我们就可以进入游戏了

#pragma mark - ZBGameQueueDelegate
- (void)gameQueueGetGameModel:(ZBGameModel *)gameModel{
    if (gameModel.gameType==1) { //1为游戏
        [self.playerManager pause];//暂停视频
         NSLog(@"进入游戏");
        GameViewController *gameVC=[[GameViewController alloc]init];
        gameVC.delegate=self;
        UIViewController *vc=[self.controlView.landScapeControlView findeCurrentViewController];
        [vc presentViewController:gameVC animated:YES completion:nil];
    }else{
        //其他不跳转 比如儿歌时间
    }
}

游戏部分,我只是随便写了一点,三个游戏节点,都是一个。一节课 视频里3-4个游戏,加上课后练习的游戏10个 起码有了。一期有10多节课,每个年龄段的课还不一样,游戏也不一样。游戏的量这么大,感觉用原生开发不太可能。 最后在游戏结束时 给一个回调,游戏结束继续播放视频

#pragma mark - GameViewControllerDelegate
- (void)gameComplete{
    [self.playerManager play];
}

经过上面的处理,服务下发的或传进来的任何时间点,任何数量的数据,我们的UI图标 和 事件都能准确的触发

我们来个看下整体效果

banma zb GIF.gif

最后奉上本文Demo

结尾:水平有限,代码也很烂,一直在努力学习中,大家多多包涵。如果你喜欢这个轮子,请给个star,这是对作者最大的鼓励和支持,拜谢!!!假如你有更好的想法或方案请留言!