tencent cloud

文档反馈

最后更新时间:2023-09-27 14:44:45
    实时方案中需要在开唱后实时同步歌曲进度,避免因歌曲误差而增加端到端延迟。同步歌曲需要基于 NTP 时间,不同设备的本地时钟并不一致,存在一定误差,因此需要引入腾讯云自研 NTP 服务。同时,中途加入合唱的用户也需要同步歌曲进度,只有同步进度后,才能参与合唱。

    实现流程

    歌曲同步的做法为:主唱用户约定在未来某个时间点(如延迟 N 秒后)开始播放歌曲,其他用户参与合唱。各端的时间都以 NTP 时间为准,NTP 时间会在 TRTC SDK 初始化后开始同步。
    
    
    
    具体流程如下:
    1. 各端进行 NTP 校时,向 TRTC 云端更新并获取最新的 NTP 时间 T。
    2. 主唱端发送合唱信令(自定义消息),约定合唱开始时间 T2。
    3. 本地根据 T2 预加载歌曲,定时播放。
    4. 其他合唱用户接收到合唱信令后执行步骤 3。
    5. 过程中对本地歌曲播放进度进行校验,当 TE 与 TC 差值超过 50ms 即 seek 校准。
    说明:
    这里的 50ms 误差是个典型值,可以根据业务容忍度适当调整,建议在 50ms 上下浮动。

    时序图

    
    
    
    歌曲同步时序主要可以分为三个部分:NTP 校时、发送及接收合唱信令、歌曲播放进度修正。下面将针对这四部分给出具体的代码实现。

    关键代码实现

    1. NTP 校时服务

    //进房时调用NTP校时接口
    [TXLiveBase updateNetworkTime];
    
    // TXLiveBaseDelegate 回调内判断是否校时成功
    - (void)onUpdateNetworkTime:(int)errCode message:(NSString *)errMsg {
    // errCode 0: 校时成功且偏差在30ms以内;1: 校时成功但偏差可能在30ms以上;-1: 校时失败
    if (errCode == 0) {
    // 调用TXLivebase的getNetworkTimestamp即可获取NTP时间戳
    NSInteger ntpTime = [TXLiveBase getNetworkTimestamp];
    } else {
    // 重新调用updateNetworkTime启动一次校时
    [TXLiveBase updateNetworkTime];
    }
    }

    2. 主唱端发送合唱信令

    NSDictionary *json = @{
    @"cmd": @"startChorus",
    // 约定时间合唱
    @"startPlayMusicTS": @(startTs),
    @"musicId": @"musicId",
    @"musicDuration": @(musicDuration),
    };
    NSString *jsonString = [self jsonStringFrom:json];
    [trtcCloud sendCustomMessage:jsonString reliable:NO];
    说明:
    建议主唱以固定时间频率循环向房间内发送合唱信令消息,以便合唱者可以中途加入合唱;
    不使用 SEI 消息发送合唱信令的原因: SEI 信息会插入到视频帧中,导致观众侧拉取的视频流中携带很多无效信息。

    3. 合唱端接收合唱信令

    - (void)onRecvCustomCmdMsgUserId:(NSString *)userId cmdID:(NSInteger)cmdId seq:(UInt32)seq message:(NSData *)message {
    
    NSString *msg = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding];
    NSError *error;
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:[msg dataUsingEncoding:NSUTF8StringEncoding]
    options:NSJSONReadingMutableContainers
    error:&error];
    
    NSObject *cmdObj = [json objectForKey:@"cmd"];
    
    NSInteger musicDuration = [[json objectForKey:@"musicDuration"] integerValue];
    
    NSString *cmd = (NSString *)cmdObj;
    
    // 合唱命令
    if ([cmd isEqualToString:@"startChorus"]) {
    // 合唱开始时间
    NSObject *startPlayMusicTsObj = [json objectForKey:@"startPlayMusicTS"];
    NSString *musicId = [json objectForKey:@"musicId"];
    
    NSInteger startPlayMusicTs = ((NSNumber *)startPlayMusicTsObj).longLongValue;
    // 合唱约定时间和当前时间差值
    NSInteger startDelayMS = labs(startPlayMusicTs - [TXLiveBase getNetworkTimestamp]);
    // 开启预加载,根据合唱约定时间和当前ntp差值,跳跃歌曲进度
    NSDictionary *jsonDict = @{
    @"api": @"preloadMusic",
    @"params": @{
    @"musicId": @(musicId),
    @"path": path,
    @"startTimeMS": @(startDelayMS),
    }
    };
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0 error:NULL];
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    [subCloud callExperimentalAPI:jsonString];
    
    // 播放歌曲
    TXAudioMusicParam *param = [[TXAudioMusicParam alloc] init];
    param.ID = musicId;
    param.path = url;
    param.loopCount = 0;
    param.publish = NO;
    
    [[subCloud getAudioEffectManager] startPlayMusic:param onStart:^(NSInteger errCode) {
    // 开始播放回调
    } onProgress:^(NSInteger progressMs, NSInteger durationMs) {
    // 歌曲进度回调
    } onComplete:^(NSInteger errCode) {
    // 播放完成回调
    }];
    
    }
    }
    说明:
    合唱端接收到第一个 startChorus 信令后,状态应从“未合唱”转为“合唱中”,此后不再响应 startChorus 信令,避免重复启动 BGM 播放。

    4. 歌曲播放进度修正

    self.startPlayChorusMusicTs; //最初约定的合唱时间
    // 当前播放进度
    NSInteger currentProgress = [[self audioEffecManager] getMusicCurrentPosInMS:self.currentPlayMusicID];
    // 当前歌曲的理想播放时间进度
    NSInteger estimatedProgress = [TXLiveBase getNetworkTimestamp] - self.startPlayChorusMusicTs;
    if (estimatedProgress >= 0 && labs(currentProgress - estimatedProgress) > 50) {
    // 当播放进度超过50ms,进行修正
    [[subCloud getAudioEffectManager] seekMusicToPosInMS:self.currentPlayMusicID pts:estimatedProgress];
    }
    
    联系我们

    联系我们,为您的业务提供专属服务。

    技术支持

    如果你想寻求进一步的帮助,通过工单与我们进行联络。我们提供7x24的工单服务。

    7x24 电话支持