iOS

腾讯美颜

更新时间: 2025/06/20 15:39:55

网易云信 NERTC SDK 支持接入腾讯美颜(TEBeautyKit)SDK,实现美颜、美妆、滤镜、贴纸等丰富美颜特效。在视频社交、在线教育、直播互动等场景中,您可以快速构建具备专业美颜能力的应用,提升用户在视频通话或直播过程中的形象表现力。

功能概述

通过集成腾讯美颜 SDK,您可以在 NERTC 音视频通话中实现以下功能:

  • 智能美颜:磨皮、美白等基础美颜,以及大眼、瘦脸、V 脸、下巴调整等面部微调
  • 美妆特效:口红、眼影、腮红等美妆效果
  • 特效滤镜:多种风格滤镜,满足不同场景需求
  • 贴纸道具:丰富的 2D、3D 贴纸,增强互动趣味性

工作原理

腾讯美颜原理.png
  1. NERTC SDK 通过 onNERtcEngineVideoFrameCaptured 回调方法将采集到的视频帧数据传递给应用层,应用层将视频帧传递给腾讯美颜 SDK 进行美颜处理。
  2. 腾讯美颜 SDK 处理完成后,将美颜后的视频帧返回给应用层。
  3. 应用层将美颜后的视频帧数据回写到原始的 pixelBuffer 中。
  4. NERTC SDK 将美颜后的视频帧进行编码和传输。

注意事项

  • 图像格式转换:腾讯美颜 SDK 处理后的数据格式可能与原始格式不同,需要进行适当的格式转换。
  • 图像方向:在设备旋转时,需要重新设置腾讯美颜的图像方向。建议在收到设备方向变化通知时,及时更新美颜 SDK 的图像方向设置。
  • 内存管理:处理视频帧时需要注意内存的锁定和解锁,避免内存访问错误。
  • 系统版本:腾讯美颜 SDK 支持的最低系统版本为 iOS 12.2,请确保您的应用适配该版本。
  • 资源释放:在退出通话或应用退出时,需要正确释放美颜相关资源,避免内存泄漏。
  • 避免黑屏:在美颜处理过程中,可能会出现黑屏的情况。请确保检查美颜资源是否正确加载,以及图像方向设置是否正确。同时确保 CVPixelBuffer 格式转换过程正确无误。

前提条件

在集成腾讯美颜 SDK 前,请确保您已完成以下准备工作:

第一步:环境集成

  1. 集成网易云信 NERTC SDK。如果您已完成本步骤,请跳过此步。

    1. 根据 更新日志 选择 NERTC SDK 版本号。

    2. 使用 CocoaPods 集成 NERTC SDK:

      Rubypod 'NERtcSDK', '~> 5.8.20' # 请使用您项目中的实际版本
      
  2. 集成腾讯美颜 SDK。详细步骤请参考《腾讯美颜 SDK》集成文档。建议使用 TEBeautyKit 组件,可以更便捷地接入腾讯美颜功能。

    1. 下载并解压 TEBeautyKit.zip,文件由腾讯美颜官方提供。

    2. TEBeautyKit 文件夹拷贝到自己的工程中,和 podfile 同级目录。

    3. 编辑 podfile 文件,添加下面的代码:

      Rubypod 'TEBeautyKit',:path => 'TEBeautyKit/TEBeautyKit.podspec'
      
    4. 执行 pod install

第二步:初始化 SDK

建议在网易云信 RTC SDK 加入房间前完成腾讯美颜 SDK 的相关初始化和设置。

  1. 腾讯美颜 License 配置,确保鉴权成功。

    Objective-C[TEBeautyKit setTELicense:@"your_tencent_license" key:@"your_tencent_key" completion:^(NSInteger authresult, NSString * _Nullable errorMsg) {
        NSLog(@"----------result: %zd  %@",authresult,errorMsg);
    }];
    
  2. 配置美颜素材路径。

    如果 JSON 文件中配置的素材是本地的,您需要将美颜素材添加到工程中。

    Objective-C- (void)initBeautyJson{
        [[TEUIConfig shareInstance] setPanelLevel:S1_07]; //根据美颜套餐选择
    }
    
  3. 初始化并添加 TEPanelView

    Objective-C-(TEPanelView *)tePanelView{
        if (!_tePanelView) {
            _tePanelView = [[TEPanelView alloc] init:nil comboType:nil];
            _tePanelView.delegate = self;
        }
        return _tePanelView;
    }
    [self.view addSubview:self.tePanelView];
    [self.tePanelView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.mas_equalTo(self.view);
        make.centerX.mas_equalTo(self.view);
        make.height.mas_equalTo(220);
        make.bottom.mas_equalTo(self.view.mas_bottom);
    }];
    

第三步:视频处理流程

  1. 创建视频数据转换工具类和美颜处理所需对象。

    Objective-C@interface YourClassName () <NERtcEngineDelegateEx>
    
    @property (nonatomic, strong) XMagic *xMagicKit;
    @property (nonatomic, strong) TEBeautyKit *teBeautyKit;
    @property (nonatomic, strong) TEImageTransform *transForm;
    
    @end
    
    @implementation YourClassName
    
    - (void)setupVideoProcessing {
         // 创建数据转换工具对象
         self.transForm = [[TEImageTransform alloc] init];
    
         // 创建腾讯美颜对象
         self.teBeautyKit = [[TEBeautyKit alloc] init];
         __weak __typeof(self)weakSelf = self;
         [TEBeautyKit create:^(XMagic * _Nullable api) {
             __strong typeof(self) strongSelf = weakSelf;
             strongSelf.xMagicKit = api;
             [strongSelf.teBeautyKit setXMagicApi:api];
             strongSelf.tePanelView.teBeautyKit = strongSelf.teBeautyKit;
             [strongSelf.teBeautyKit setTePanelView:strongSelf.tePanelView];
             [strongSelf.teBeautyKit setLogLevel:YT_SDK_ERROR_LEVEL];
             strongSelf.tePanelView.beautyKitApi = api;
             [strongSelf.xMagicKit registerSDKEventListener:strongSelf];
         }];
    }
    
  2. 实现 NERTC SDK 的视频帧回调。

    Objective-C// NERTC SDK 视频采集数据回调
    - (void)onNERtcEngineVideoFrameCaptured:(CVPixelBufferRef)pixelBuffer rotation:(NERtcVideoRotationType)rotation {
        // 美颜对象检查
        if (!self.teBeautyKit || !self.xMagicKit || !self.transForm) {
            // 检查美颜 SDK 对象是否已初始化
            return;
        }
    
        // ⚠️ 转换 NERTC 的旋转角度为腾讯美颜 SDK 的旋转角度
        YtLightDeviceCameraOrientation teOrientation = YtLightCameraRotation0;
        switch (rotation) {
            case kNERtcVideoRotation_0:
                teOrientation = YtLightCameraRotation0;
                break;
            case kNERtcVideoRotation_90:
                teOrientation = YtLightCameraRotation90;
                break;
            case kNERtcVideoRotation_180:
                teOrientation = YtLightCameraRotation180;
                break;
            case kNERtcVideoRotation_270:
                teOrientation = YtLightCameraRotation270;
                break;
            default:
                break;
        }
    
        // ⚠️ 设置图像方向
        [self.xMagicKit setImageOrientation:teOrientation];
    
        // 处理视频帧
        YTProcessOutput *output = [self.teBeautyKit processPixelData:pixelBuffer
                                                         withOrigin:YtLightImageOriginTopLeft
                                                    withOrientation:YtLightCameraRotationAuto];
    
        if (output.pixelData.data == NULL) {
            return;
        }
    
        // 确定网易云信 RTC SDK 数据的像素格式类型
        OSType pixelType = CVPixelBufferGetPixelFormatType(pixelBuffer);
        TEPixelFormatType destFormatType = TE_NV12V;
        switch (pixelType) {
            case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
                destFormatType = TE_NV12V;
                break;
            case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
                destFormatType = TE_NV12F;
                break;
            case kCVPixelFormatType_32BGRA:
                destFormatType = TE_BGRA;
                break;
            default:
                break;
        }
    
        // 腾讯美颜处理完后的数据格式与网易云信 RTC SDK 回调数据的原始格式不相同,需要将格式转换回去
        CVPixelBufferRef destBuffer = [self.transForm transformCVPixelBufferToBuffer:output.pixelData.data outputFormat:destFormatType];
    
        // 将美颜处理过的数据,填充回 NERTC SDK 采集数据回调的内存地址中
        [self.class ConvertPixBuffer:destBuffer toDestPixBuffer:pixelBuffer];
    }
    
    // 像素数据拷贝方法
    + (void)ConvertPixBuffer:(CVPixelBufferRef)srcPixelBuffer toDestPixBuffer:(CVPixelBufferRef)outPixelBuffer {
        size_t width = CVPixelBufferGetWidth(srcPixelBuffer);
        size_t height = CVPixelBufferGetHeight(srcPixelBuffer);
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(srcPixelBuffer);
        OSType pixelType = CVPixelBufferGetPixelFormatType(srcPixelBuffer);
    
        size_t destWidth = CVPixelBufferGetWidth(outPixelBuffer);
        size_t destHeight = CVPixelBufferGetHeight(outPixelBuffer);
        size_t destBytesPerRow = CVPixelBufferGetBytesPerRow(outPixelBuffer);
        OSType destPixelType = CVPixelBufferGetPixelFormatType(outPixelBuffer);
    
        // 检查源缓冲区和目标缓冲区的参数是否一致
        if (width != destWidth || height != destHeight || bytesPerRow != destBytesPerRow || pixelType != destPixelType) {
            return;
        }
    
        BOOL isPlanar = CVPixelBufferIsPlanar(srcPixelBuffer);
        if (isPlanar) {
            size_t srcCount = CVPixelBufferGetPlaneCount(srcPixelBuffer);
    
            CVPixelBufferLockBaseAddress(srcPixelBuffer, 0);
            CVPixelBufferLockBaseAddress(outPixelBuffer, 0);
            for (size_t i = 0; i < srcCount; i++) {
                void *srcBaseAddress = CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, i);
                size_t srcBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, i);
                size_t srcHeight = CVPixelBufferGetHeightOfPlane(srcPixelBuffer, i);
                size_t srcLength = srcBytesPerRow * srcHeight;
    
                void *destBaseAddress = CVPixelBufferGetBaseAddressOfPlane(outPixelBuffer, i);
    
                memcpy(destBaseAddress, srcBaseAddress, srcLength);
            }
            CVPixelBufferUnlockBaseAddress(outPixelBuffer, 0);
            CVPixelBufferUnlockBaseAddress(srcPixelBuffer, 0);
        } else {
            CVPixelBufferLockBaseAddress(srcPixelBuffer, 0);
            CVPixelBufferLockBaseAddress(outPixelBuffer, 0);
            void *srcBaseAddress = CVPixelBufferGetBaseAddress(srcPixelBuffer);
            size_t srcLength = bytesPerRow * height;
    
            void *destBaseAddress = CVPixelBufferGetBaseAddress(outPixelBuffer);
    
            memcpy(destBaseAddress, srcBaseAddress, srcLength);
            CVPixelBufferUnlockBaseAddress(outPixelBuffer, 0);
            CVPixelBufferUnlockBaseAddress(srcPixelBuffer, 0);
        }
    }
    
  3. 启用 NERTC SDK 的视频采集:

    Objective-C// 在加入房间之前,设置参数开启摄像头采集数据的回调
    NSDictionary *params = @{kNERtcKeyVideoCaptureObserverEnabled : @YES};
    [[NERtcEngine sharedEngine] setParameters:params];
    
    // 设置代理以接收视频帧回调
    [engine setDelegate:self];
    
    // 启用视频功能
    [engine enableLocalVideo:YES];
    

第四步:资源释放

在退出通话或应用退出时,需要正确释放美颜相关资源:

Objective-C- (void)dealloc {
    //释放美颜资源
    [self.teBeautyKit onDestroy];

    // 释放其他资源
    self.transForm = nil;
    self.teBeautyKit = nil;
    self.xMagicKit = nil;
}
此文档是否对你有帮助?
有帮助
去反馈
  • 功能概述
  • 工作原理
  • 注意事项
  • 前提条件
  • 第一步:环境集成
  • 第二步:初始化 SDK
  • 第三步:视频处理流程
  • 第四步:资源释放