蒙版弹幕功能可以在弹幕经过画面中的主体区域(如人像)时,智能地将其隐藏,实现“弹幕绕人走”的效果,从而在保证弹幕互动性的同时,避免遮挡视频的核心内容,极大地提升用户观看体验。该功能尤其适用于人物访谈、舞蹈视频、游戏直播等需要突出画面主体的场景。效果演示:
本文将指导您如何基于应用中已有的弹幕渲染功能,实现播放器 SDK 提供的蒙版弹幕能力。
蒙版弹幕的实现,是播放器 SDK 与您的弹幕渲染模块协作完成的:
SDK 输出蒙版信息:播放器 SDK 会根据当前视频帧的时间戳,实时输出一个描述画面主体轮廓的蒙版信息(一个 SVG 字符串)。SVG 示例如下:
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="455px" height="256px" viewBox="0 0 455 256" preserveAspectRatio="xMidYMid meet"> <g transform="translate(0,256) scale(0.1,-0.1)" fill="#000000"> <path d="M0 1280 l0 -1280 694 0 695 0 19 53 c11 28 36 84 55 122 20 39 45 104 56 145 18 67 48 157 96 290 9 25 22 52 30 60 42 49 89 220 99 363 7 100 51 230 89 264 12 11 63 33 112 47 50 15 117 35 150 45 56 17 60 20 63 51 3 30 -10 73 -61 195 -11 28 -24 70 -27 95 -4 25 -13 66 -21 92 -10 38 -11 61 0 125 14 91 17 187 7 224 -6 18 -3 31 9 42 20 21 68 22 84 3 25 -30 66 -29 133 5 112 57 218 33 288 -65 19 -27 61 -78 92 -113 36 -40 60 -77 64 -98 7 -37 -8 -121 -31 -169 -8 -17 -21 -59 -29 -94 -8 -34 -23 -77 -35 -95 -68 -108 -71 -126 -23 -164 20 -16 69 -47 107 -68 39 -20 106 -61 150 -90 44 -29 99 -62 122 -74 23 -11 48 -30 56 -41 11 -16 21 -19 40 -15 38 10 91 -11 132 -53 46 -47 57 -73 44 -110 -22 -65 -123 -81 -154 -24 l-14 27 -1 -35 c0 -19 7 -67 15 -105 8 -39 15 -86 15 -105 1 -33 4 -36 65 -64 119 -53 150 -110 120 -218 -17 -63 -87 -220 -111 -248 -12 -15 -114 -190 -114 -196 0 -2 331 -4 735 -4 l735 0 0 1280 0 1280 -2275 0 -2275 0 0 -1280z"/> </g> </svg>
上述 SVG 效果图如下:
接收并处理蒙版:您需要通过代理回调接收这个 SVG 字符串,并将其转换为一个图片 UIImage。
应用为视图遮罩:最后,将这张实时更新的蒙版图片,设置为您的弹幕渲染视图的 maskView。iOS 系统会自动根据 maskView 的不透明区域来决定弹幕视图哪些部分应该被显示,从而实现弹幕的动态遮挡效果。
UIView)。"NeedBarrageMask" 字段。在初始化 TTVideoEngine 和播放过程中,通过 setOptionForKey 方法开启相关功能开关。
// 1. 在 TTVideoEngine 初始化时,开启蒙版弹幕的功能线程。 // 建议根据一个全局配置来决定是否开启。 [self.videoEngine setOptionForKey:VEKeyPlayerEnableBarrageMaskThread_BOOL value:@(YES)]; // ... 在播放器准备播放时 ... // 2. 在视频播放过程中,您可以随时通过此开关控制蒙版的开启或关闭。 // isEnabled 是您自己的业务逻辑变量,例如用户点击了“开启/关闭蒙版”按钮。 BOOL isEnabled = YES; [self.videoEngine setOptionForKey:VEKKeyPlayerBarrageMaskEnabled_BOOL value:@(isEnabled)];
根据您的播放方式,设置视频源和蒙版源。
对于 Vid 播放源,SDK 会自动关联蒙版信息,您无需额外设置蒙版 URL。
// 与普通 Vid 播放的设置完全相同。 TTVideoEngineVidSource *source = [[TTVideoEngineVidSource alloc] initWithVid:self.vid playAuthToken:self.playAuthToken resolution:resolution]; [self.engine setVideoEngineVideoSource:source];
对于 Url 播放源,您需要额外通过 setBarrageMaskURL: 方法指定蒙版文件的 URL。
// 1. 设置视频播放源 TTVideoEngineUrlSource *source = [[TTVideoEngineUrlSource alloc] initWithUrl:videoUrl cacheKey:cacheKey]; [self.engine setVideoEngineVideoSource:source]; // 2. 设置与该视频关联的蒙版文件 URL [self.engine setBarrageMaskURL:maskUrl];
您需要设置 TTVideoEngineMaskDelegate 代理,以实时接收 SDK 回调的 SVG 蒙版信息,并将其渲染到一个用于做遮罩的 UIImageView 上。
请确保您的项目中已集成了 SVGKit 库,用于解析 SVG。
// Podfile pod 'SVGKit'
在您的播放器 UIViewController 中,实现代理回调方法。
#import <SVGKit/SVGKit.h> // 确保您的 ViewController 遵循 TTVideoEngineMaskDelegate 协议 // @interface YourViewController () <TTVideoEngineMaskDelegate> #pragma mark - TTVideoEngineMaskDelegate /** * 当有新的蒙版信息时,此方法会被调用。 * @param videoEngine 播放器实例 * @param svg 当前帧的蒙版 SVG 字符串 * @param pts 当前帧的时间戳 */ - (void)videoEngine:(TTVideoEngine *)videoEngine onMaskInfoCallBack:(NSString *)svg pts:(NSUInteger)pts { // SVG 解析和 UIImage 转换是耗时操作,应在后台线程执行。 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 1. 使用 SVGKit 将 SVG 字符串转换为 SVGKImage 对象。 SVGKImage *svgkImage = [SVGKImage imageWithData:[svg dataUsingEncoding:NSUTF8StringEncoding]]; // 获取转换后的 UIImage。 UIImage *maskUIImage = svgkImage.UIImage; // 更新 UI 必须返回主线程。 dispatch_async(dispatch_get_main_queue(), ^{ if (maskUIImage) { // 2. 将生成的图片设置为我们遮罩视图的内容。 // self.maskView 是一个 UIImageView 实例,专门用于承载蒙版。 self.maskView.image = maskUIImage; } }); }); }
将承载着实时蒙版图片的 maskView 设置为您的弹幕渲染视图的 maskView 属性。
// 设置 maskView 之前先根据视频内容更新 frame // Frame 计算参考: // x = (playerView.bounds.size.width - videoWidth) / 2 // y = (playerView.bounds.size.height - videoHeight) / 2 // convertRect:toView: _maskView.frame = CGRectMake(x, y, videoWidth, videoHeight); // renderView:弹幕的渲染视图 // _maskView: 上述获取到的包含蒙版信息的 mask view. [renderView setMaskView:_maskView];
至此,蒙版弹幕功能的核心流程已全部完成。
合理的视图层级是保证功能正常显示和交互的前提。开发者可根据弹幕是否需要与播放器控制层进行交互,来选择适合当前业务的视图层级。以下提供两种常见的层级结构(由下至上)供您参考:
第一种(推荐):
PlayerView ├─ 视频渲染内容 (Video Content) ├─ PlayerControlsView <-- 播放器控制层 └─ DanmakuContainerView <-- 弹幕的容器视图 └─ danmakuRenderView <-- 您的弹幕渲染视图,其 maskView 已被设置
第二种:
PlayerView ├─ 视频渲染内容 (Video Content) ├─ DanmakuContainerView <-- 弹幕的容器视图 │ └─ danmakuRenderView <-- 您的弹幕渲染视图,其 maskView 已被设置 └─ PlayerControlsView <-- 播放器控制层(按钮、进度条等)
当设备旋转或播放器视图尺寸发生变化时,视频内容的实际渲染区域也会随之改变。此时,您必须重新计算视频内容的 frame,并更新 maskView 的 frame,以确保蒙版与视频内容始终保持对齐。
_maskView.frame = CGRectMake(x, y, videoWidth, videoHeight); // 必要时调用, renderView 是弹幕的渲染视图 [renderView setNeedsLayout]; [renderView layoutIfNeeded];