虚拟直播场景为元世界交际娱乐形式下的全新直播方式,由虚拟形象替代真人出镜,能够给用户打造不一样的直播体会,还能够加入表情随动、手势辨认触发特效等多种玩法,在场景里支撑多位虚拟形象视频连麦互动,更容易吸引证户参加连麦互动,提升用户的消费意愿及粘性。

即构虚拟形象引擎(Zego Avatar)支撑自定义办理人物的虚拟形象,经过默许的虚拟形象或许自定义生成的专有虚拟形象,以表情随动、声音驱动等方式与真人实时互动,可广泛应用于语聊直播、交际互动、在线训练等多种场景中。(关于虚拟形象Avatar的完成咱们下篇文章将进行具体描述)

本篇文章咱们将具体介绍下如何运用即构 SDK 完成虚拟直播间的建立流程

1、架构规划

虚拟直播场景的主要架构如下图所示(以多人连麦直播互动为例):

元宇宙场景技术实践|虚拟直播间搭建教程

虚拟直播场景架构规划图

2、体会APP源码

ZEGO 针对虚拟直播提供了体会 App 源码 (doc-zh.zego.im/article/156…) ,以供开发者进一步了解 ZEGO 虚拟直播计划。

开发前的准备作业

在开端正式的开发作业之前,需求先做好以下的准备作业:

  • 已在ZEGO 操控台 创立项目,并申请有效的 AppID 和 AppSign,详情请参阅操控台 – 项目办理中的“项目信息”;
  • 已在项目中集成 ZEGO Express SDK,详情请参阅 实时音视频 – 快速开端 – 集成 SDK
  • 已在项目中集成 ZEGO Avatar SDK,详情请参阅 Avatar 虚拟形象 – 快速开端 – 集成 SDK
  • 请联系 ZEGO 商务人员,提供申请到的 AppID,以及自己项目的 Bundle Identifier,并注册 Avatar 服务。

具体完成流程

一切准备就绪,首要介绍下虚拟直播场景的整体流程,能够做个初步的了解:

  1. 主播进入房间后,给 Avatar 设置虚拟形象,开端收集 Avatar 纹路内容,并进行预览并推流。
  2. 观众进入房间后,给 Avatar 设置虚拟形象,并进行拉流。
  3. 主播、观众均经过信令模块进行连接,信令模块能够操控当时事务房间内的直播流程,同步并告诉各端当时的直播状况。
  4. 无论是否有连麦观众, 主播和观众均经过 ZEGO 音视频云服务进行推拉流。
  5. 观众恳求与主播连麦后,信令模块会告诉主播,并同步连麦者的个人信息。
  6. 主播承受连麦申请后,连麦观众开端收集 Avatar 纹路内容并推流,房间内所有成员将会接收到流更新告诉,并拉取连麦观众的音视频流。
  7. 若连麦观众不再需求连麦,则向事务后台主张下麦恳求。收到信令模块的下麦告诉后,连麦观众中止推流、中止收集 Avatar 纹路内容、中止表情随动,主播和房间内的其他观众中止拉取该观众的流。

具体流程图如下:

元宇宙场景技术实践|虚拟直播间搭建教程

接下来咱们依照开发顺序,一步步完成咱们想要建立的虚拟直播间:

1、注册Avatar服务

请联系 ZEGO 商务人员为 AppID 注册Avatar服务。

2、初始化 Express Video SDK

在运用 Express Video SDK 进行视频通话之前,需求初始化 SDK。因为初始化操作 SDK 时,内部处理的操作较多,主张开发者在 App 启动的时分进行。

ZegoEngineProfile *profile = [ZegoEngineProfile new];
// 请经过官网注册获取,格局为:1234567890
profile.appID = appID; 
//请经过官网注册获取,格局为:@"0123456789012345678901234567890123456789012345678901234567890123"(共64个字符)
profile.appSign = appSign; 
//通用场景接入
profile.scenario = ZegoScenarioGeneral; 
// 创立引擎,并注册 self 为 eventHandler 回调。不需求注册回调的话,eventHandler 参数能够传 nil,后续可调用 "-setEventHandler:" 方法设置回调
[ZegoExpressEngine createEngineWithProfile:profile eventHandler:self];

在初始化 Express Video SDK 的时分需求注册 RTC 的自定义收集,Avatar 形象是经过自定义收集推送纹路。因为 Avatar 的数据是相反方向的,所以在初始化的时分需求设置镜像。

//设置 RTC 镜像 (Avatar 推送的镜像相反)
[engine setVideoMirrorMode:ZegoVideoMirrorModeBothMirror];
// 设置自定义收集推流
ZegoCustomVideoCaptureConfig *captureConfig = [[ZegoCustomVideoCaptureConfig alloc] init];
captureConfig.bufferType = ZegoVideoBufferTypeCVPixelBuffer;
[[ZegoExpressEngine sharedEngine] enableCustomVideoCapture:YES config:captureConfig channel:ZegoPublishChannelMain];
​
// 设置自定义收集回调
[[ZegoExpressEngine sharedEngine] setCustomVideoCaptureHandler:self];
float scaleScreen = [UIScreen mainScreen].nativeScale;
int captureWidth = ([ZGMetaLiveCurrentUser shared].user.userType == ZGMetaLiveUserTypeHost) ? 720 : 360;
int captureHeight = ([ZGMetaLiveCurrentUser shared].user.userType == ZGMetaLiveUserTypeHost) ? 1280  : 640;
// 装备Avatar收集画布尺寸
ZegoVideoConfig *videoConfig = [ZegoVideoConfig configWithPreset:([ZGMetaLiveCurrentUser shared].user.userType == ZGMetaLiveUserTypeHost) ? ZegoVideoConfigPreset720P :ZegoVideoConfigPreset360P];
videoConfig.encodeResolution = CGSizeMake(captureWidth, captureHeight );
[[ZegoExpressEngine sharedEngine] setVideoConfig:videoConfig];

更多初始化 Express Video SDK 的细节请参阅:实时音视频 – 快速开端 – 完成流程 的 “3.1 创立引擎”。

3、创立虚拟形象

在运用虚拟直播前,创立自己的个人形象。详情请参阅 创立虚拟形象。

4、登录直播房间

主播开端直播或观众观看直播前,需求先登录到直播房间。在收到登录房间成功的回调后,能够直接调用 Express Video SDK 的接口进行推拉流操作。

// 创立用户
ZegoUser *user = [ZegoUser userWithUserID:userID userName:userName];
// 设置为 YES 后才能承受 [onRoomUserUpdate] 回调
ZegoRoomConfig *config = [[ZegoRoomConfig alloc] init];
config.isUserStatusNotify = YES;
// 登录房间
[[ZegoExpressEngine sharedEngine] loginRoom:roomID user:user config:config];

更多运用 Express Video SDK 完成登录直播房间的细节请参阅:实时音视频 – 快速开端 – 完成视频通话 的 “3.2 登录房间”。

5、设置个人虚拟形象

初始化 ZegoCharacterHelper 类,设置现已创立的个人的虚拟形象,用于直播的个人形象展现。

_helper = [[ZegoCharacterHelper alloc] init:assetBundlesPath];
 NSString *packagePath = [bundlePath stringByAppendingString:@"/ios/Packages/"];  //Resource/ios
 [_helper setExtendPackagesPath:packagePath];
 [_helper setDefaultAvatar:((self.currentGender == ZegoGenderType_Female) ? MODEL_ID_FEMALEBODY : MODEL_ID_MALEBODY)];

6、单主播直播

6.1 获取 Avatar 的纹路内容

Avatar 的虚拟形象数据是经过 startCaptureAvatar 回调到上层经过自定义收集推送出去。因为 Avatar 数据是通明布景,RTC 是没布景的,转化的时分默许黑色,开发者能够自即将布景设置为需求的色彩。

//依据实践需求设置 Avatar 回来内容的宽(captureWidth)和高(captureHeight)
AvatarCaptureConfig* config = [[AvatarCaptureConfig alloc] initWithWidth:captureWidth height:captureHeight];
@weakify(self);//解决self循环引证
[self.helper startCaptureAvatar:config callback:^(unsigned long long texture, int width, int height) {
   @strongify(self);
   @autoreleasepool {
     [self setupBgColorWithTexture:(__bridge id<MTLTexture>)(void*)texture color:GoAvatarHexColor(colorStr) ];
   }
}];
// 设置推流布景色彩
- (void)setupBgColorWithTexture:(id<MTLTexture>)texture color:(UIColor*)color
{
   @weakify(self);
   [[MetalTools sharedInstance] setupBgColorWithTexture:texture 
                         bgColor:color 
                         callback:^(id<MTLTexture>  _Nonnull newTexture) {
     @strongify(self);
     [self sendCustomerBuffer:newTexture];
   }];
}
// 生成完整的 Avatar 纹路数据
- (void)sendCustomerBuffer:(id)newTexture{
   //推流
   @weakify(self);
   [self getPixelBufferFromBGRAMTLTexture:(__bridge id<MTLTexture>)(__bridge void*)newTexture result:^(CVPixelBufferRef pixelBuffer) {
     @strongify(self);
     //这里的格局是 BGRA,需求转化
     CMTime time = CMTimeMakeWithSeconds([[NSDate date] timeIntervalSince1970], 1000);
     CMSampleTimingInfo timingInfo = { kCMTimeInvalid, time, time };
​
     CMVideoFormatDescriptionRef desc;
     CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, &desc);
​
     CMSampleBufferRef sampleBuffer;
     CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, (CVImageBufferRef)pixelBuffer, desc, &timingInfo, &sampleBuffer);
     if ([self.captureHandler respondsToSelector:@selector(onAvatarCaptureDeviceDidCapturedData:)]) {
       [self.captureHandler onAvatarCaptureDeviceDidCapturedData:sampleBuffer];
     }
​
     CFRelease(sampleBuffer);
     CFRelease(desc);
   }];
}
// 把当时纹路数据转化成 pixelBuffer
- (void)getPixelBufferFromBGRAMTLTexture:(id<MTLTexture>)texture result:(void(^)(CVPixelBufferRef pixelBuffer))block {
​
   CVPixelBufferRef pxbuffer = NULL;
   NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber 
                             numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                           [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];
   unsigned long width  = texture.width;
   unsigned long height = texture.height;
   size_t imageByteCount = width * height * 4;
   void *imageBytes = malloc(imageByteCount);
   NSUInteger bytesPerRow = texture.width * 4;
   MTLRegion region = MTLRegionMake2D(0, 0, texture.width, texture.height);
   [texture getBytes:imageBytes bytesPerRow:bytesPerRow fromRegion:region mipmapLevel:0];
​
   CVPixelBufferCreateWithBytes(kCFAllocatorDefault,texture.width,texture.height,kCVPixelFormatType_32BGRA,imageBytes,bytesPerRow,NULL,NULL,(__bridge CFDictionaryRef)options,&pxbuffer);
​
   if (block) {
     block(pxbuffer);
   }
   CVPixelBufferRelease(pxbuffer);
   free(imageBytes);
}

6.2 主播开启预览并推流

主播向 ZEGO 音视频云服务推流,需求自己生成唯一的 StreamID,然后开端预览并推流。

// 依据 view 目标创立待烘托画布
ZegoCanvas *canvas = [ZegoCanvas canvasWithView:view];
// 设置烘托形式
canvas.viewMode = ZegoViewModeAspectFill;
// 开端在画布进行本地预览
[[ZegoExpressEngine sharedEngine] startPreview:canvas];
// 主播开端推流
[[ZegoExpressEngine sharedEngine] startPublishingStream:@"hostStreamID"];

更多运用 Express Video SDK 完成预览和推流的细节请参阅:实时音视频 – 快速开端 – 完成视频通话 的 “3.3 推流”。

6.3 观众拉流

观众进入房间后,会收到 Express Video SDK 的流更新告诉,从中筛选出主播流的 StreamID 进行拉流。

// 观众拉主播流
// 依据 view 目标创立待烘托画布
ZegoCanvas *canvas = [ZegoCanvas canvasWithView:view];
// 设置烘托形式
canvas.viewMode = ZegoViewModeAspectFill;
// 开端拉流并在画布进行烘托
[[ZegoExpressEngine sharedEngine] startPlayingStream:@"audienceStreamID" canvas:canvas];

更多运用 SDK 完成拉流的细节请参阅:快速开端 – 完成流程 的 “3.4 拉流”。

7、观众连麦

7.1 连麦观众推流

观众调用事务后台恳求连麦接口,调用成功后,事务后台向主播发送恳求连麦自定义信令。主播收到信令后,调用事务后台赞同连麦接口,调用成功后,事务后台向房间内所有成员发送连麦成功的播送信令,连麦观众收到信令后,开端推流,观众上台后也是依照 6.1 获取 Avatar 的纹路内容 的流程,把 Avatar 的内容经过自定义收集推流出去。

// 连麦观众推流
[[ZegoExpressEngine sharedEngine] startPublishingStream:@"audienceStreamID"];

7.2 主播拉流

连麦观众推流后,房间内所有成员会收到 Express Video SDK 的流更新告诉,主播获取连麦观众流的 StreamID 进行拉流。

房间内其他观众也在收到流更新回调时,获取连麦观众流的 StreamID 进行拉流。

// 主播拉连麦观众流 
// 依据 view 目标创立待烘托画布
ZegoCanvas *canvas = [ZegoCanvas canvasWithView:view];
// 设置烘托形式
canvas.viewMode = ZegoViewModeAspectFill;
// 开端拉流并在画布进行烘托
[[ZegoExpressEngine sharedEngine] startPlayingStream:@"audienceStreamID" canvas:canvas];

7.3 连麦观众下麦

连麦观众调用事务后台的下麦接口,调用成功后,事务后台向房间内所有成员发送该观众下麦的播送信令。连麦观众收到信令后中止推流、中止收集获取 Avatar 纹路内容、中止表情随动检测,房间内其他观众收到信令后中止拉流。

// 观众中止预览
[[ZegoExpressEngine sharedEngine] stopPreview];
// 观众完毕推流
[[ZegoExpressEngine sharedEngine] stopPublishingStream];
// 房间内其他成员完毕拉流 
[[ZegoExpressEngine sharedEngine] stopPlayingStream:@"audienceStreamID"];
// 中止收集获取 Avatar
[[GoAvatarManager shareInstance] stopCaptureAvatar]

更多运用 Express Video SDK 完成中止推拉流的细节请参阅:实时音视频 – 快速开端 – 完成流程的 “4.2 中止推拉流”。

进阶功用

1、真人和虚拟形象切换

真人和虚拟形象的切换功用,主要是会体会不同视角效果。“真人”就是惯例意义上的视频画,“虚拟形象”则是不同人模形象的展现。

  • 设置 RTC 视频收集源当开发者需求从 Avatar 虚拟形象切换到真人形象或许从真人形象切换到 Avatar 虚拟形象的时分,均需求调用 Express SDK 的callExperimentalAPI设置 RTC 视频收集源。
static int const VIDEO_SRC_CAMERA  = 2;        // 摄像头视频源,在该场景中特指真人画面
static int const VIDEO_SRC_EXTERNAL_CAPTURE  = 3;   // 外部视频源,在该场景中特指虚拟形象
NSDictionary *param = @{
   @"method":@"express.video.set_video_source",
   @"params":@{
     // source 表示 RTC 视频收集源;
     // VIDEO_SRC_CAMERA 为真人形象;
     @"source":@(VIDEO_SRC_CAMERA),
     // VIDEO_SRC_EXTERNAL_CAPTURE 为 Avatar 虚拟形象
     // @"source":@(VIDEO_SRC_EXTERNAL_CAPTURE),
     @"channel":@(ZegoPublishChannelMain),
   },
};
[[ZegoExpressEngine sharedEngine] callExperimentalAPI:[param modelToJSONString]]
  • 中止或许开启收集 Avatar 纹路内容从 Avatar 虚拟形象切换到真人形象时,需求调用 stopCaptureAvatar 中止收集 Avatar 纹路内容。
// 中止收集 Avatar 纹路内容
[[GoAvatarManager shareInstance] stopCaptureAvatar];
​
// 开端收集 Avatar 纹路内容
//[[GoAvatarManager shareInstance] startCaptureAvatar];

从真人形象切换到 Avatar 虚拟形象时,需求调用 startCaptureAvatar 开端收集 Avatar 纹路内容,详情请参阅 本篇文章 6.1 获取 Avatar 的纹路内容

2、实时音讯互动

ZEGO 支撑在虚拟直播中加入实时音讯互动功用,实时展现房间内的音讯,例如发音讯、进退房提示、互动告诉等。为便当开发者快速完成此功用,体会 App 源码 提供了相关组件,有关在您的项目中接入该组件的流程,请参阅 实时音讯互动 – 组件接入。

Demo展现

用虚拟形象替代真人出镜,打造不一样的互动体会,以上就是关于即构虚拟直播间的完成流程,感兴趣的小伙伴能够着手测验建立,下方是能够出现的 demo 截图:

元宇宙场景技术实践|虚拟直播间搭建教程

元宇宙场景技术实践|虚拟直播间搭建教程