智慧服务,成就美好体验 项目咨询

主页 > 服务与支持 > 开发平台 > 客户端SDK参考 > iOS Native SDK > 会议 桌面协同与共享

入门使用

桌面协同与共享

更新时间:2019-11-20

开始和结束屏幕共享

说明: 

iOS 屏幕共享要求操作系统 iOS 12.0 及以上版本,使用功能时,需要启动系统的屏幕录制功能,因此需要配置相关工程,详见会议中屏幕共享进行配置。

描述

会议中,主讲人和与会者可以进行屏幕共享。

前提条件:

  1. 加入数据会议成功。
  2. 加载屏幕/程序共享模块成功。

业务流程

主讲人共享屏幕

图1 主讲人共享屏幕流程 
  1. 主讲人选择共享本端屏幕,UI调用tsdk_app_share_set_owner()接口设置自己为共享权限拥有者。
    说明: 

    SDK并未对接口tsdk_app_share_set_owner的调用者进行与角色限制,但实际应用场景中,UI应该仅对主讲人才提供主动“共享屏幕”入口。

    代码示例:
    //oc code 
    - (BOOL)inviteDataShareWithNumber:(NSString *)number
    {    
        TSDK_RESULT result = tsdk_app_share_set_owner(_confHandle, (TSDK_CHAR*)[number UTF8String], TSDK_E_CONF_AS_ACTION_ADD);        
        return result;
    }
     
  2. 主讲人侧和其他与会者侧SDK向UI上报共享权限拥有者变更通知消息TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新共享者信息。
    说明: 

    TSDK_E_CONF_EVT_AS_OWNER_CHANGE通知中携带的共享类型为TSDK_E_CONF_AS_ACTION_ADD,并且共享者ID为自己,调起系统屏幕录制界面,并选择当前程序然后点击“开始录制”。

    代码示例:
    //oc code
    - (void)startReplayKitBroadcast {
        dispatch_async(dispatch_get_main_queue(), ^{
            DDLogInfo(@"enter startReplayKitBroadcast ");
            if (@available(iOS 12, *)) {
                NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
                BOOL isNeedSetpreferredExtension = [userDefaults boolForKey:@"ScreenShareFlag"];
                NSString *mainBundleId = [[NSBundle mainBundle]bundleIdentifier];
                NSString *extensionId = [mainBundleId stringByAppendingString:@".ScreenShareExtension"];
                if (isNeedSetpreferredExtension) {
                    self.broadcastPickerView.preferredExtension = extensionId;
                }
                self.broadcastPickerView.showsMicrophoneButton = NO;
    
                for (UIView *view in self.broadcastPickerView.subviews) {
                    if ([view isKindOfClass:[UIButton class]]) {
                        [(UIButton *)view sendActionsForControlEvents:UIControlEventTouchDown];
                    }
                }
            }
        });
    }
     
  3. UI调用tsdk_app_share_set_virtual_view_info()接口,设置虚拟显示器信息。
    说明: 

    1、在一个全局的对象的初始化方法中,调用initScreenShareManager初始化screenSharemanage对象;

    2、当点击“开始录制”后,会上报系统录屏状态,当状态state == 1时,且自己已经加入数据会议中,开始调用设置虚拟显示器信息接口tsdk_app_share_set_virtual_view_info(),如果不在数据会议中,则通知系统关闭屏幕录制功能。

    3、录制开始后,系统会不停的上报屏幕数据上来,调用processImage()方法,将图片数据的相关信息转换后传给服务器。

    //oc code
    - (void)initScreenShareManager {
        DDLogInfo(@"enter initScreenShareManager ");
        NSString *appGroup = @"group.eSpaceMclientV2";
        self.screenShareManager = [[ScreenShareManager alloc]initWithAppGroupIdentifier:appGroup];
        [self.screenShareManager listenForMessageWithIdentifier:@"screenshare" listener:^(id messageObject) {
    
            NSDictionary *dir = [messageObject valueForKey:@"screendata"];
            [self processImage:dir];
        }];
        [self.screenShareManager listenForMessageWithIdentifier:@"screenShareStateChange" listener:^(id messageObject) {
            long state = [[messageObject valueForKey:@"state"] longValue];
            if (state == 1) {
                if (self.isJoinDataConfSuccess) {
                    self.mIsScreenSharing = YES;
                    [self startDataConfAsPre];
                    [self startDataShare];
    
                } else {
                    NSError *error = [[NSError alloc] initWithDomain:@"ScreenShare" code:-1 userInfo:@{NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"the meeting is unavailable", nil)}];
                    [self.screenShareManager passMessageObject:@{@"result" : error} identifier:@"StopBroadcast"];
                }
    
            } else if (state == 0) {
                [self confStopReplayKitBroadcast];
            }
        }];
    }
    
    - (void) startDataConfAsPre {
        DDLogInfo(@"enter startDataConfAsPre _mIsScreenSharing: %d _mWidthPixels: %d _mHeightPixels: %d " , self.mIsScreenSharing, self.mWidthPixels , self.mHeightPixels);
        self.mIsStartScreenShareDelayed = NO;
        if (self.mWidthPixels == 0 || self.mHeightPixels == 0) {
            self.mIsStartScreenShareDelayed = YES;
            return;
        }
        TSDK_S_CONF_AS_VIRTUAL_VIEW_INFO virtual_view_info;
        memset(&virtual_view_info, 0, sizeof(TSDK_S_CONF_AS_VIRTUAL_VIEW_INFO));
        virtual_view_info.width = _mWidthPixels;
        virtual_view_info.height = _mHeightPixels;
        virtual_view_info.bit_count = 32;
    
        tsdk_app_share_set_virtual_view_info(_confHandle, &virtual_view_info);
    }
    
    -(void)processImage:(NSDictionary *)dictionary {
        unsigned int width = [[dictionary objectForKey:@"width"] unsignedIntValue];
        unsigned int height = [[dictionary objectForKey:@"height"] unsignedIntValue];
        long orientation = [[dictionary objectForKey:@"orientation"] longValue];
        if (!self.mIsScreenSharing) {
            if (orientation == UIImageOrientationLeft
                || orientation == UIImageOrientationRight
                || orientation == UIImageOrientationLeftMirrored
                || orientation == UIImageOrientationRightMirrored) {
                self.mWidthPixels = height;
                self.mHeightPixels = width;
            } else {
                self.mWidthPixels = width;
                self.mHeightPixels = height;
            }
            if (self.mIsStartScreenShareDelayed) {
                [self startDataConfAsPre];
            }
            return;
        }
        unsigned int yPitch = [[dictionary objectForKey:@"yPitch"] unsignedIntValue];
        unsigned int cbCrPitch = [[dictionary objectForKey:@"yPitch"] unsignedIntValue];
        NSData *yData = [dictionary objectForKey:@"yData"];
        NSData *uvData = [dictionary objectForKey:@"uvData"];
        uint8_t *yBuffer = (unsigned char *)[yData bytes];
        uint8_t *cbCrBuffer = (unsigned char *)[uvData bytes];
    
        TSDK_S_CONF_AS_VIEW_DATA_INFO asViewUpdataData;
        memset(&asViewUpdataData, 0, sizeof(TSDK_S_CONF_AS_VIEW_DATA_INFO));
        asViewUpdataData.y_data = yBuffer;
        asViewUpdataData.cb_cr_data = cbCrBuffer;
        asViewUpdataData.y_data_size = yPitch;
        asViewUpdataData.cb_cr_data_size = cbCrPitch;
        asViewUpdataData.width = width;
        asViewUpdataData.height = height;
        asViewUpdataData.orientation = (TSDK_E_IMAGE_ORIENTATION)orientation;
        tsdk_app_share_update_view_data(_confHandle, &asViewUpdataData);
    }
     
  4. UI调用tsdk_app_share_start()接口开始共享屏幕。
    代码示例:
    //oc code
    - (void)startDataShare
    {
        TSDK_RESULT result = tsdk_app_share_start(_confHandle, TSDK_E_CONF_APP_SHARE_DESKTOP);
    }
     
  5. 主讲人侧和其他与会者侧SDK向UI上报屏幕共享状态变更通知消息TSDK_E_CONF_EVT_AS_STATE_CHANGE,UI刷新屏幕共享状态信息。
  6. 共享侧SDK自动抓取屏幕数据,由业务服务器发送给其他与会者,其他与会者侧SDK向UI上报屏幕数据更新通知消息TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE。
    代码示例:
    //oc code
    case TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE:
    {
        DDLogInfo(@"TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE");
        [self handleScreenShareDataConfhandle:notify.param1];
    }
        break;
     
  7. UI调用tsdk_app_share_get_screen_data()接口获取屏幕数据,刷新共享显示区域。
    代码示例:
    //oc code
    -(void)handleScreenShareDataConfhandle:(TSDK_UINT32 )confHandle
    {
        if (!_isStartScreenSharing) {
            DDLogInfo(@"[Meeting] COMPT_MSG_AS_ON_SCREEN_DATA:current share type is not screen share!");
            return;
        }
    
        if (_currentDataShareTypeId != 0x0002 && _currentDataShareTypeId != 0 && _currentDataShareTypeId != -1) {
            return;
        }
    
        TSDK_S_CONF_AS_SCREEN_DATA screenData;
        memset((void *)(&screenData), 0, sizeof(screenData));
        // 获取数据
        TSDK_RESULT dataRet = tsdk_app_share_get_screen_data(confHandle, &screenData);
    
        if (dataRet != TSDK_SUCCESS)
        {
            DDLogInfo(@"tsdk_app_share_get_screen_data failed:%d",dataRet);
            return;
        }
        DDLogInfo(@"tsdk_app_share_get_screen_data :%d",dataRet);
        char *data = (char *)screenData.data;
        if (data == NULL) {
            return;
        }
        TSDK_UINT32 ssize = *((TSDK_UINT32 *)((char *)data + sizeof(TSDK_UINT16)));
        NSData *imageData = [NSData dataWithBytes:data length:ssize];
        if (imageData == nil)
        {
            DDLogInfo(@"share imageData from data fail!");
            return;
        }
        __weak typeof(self) weakSelf = self;
        dispatch_async(espace_dataconf_datashare_queue, ^{
            [weakSelf receiveSharedData:imageData];
        });
    }
     

 

主讲人邀请其他与会者共享屏幕

图2 主讲人邀请其他与会者共享屏幕流程

  1. 主讲人选择邀请其他与会者共享屏幕,UI调用tsdk_app_share_set_owner()接口设置其他与会者为共享权限拥有者。
    说明: 

    SDK并未对接口tup_conf_as_set_owner的调用者进行与角色限制,但实际应用场景中,UI应该仅对主讲人才提供“邀请其他与会者共享”入口;成功调用该接口后,原来拥有共享权限的用户的共享权限会自动取消,正在进行的共享会停止。

    代码示例:
    //oc code 
    - (BOOL)inviteDataShareWithNumber:(NSString *)number
    {    
        TSDK_RESULT result = tsdk_app_share_set_owner(_confHandle, (TSDK_CHAR*)[number UTF8String], TSDK_E_CONF_AS_ACTION_ADD);        
        return result;
    }
     
  2. 主讲人侧和其他与会者侧SDK向UI上报共享权限拥有者变更通知消息TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新屏幕共享者信息。
    说明: 

    TSDK_E_CONF_EVT_AS_OWNER_CHANGE通知中携带的共享类型为TSDK_E_CONF_AS_ACTION_REQUEST,并且共享者ID为自己,UI应提示用户收到共享邀请,并提供“同意共享”和“拒绝共享”入口,“同意共享”则调起系统屏幕录制界面,并选择当前程序然后点击开始共享,“拒绝共享”则调用tsdk_app_share_set_owner()接口,释放owner

    代码示例:
    //oc code
    case TSDK_E_CONF_EVT_AS_OWNER_CHANGE:
    {
        TSDK_E_CONF_AS_ACTION_TYPE actionType = (TSDK_E_CONF_AS_ACTION_TYPE)notify.param2;
        TSDK_S_ATTENDEE *owner =  (TSDK_S_ATTENDEE *)notify.data;
        TSDK_BOOL isSelf = owner->status_info.is_self;
        if (isSelf) {
            if (actionType == TSDK_E_CONF_AS_ACTION_ADD) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:APP_START_SYSTEM_SHARE_VIEW object:nil];
                });
            }else if (actionType == TSDK_E_CONF_AS_ACTION_DELETE){
                self.mIsScreenSharing = NO;
                [self confStopReplay];
                [self respondsECConferenceDelegateWithType:SELF_DATACONF_AS_SCREEN_SHARE_STOP result:nil];
            }else if (actionType == TSDK_E_CONF_AS_ACTION_REQUEST){
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:CONF_SHARE_REQUEST_ACTION
                                                                        object:nil];
    
                });
            }
        }
    }
        break;
    
    - (BOOL)cancelDataShareWithNumber:(NSString *)number
    {
        TSDK_RESULT result = tsdk_app_share_set_owner(_confHandle, (TSDK_CHAR*)[number UTF8String], TSDK_E_CONF_AS_ACTION_DELETE);
    
        return result;
    }
     
    说明: 

    后继步骤与“主讲人共享屏幕”相同。

共享者主动结束共享

图3 共享者主动结束共享流程 
  1. UI调用tsdk_app_share_stop()接口停止共享,同时调用系统关闭屏幕录制方法。
    代码示例:
    //oc code
    - (void)confStopReplayKitBroadcast
    {
        if (self.mIsScreenSharing) {
            TSDK_RESULT result = tsdk_app_share_stop(_confHandle);
        }
        [self confStopReplay];
    }
    - (void)confStopReplay
    {
        NSError *error = [[NSError alloc] initWithDomain:@"ScreenShare" code:-1 userInfo:@{NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"stop_screen_share", nil)}];
        [self.screenShareManager passMessageObject:@{@"result" : error} identifier:@"StopBroadcast"];
        _mWidthPixels = 0;
        _mHeightPixels = 0;
        self.mIsScreenSharing = NO;
        _mIsStartScreenShareDelayed = NO;
    }
     
  2. 共享侧和其他与会者侧SDK向UI上报屏幕共享状态变更通知消息TSDK_E_CONF_EVT_AS_STATE_CHANGE,UI刷新屏幕共享状态信息。
  3. 共享侧向UI调用tsdk_app_share_set_owner()释放共享权限拥有者权限。
  4. 共享侧和其他与会者侧SDK向UI上报共享权限拥有者变更通知消息TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新屏幕共享者信息。

 

主讲人结束共享者共享

图4 主讲人结束共享者共享流程 

 

  1. 主讲人UI调用tsdk_app_share_set_owner()接口释放当前共享者共享权限。
  2. 共享侧和其他与会者侧SDK向UI上报共享权限拥有者变更通知消息TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新屏幕共享者信息。
    说明: 

    TSDK_E_CONF_EVT_AS_OWNER_CHANGE通知中携带的共享类型为TSDK_E_CONF_AS_ACTION_DELETE

    ,并且共享者ID为自己,则表示自己的共享权限被取消,停止自己共享

    代码示例:
    //oc code
    case TSDK_E_CONF_EVT_AS_OWNER_CHANGE:
    {
        TSDK_E_CONF_AS_ACTION_TYPE actionType = (TSDK_E_CONF_AS_ACTION_TYPE)notify.param2;
        TSDK_S_ATTENDEE *owner =  (TSDK_S_ATTENDEE *)notify.data;
        TSDK_BOOL isSelf = owner->status_info.is_self;
        if (isSelf) {
            if (actionType == TSDK_E_CONF_AS_ACTION_ADD) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:APP_START_SYSTEM_SHARE_VIEW object:nil];
                });
            }else if (actionType == TSDK_E_CONF_AS_ACTION_DELETE){
                self.mIsScreenSharing = NO;
                [self confStopReplay];
                [self respondsECConferenceDelegateWithType:SELF_DATACONF_AS_SCREEN_SHARE_STOP result:nil];
            }else if (actionType == TSDK_E_CONF_AS_ACTION_REQUEST){
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:CONF_SHARE_REQUEST_ACTION
                                                                        object:nil];
    
                });
            }
        }
    }
        break;
     
  3. 共享侧调UI用tsdk_app_share_stop()接口停止共享。
    说明: 

    1、调用tsdk_app_share_stop接口前,先结束本地系统界面录制。

    代码示例:
    //oc code
    - (void)confStopReplayKitBroadcast
    {
        if (self.mIsScreenSharing) {
            TSDK_RESULT result = tsdk_app_share_stop(_confHandle);
        }
        [self confStopReplay];
    }
    - (void)confStopReplay
    {
        NSError *error = [[NSError alloc] initWithDomain:@"ScreenShare" code:-1 userInfo:@{NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"stop_screen_share", nil)}];
        [self.screenShareManager passMessageObject:@{@"result" : error} identifier:@"StopBroadcast"];
    
        _mWidthPixels = 0;
        _mHeightPixels = 0;
        self.mIsScreenSharing = NO;
        _mIsStartScreenShareDelayed = NO;
    }
     
  4. 共享侧和其他与会者侧SDK向UI上报屏幕共享状态变更通知消息TSDK_E_CONF_EVT_AS_STATE_CHANGE,UI刷新屏幕共享状态信息。

注意事项

无。

创建标注

描述

在其他终端中,与会者间可以通过标注功能在共享界面进行远程交流。

  1. 加入数据会议成功。
  2. 加载相关模块成功。
说明: 

1、在屏幕共享时,当别人发起标注,移动端iOS支持进行标注,不支持主动发起标注功能。

业务流程

设置画笔和画刷

图5 设置画笔和画刷流程 
说明: 
  1. 启用标注功能后,可以设置画笔和画刷的属性,画笔属性会影响标注线条的颜色、宽度和类型;画刷属性会影响填充的颜色等;
  2. 设置画笔和画刷后,所有新创建的TSDK_E_ANNOTATION_DRAWING类型标注都会使用新的画笔和画刷,旧的标注不受影响。
  3. 当收到TSDK_E_CONF_EVT_AS_STATE_CHANGE消息中的共享子状态sub_state为512时,方可进行下面设置画笔画刷操作,然后才可以进行标注。
//oc code 
case TSDK_E_CONF_EVT_AS_STATE_CHANGE:
{
    DDLogInfo(@"TSDK_E_CONF_EVT_AS_STATE_CHANGE");
    TSDK_S_CONF_AS_STATE_INFO *shareState = (TSDK_S_CONF_AS_STATE_INFO *)notify.data;
    BOOL isStopSharing = NO;
    TSDK_E_CONF_SHARE_STATE state =  shareState->state;
    switch (state) {
        case TSDK_E_CONF_AS_STATE_NULL:
        {
            _isStartScreenSharing = NO;
            isStopSharing = YES;
        }
            break;
        case TSDK_E_CONF_AS_STATE_START:
        case TSDK_E_CONF_AS_STATE_VIEW:
        {
            TSDK_S_CONF_AS_STATE_INFO *as_state_info = (TSDK_S_CONF_AS_STATE_INFO *)notify.data;
            TSDK_UINT32 Annotation = as_state_info->sub_state;

            [self beginAnnotation:(Annotation == 512)];

            _isStartScreenSharing = YES;
            _currentDataShareTypeId = TSDK_E_COMPONENT_AS;
            [self handleScreenShareDataConfhandle:notify.param1];
        }
            break;
        default:
            break;
    }
    if (isStopSharing) {
        __weak typeof(self) weakSelf = self;
        dispatch_async(espace_dataconf_datashare_queue, ^{
            [weakSelf stopSharedData];
            _isStartScreenSharing = NO;
        });
    }
}
    break;
 
  1. UI调用tsdk_annotation_set_pen()接口设置画笔属性,设置时指定当前共享的组件ID、画笔类型和画笔属性。
    说明: 
    1. 因仅屏幕共享、文档共享和白板共享支持标注功能,所以组件ID仅支持对应的组件ID;
    2. 此接口可以返回原画笔的属性,在应用层需要记录原画笔属性时,此接口的最后一个参数应非空。
    代码示例:
    //oc code 
    - (void)annotationSetPenAndAnnotationColor:(unsigned int)color lineWidth:(int)lineWidth {
    
        TSDK_S_ANNOTATION_PEN_INFO newPenInfo;
        memset(&newPenInfo, 0, sizeof(TSDK_S_ANNOTATION_PEN_INFO));
        newPenInfo.style = TSDK_E_ANNOTATION_PEN_STYLE_SOLID;
        newPenInfo.color = color;
        newPenInfo.width = lineWidth;
    
        TSDK_S_ANNOTATION_PEN_INFO oldPenInfo;
        memset(&oldPenInfo, 0, sizeof(TSDK_S_ANNOTATION_PEN_INFO));
    
    
        TSDK_RESULT result = tsdk_annotation_set_pen(_confHandle, (TSDK_E_COMPONENT_ID)_currentDataShareTypeId, TSDK_E_ANNOTATION_PEN_NORMAL, &newPenInfo, &oldPenInfo);
        DDLogInfo(@"Annotation tsdk_annotation_set_pen: %d", result);
    
    
        TSDK_S_ANNOTATION_BRUSH_INFO newBrushInfo;
        memset(&newBrushInfo, 0, sizeof(TSDK_S_ANNOTATION_BRUSH_INFO));
        newBrushInfo.style = TSDK_E_ANNOTATION_BRUSH_SOLID;
        newBrushInfo.color = color;
    
        TSDK_S_ANNOTATION_BRUSH_INFO oldBrushInfo;
        memset(&oldBrushInfo, 0, sizeof(TSDK_S_ANNOTATION_BRUSH_INFO));
    
        TSDK_RESULT brushResult = tsdk_annotation_set_brush(_confHandle, (TSDK_E_COMPONENT_ID)_currentDataShareTypeId, &newBrushInfo, &oldBrushInfo);
        DDLogInfo(@"Annotation tsdk_annotation_set_brush: %d", brushResult);
    
    }
    
     
  2. UI调用tsdk_annotation_set_brush()接口设置画刷属性,设置时指定当前共享的组件ID,画刷属性。
    说明: 
    1. 当前仅屏幕共享支持标注功能,所以组件ID仅支持对应的组件ID;
    2. 此接口可以返回原画刷的属性,在应用层需要记录原画刷属性时,此接口的最后一个参数应非空。

 

创建几何标注

图6 创建几何标注流程 
  1. UI调用tsdk_annotation_create_start()接口开始创建标注,标注类型为TSDK_E_ANNOTATION_DRAWING。
    说明: 
    1. 屏幕共享过程中、仅具备标注权限的与会者可以进行标注操作。应用程序界面应该根据当前共享状态和权限给用户提供标注入口;
    2. 创建标注的接口中用到的坐标都是相对于页面左上角的TWIPS单位坐标,向右向下为正。
    代码示例:
    //oc code 
    -(void)conferenceCreateAnnotationWithStartPointx:(long)pointx Pointy:(long)pointy {
        CGFloat hScale = [self heightScale];
        CGFloat wScale = [self widthScale];
    
        TSDK_S_POINT tsdkPoint;
        memset(&tsdkPoint, 0, sizeof(TSDK_S_POINT));
        tsdkPoint.x = (TSDK_INT32)pointx * wScale;
        tsdkPoint.y = (TSDK_INT32)pointy * hScale;
    
        tsdk_annotation_create_start(_confHandle, (TSDK_E_COMPONENT_ID)_currentDataShareTypeId, TSDK_NULL_PTR, TSDK_E_ANNOTATION_DRAWING, 1, &tsdkPoint);
    }
    
     
  2. UI定时循环调用tsdk_annotation_create_update()接口在创建过程中更新数据。
    代码示例:
    //oc code 
    - (void)conferenceUpdateAnnotationWithPointx:(long)pointx Pointy:(long)pointy {
        CGFloat hScale = [self heightScale];
        CGFloat wScale = [self widthScale];
    
        TSDK_S_ANNOTATION_DRAWING_DATA data;
        memset(&data, 0, sizeof(TSDK_S_ANNOTATION_DRAWING_DATA));
        data.point.x = (TSDK_INT32)pointx * wScale;
        data.point.y = (TSDK_INT32)pointy * hScale;
    
        tsdk_annotation_create_update(_confHandle, (TSDK_E_COMPONENT_ID)_currentDataShareTypeId, TSDK_E_ANNOTATION_DRAWING, &data);
    }
    
     
  3. UI调用tsdk_annotation_create_done()接口完成标注创建。
    说明: 

    若完成标注创建,则此接口的第三个参数应设置为0,否则设置为1。

    代码示例:
    //oc code 
    - (void)conferenceFinishAnnotation {
        TSDK_UINT32 annotation_id = 0;
        tsdk_annotation_create_done(_confHandle, (TSDK_E_COMPONENT_ID)_currentDataShareTypeId, 0, &annotation_id);
    }
    
     
  4. 其他与会者侧SDK向UI上报数据更新消息,屏幕共享时上报TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE,文档共享时上报TSDK_E_CONF_EVT_DS_DOC_DRAW_DATA_NOTIFY,白板共享时上报TSDK_E_CONF_EVT_WB_DOC_DRAW_DATA_NOTIFY。
    说明: 

    实际上,在标注创建过程中,每次数据更新之后都会收到相应的数据更新通知,当前因流程图限制,仅呈现在标注创建完成后收到数据更新通知消息。

  5. UI调用tsdk_doc_share_get_surface_bmp()/tsdk_app_share_get_screen_data()接口获取当前页面图像数据,并使用获取到的数据刷新共享显示页面。

注意事项

无。

 

删除标注

图7 删除标注流程 
  1. UI调用tsdk_annotation_delete_annotation()接口删除标注。
    说明: 

    此接口可以对选中的多个标注同时开始删除,应用程序也可以实现对标注ID的记录管理,给用户提供删除“自己创建的标注”、“其他人创建的标注”和“所有标注”功能入口。

    擦除可以擦除线,擦除所有两种擦除场景。

    代码示例:
    //oc code 
    - (void)conferenceEraseAnnotationsIntersectedBySegmentWithStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
    
        TSDK_S_ANNOTATION_HIT_TEST_LINE_INFO hit_test_line_info;
        memset(&hit_test_line_info, 0, sizeof(TSDK_S_ANNOTATION_HIT_TEST_LINE_INFO));
        hit_test_line_info.doc_page_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
        hit_test_line_info.doc_page_info.document_id = 0;
        hit_test_line_info.doc_page_info.page_count = 0;
        hit_test_line_info.doc_page_info.page_index = 0;
    
        hit_test_line_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
    
        CGFloat hScale = [self heightScale];
        CGFloat wScale = [self widthScale];
        hit_test_line_info.start_point.x = (TSDK_INT32)startPoint.x * wScale;
        hit_test_line_info.start_point.y = (TSDK_INT32)startPoint.y * hScale;
    
        hit_test_line_info.end_point.x = (TSDK_INT32)endPoint.x * wScale;
        hit_test_line_info.end_point.y = (TSDK_INT32)endPoint.y * hScale;
    
        hit_test_line_info.hit_test_mode = TSDK_E_ANNOTATION_HIT_TEST_SOMEONE;
    
        strcpy(hit_test_line_info.user_number, [self.selfJoinNumber UTF8String]);
    
        TSDK_UINT32 *selectedIds = NULL;
    
        TSDK_UINT32 idsCount = 0;
    
        TSDK_RESULT result = tsdk_annotation_hit_test_line(_confHandle, &hit_test_line_info, &selectedIds, &idsCount);
        DDLogInfo(@"tsdk_annotation_hit_test_line: %d", result);
    
        if (selectedIds && idsCount > 0) {
            TSDK_S_ANNOTATION_DELETE_INFO delete_info;
            delete_info.annotation_id_list = selectedIds;
            delete_info.count = idsCount;
            delete_info.doc_page_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
            delete_info.doc_page_info.document_id = 0;
            delete_info.doc_page_info.page_count = 0;
            delete_info.doc_page_info.page_index = 0;
    
            delete_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
    
            result = tsdk_annotation_delete_annotation(_confHandle, &delete_info);
            DDLogInfo(@"Annotation tsdk_annotation_delete_annotation: %d", result);
        }
    }
    
    - (void)conferenceEraseAllAnnotations {
        TSDK_S_RECTANGULAR rect = { .left = 0, .top = 0, .right = (TSDK_INT32)INT_MAX, .bottom = (TSDK_INT32)INT_MAX };
        [self conferenceEraseAnnotationsInRect:rect];
    }
    - (void)conferenceEraseAnnotationsInRect:(TSDK_S_RECTANGULAR)rect {
        TSDK_UINT32 *selectedIds = NULL;
        TSDK_UINT32 idsCount = 0;
        TSDK_S_ANNOTATION_HIT_TEST_RECT_INFO hit_test_rect_info;
        hit_test_rect_info.doc_page_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
        if (_currentDataShareTypeId == TSDK_E_COMPONENT_AS) {
            hit_test_rect_info.doc_page_info.document_id = 0;
            hit_test_rect_info.doc_page_info.page_count = 0;
            hit_test_rect_info.doc_page_info.page_index = 0;
        }
        hit_test_rect_info.hit_test_mode = TSDK_E_ANNOTATION_HIT_TEST_SOMEONE;
        hit_test_rect_info.rectangle_area.bottom = rect.bottom;
        hit_test_rect_info.rectangle_area.left = rect.left;
        hit_test_rect_info.rectangle_area.right = rect.right;
        hit_test_rect_info.rectangle_area.top = rect.top;
        strcpy(hit_test_rect_info.user_number, [self.selfJoinNumber UTF8String]);
    
        hit_test_rect_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
    
        TSDK_RESULT result = tsdk_annotation_hit_test_rect(_confHandle, &hit_test_rect_info, &selectedIds, &idsCount);
    
        DDLogInfo(@"Annotation eraseAnnotationsInRect: tup_conf_annotation_hittest_rect returns: %d", result);
    
        if (selectedIds && idsCount > 0) {
            TSDK_S_ANNOTATION_DELETE_INFO delete_info;
            delete_info.annotation_id_list = selectedIds;
            delete_info.count = idsCount;
            delete_info.doc_page_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
            delete_info.doc_page_info.document_id = 0;
            delete_info.doc_page_info.page_count = 0;
            delete_info.doc_page_info.page_index = 0;
    
            delete_info.component_id = (TSDK_E_COMPONENT_ID)_currentDataShareTypeId;
    
            result = tsdk_annotation_delete_annotation(_confHandle, &delete_info);
            DDLogInfo(@"Annotation eraseAnnotationsInRect: tup_conf_annotation_delete returns: %d", result);
        }
    }
    
     
  2. 其他与会者侧SDK向UI上报数据更新消息,屏幕共享时上报TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE,文档共享时上报TSDK_E_CONF_EVT_DS_DOC_DRAW_DATA_NOTIFY,白板共享时上报TSDK_E_CONF_EVT_WB_DOC_DRAW_DATA_NOTIFY。
  3. UI调用tsdk_doc_share_get_surface_bmp()/tsdk_app_share_get_screen_data()接口获取当前页面图像数据,并使用获取到的数据刷新共享显示页面。

注意事项

无。

观看屏幕和程序共享

描述

会议中,移动与会者观看屏幕或程序共享。

前提条件

  1. 加入数据会议成功。
  2. 加载屏幕/程序共享模块成功。

业务流程

一、开始观看屏幕或程序共享

图8 开始观看屏幕或程序共享流程 
  1. 会议中,主讲人设置共享权限拥有者,邀请其他与会者进行屏幕或程序共享,移动与会者侧SDK向UI上报共享者变更消息,对应的消息ID为TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新共享者信息。
  2. 共享者开始共享屏幕或程序,移动与会者侧SDK上报屏幕共享状态变更通知消息,对应的消息ID为TSDK_E_CONF_EVT_AS_STATE_CHANGE,UI刷新屏幕共享状态信息。
    代码示例:
    case TSDK_E_CONF_EVT_AS_STATE_CHANGE:        
    {            
        DDLogInfo(@"TSDK_E_CONF_EVT_AS_STATE_CHANGE");            
        TSDK_S_CONF_AS_STATE_INFO *shareState = (TSDK_S_CONF_AS_STATE_INFO *)notify.data;                     TSDK_E_CONF_SHARE_STATE state =  shareState->state;            
        if (state == TSDK_E_CONF_AS_STATE_NULL) 
        {                
            [self respondsECConferenceDelegateWithType:DATACONF_SHARE_SCREEN_DATA_STOP result:nil];            
        }        
    }            
    break;
     
  3. 共享侧SDK自动抓取屏幕数据,由业务服务器发送给其他与会者,移动与会者侧SDK向UI上报屏幕数据更新通知消息,对应的消息ID为TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE。
    代码示例:
    case TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE:        
    {            
        DDLogInfo(@"TSDK_E_CONF_EVT_AS_SCREEN_DATA_UPDATE");            
        [self handleScreenShareDataConfhandle:notify.param1];        
    }            
    break;
     
  4. UI调用tsdk_app_share_get_screen_data()接口获取屏幕数据,刷新共享显示区域。
    说明: 

    屏幕共享数据通过接口同步返回,转换成图片格式在UI显示。

    代码示例:
    -(void)handleScreenShareDataConfhandle:(TSDK_UINT32 )confHandle
    {    
        TSDK_S_CONF_AS_SCREEN_DATA screenData;    
        memset((void *)(&screenData), 0, sizeof(screenData));    
    // get data info    
        TSDK_RESULT dataRet = tsdk_app_share_get_screen_data(confHandle, &screenData);    
        if (dataRet != TSDK_SUCCESS)    
        {        
            DDLogInfo(@"tsdk_app_share_get_screen_data failed:%d",dataRet);        
            return;    
        }    
        DDLogInfo(@"tsdk_app_share_get_screen_data :%d",dataRet);    
        char *data = (char *)screenData.data;    
        TSDK_UINT32 ssize = *((TSDK_UINT32 *)((char *)data + sizeof(TSDK_UINT16)));    
        NSData *imageData = [NSData dataWithBytes:data length:ssize];    
        UIImage *image = [[UIImage alloc] initWithData:imageData];    
        if (image == nil)    
        {        
            DDLogInfo(@"share image from data fail!");       
            return;    
        }    
        NSDictionary *shareDataInfo = @{                                    
            DATACONF_SHARE_DATA_KEY:image                                    
        };    
        [self respondsECConferenceDelegateWithType:DATA_CONF_AS_ON_SCREEN_DATA result:shareDataInfo];
    }
     

 

二、屏幕或程序共享结束处理

图9 屏幕或程序共享结束处理流程 
说明: 

当前图示为共享者主动停止共享,若主讲人结束共享者共享,则对于观看侧,相应的消息顺序相反。

  1. 共享者停止共享,移动与会者侧SDK通过上报屏幕共享状态变更通知消息,对应的消息ID为TSDK_E_CONF_EVT_AS_STATE_CHANGE,UI刷新屏幕共享状态信息。
  2. 共享者释放共享权限,移动与会者侧SDK向UI上报共享者变更消息,对应的消息ID为TSDK_E_CONF_EVT_AS_OWNER_CHANGE,UI刷新屏幕共享状态信息。

注意事项

无。

聊天

描述

会议中,所有人可以收到其他与会者发送的聊天消息内容。

前提条件是加入数据会议成功。

业务流程

图10 发送聊天消息 
  1. UI调用接口tsdk_send_chat_msg_in_conference()接口在会议中发送公共即时消息。
    代码示例:
    TSDK_RESULT ret;
    ret = tsdk_send_chat_msg_in_conference(confHandle, chatMsgInfo);
    if (TSDK_SUCCESS != ret)
    {
        LOG_D_CALL_ERROR("send chat msg failed. result=%#x", ret);
        return -1;
    }
    return TSDK_SUCCESS;
     
  2. 会议中所有用户(包括消息发送者)侧SDK均收到消息通知,向UI上报TSDK_E_CONF_EVT_RECV_CHAT_MSG事件,UI显示公共即时消息。
    代码示例:
    case TSDK_E_CONF_EVT_RECV_CHAT_MSG:
    {
         /*Notify UI*/
    }
     

注意事项

无。

观看文档共享

描述

会议中,移动与会者观看文档共享。

说明: 

移动应用程序暂不具备共享文档的能力,文档的共享者为PC应用程序。

前提条件

  1. 加入数据会议成功。
  2. 加载文档共享模块成功。

业务流程

一、开始观看文档共享

图11 开始观看文档共享流程 
  1. 会议中,主讲人设置共享权限拥有者,邀请其他与会者进行文档共享,移动与会者侧SDK向UI上报共享者变更消息,对应的消息ID为TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新共享者信息。
  2. 共享者开始共享文档,移动与会者侧SDK上报同步翻页预先通知消息,对应的消息ID为TSDK_E_CONF_EVT_DS_DOC_CURRENT_PAGE_IND。
    代码示例:
    case TSDK_E_CONF_EVT_DS_DOC_CURRENT_PAGE_IND:        
    {           
        TSDK_S_DOC_PAGE_BASE_INFO *pageInfo = (TSDK_S_DOC_PAGE_BASE_INFO *)notify.data;            
        [self handleDsDocCurrentPageInfoWithConfHandle:notify.param1 andPageInfo:pageInfo];                    
    }            
        break;
     
  3. UI分别按顺序调用接口tsdk_doc_share_set_current_page(),tsdk_doc_share_get_syn_document_info(),tsdk_doc_share_set_canvas_size(),设置显示区域大小。

    代码示例:

    - (void)handleDsDocCurrentPageInfoWithConfHandle:(TSDK_INT32)confHandle andPageInfo:(TSDK_S_DOC_PAGE_BASE_INFO *)pageInfo
    {    
        tsdk_doc_share_set_current_page(confHandle, pageInfo, NO);    
        TSDK_S_DOC_PAGE_DETAIL_INFO detailInfo;    
        memset(&detailInfo, 0, sizeof(TSDK_S_DOC_PAGE_DETAIL_INFO));        
        TSDK_RESULT result = tsdk_doc_share_get_syn_document_info(confHandle, pageInfo->component_id, &detailInfo);        
        if (result == TSDK_SUCCESS && (detailInfo.height > 0 && detailInfo.width > 0)) 
        {        
            TSDK_S_SIZE size;        
            memset(&size, 0, sizeof(TSDK_S_DOC_PAGE_BASE_INFO));        
            size.width = detailInfo.width;        
            size.high = detailInfo.height;        
            tsdk_doc_share_set_canvas_size(confHandle, pageInfo->component_id, &size, YES);    
    }}
     
  4. 共享侧SDK自动抓取文档数据,由业务服务器发送给其他与会者,移动与会者侧SDK向UI上报文档界面数据更新通知消息,对应的消息ID为TSDK_E_CONF_EVT_DS_DOC_DRAW_DATA_NOTIFY。
    代码示例:
    case TSDK_E_CONF_EVT_DS_DOC_DRAW_DATA_NOTIFY:        
    {            
        [self handleDsDocShareDataConfHandle:notify.param1];        
    }            
        break;
     
  5. UI调用tsdk_doc_share_get_surface_bmp()接口获取文档数据,刷新共享显示区域。
    说明: 

    文档共享数据通过接口同步返回,转换成图片格式在UI显示。

    代码示例:
    - (void)handleDsDocShareDataConfHandle:(TSDK_UINT32)confHandle
    {    
        __weak typeof(self) weakSelf = self;    
        dispatch_async(espace_dataconf_datashare_queue, ^{        
            @autoreleasepool {            
                TSDK_UINT32 iWidth = 0;            
                TSDK_UINT32 iHeight = 0;            
                TSDK_VOID *pData = tsdk_doc_share_get_surface_bmp(confHandle, TSDK_E_COMPONENT_DS, &iWidth, &iHeight);            
                if (NULL == pData) {                
                DDLogInfo(@"[Meeting] Data is null.");                
                return;            
                }           
                char *pBmpData = (char *)pData;            
                TSDK_UINT32 wSize = *(TSDK_UINT32 *)((char *)pBmpData + sizeof(TSDK_UINT16));           
                NSData *imgData = [NSData dataWithBytes:(void*)pBmpData length:wSize];            
                if (nil == imgData) {                
                DDLogInfo(@"[Meeting] Make image from data failed.");                
                return;            
                }            
                [weakSelf receiveSharedData:imgData];        
            }    
        });
    }
     

 

二、文档共享结束处理

图12 文档共享结束处理流程 
说明: 

当前图示为共享者主动停止共享,若主讲人结束共享者共享,则对于观看侧,相应的消息顺序相反。

  1. 共享者停止共享,移动与会者侧SDK通过上报文档共享结束通知消息,对应的消息ID为TSDK_E_CONF_EVT_DS_DOC_DEL,UI刷新界面结束共享。
  2. 共享者释放共享权限,移动与会者侧SDK向UI上报共享者变更消息,对应的消息ID为TSDK_E_CONF_EVT_AS_OWNER_CHANGE,UI刷新共享者信息。

注意事项

无。

观看白板共享

描述

会议中,移动与会者观看白板共享。

说明: 

移动应用程序暂不具备共享白板的能力,白板的共享者为PC应用程序。

前提条件:

  1. 加入数据会议成功。
  2. 加载白板共享模块成功。

业务流程

一、开始观看白板共享

图13 开始观看白板共享流程 
  1. 会议中,主讲人设置共享权限拥有者,邀请其他与会者进行文档白板,移动与会者侧SDK向UI上报共享者变更消息,对应的消息ID为TSDK_E_CONF_EVT_AS_OWNER_CHANGE,携带当前共享权限拥有者ID,UI刷新共享者信息。
  2. 共享者开始共享白板,移动与会者侧SDK上报同步翻页预先通知消息,对应的消息ID为TSDK_E_CONF_EVT_WB_DOC_CURRENT_PAGE_IND。
    代码示例:
    case TSDK_E_CONF_EVT_WB_DOC_CURRENT_PAGE_IND:        
    {           
        TSDK_S_DOC_PAGE_BASE_INFO *pageInfo = (TSDK_S_DOC_PAGE_BASE_INFO *)notify.data;            
        [self handleDsDocCurrentPageInfoWithConfHandle:notify.param1 andPageInfo:pageInfo];                    
    }            
        break;
     
  3. UI分别按顺序调用接口tsdk_doc_share_set_current_page(),tsdk_doc_share_get_syn_document_info(),tsdk_doc_share_set_canvas_size(),设置显示区域大小。

    代码示例:

    - (void)handleDsDocCurrentPageInfoWithConfHandle:(TSDK_INT32)confHandle andPageInfo:(TSDK_S_DOC_PAGE_BASE_INFO *)pageInfo
    {    
        tsdk_doc_share_set_current_page(confHandle, pageInfo, NO);    
        TSDK_S_DOC_PAGE_DETAIL_INFO detailInfo;    
        memset(&detailInfo, 0, sizeof(TSDK_S_DOC_PAGE_DETAIL_INFO));        
        TSDK_RESULT result = tsdk_doc_share_get_syn_document_info(confHandle, pageInfo->component_id, &detailInfo);        
        if (result == TSDK_SUCCESS && (detailInfo.height > 0 && detailInfo.width > 0)) 
        {        
            TSDK_S_SIZE size;        
            memset(&size, 0, sizeof(TSDK_S_DOC_PAGE_BASE_INFO));        
            size.width = detailInfo.width;        
            size.high = detailInfo.height;        
            tsdk_doc_share_set_canvas_size(confHandle, pageInfo->component_id, &size, YES);    
    }}
     
  4. 共享侧SDK自动抓取白板数据,由业务服务器发送给其他与会者,移动与会者侧SDK向UI上报白板界面数据更新通知消息,对应的消息ID为TSDK_E_CONF_EVT_WB_DOC_DRAW_DATA_NOTIFY。
    代码示例:
    case TSDK_E_CONF_EVT_WB_DOC_DRAW_DATA_NOTIFY:        
    {            
        [self handleWbDocShareDataConfHandle:notify:notify.param1];        
    }            
        break;
     
  5. UI调用tsdk_doc_share_get_surface_bmp()接口获取白板数据,刷新共享显示区域。
    说明: 

    白板共享数据通过接口同步返回,转换成图片格式在UI显示。

    代码示例:
    - (void)handleWbDocShareDataConfHandle:(TSDK_UINT32)confHandle
    {    
        __weak typeof(self) weakSelf = self;    
        dispatch_async(espace_dataconf_datashare_queue, ^{        
            @autoreleasepool {            
                TSDK_UINT32 iWidth = 0;            
                TSDK_UINT32 iHeight = 0;            
                TSDK_VOID *pData = tsdk_doc_share_get_surface_bmp(confHandle, TSDK_E_COMPONENT_DS, &iWidth, &iHeight);            
                if (NULL == pData) {                
                DDLogInfo(@"[Meeting] Data is null.");                
                return;            
                }           
                char *pBmpData = (char *)pData;            
                TSDK_UINT32 wSize = *(TSDK_UINT32 *)((char *)pBmpData + sizeof(TSDK_UINT16));           
                NSData *imgData = [NSData dataWithBytes:(void*)pBmpData length:wSize];            
                if (nil == imgData) {                
                DDLogInfo(@"[Meeting] Make image from data failed.");                
                return;            
                }            
                [weakSelf receiveSharedData:imgData];        
            }    
        });
    }
     

 

二、白板共享结束处理

图14 白板共享结束处理流程 
说明: 

当前图示为共享者主动停止共享,若主讲人结束共享者共享,则对于观看侧,相应的消息顺序相反。

  1. 共享者停止共享,移动与会者侧SDK通过上报白板共享结束通知消息,对应的消息ID为TSDK_E_CONF_EVT_WB_DOC_DEL,UI刷新界面结束共享。
  2. 共享者释放共享权限,移动与会者侧SDK向UI上报共享者变更消息,对应的消息ID为TSDK_E_CONF_EVT_AS_OWNER_CHANGE,UI刷新共享者信息。

注意事项

无。