Mobile Development 22 min read

Implementation of a Flexible Danmaku (Bullet Comment) System for Long‑Form Video on iOS

This article explains the design and implementation of a flexible, high‑performance bullet‑comment (danmaku) system for long‑form video on iOS, covering canvas rendering, track management, polling, view reuse, animation control, gradient effects, data handling, and integration with ads and user interactions.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Implementation of a Flexible Danmaku (Bullet Comment) System for Long‑Form Video on iOS

The article introduces the evolution of bullet comments (danmaku) from short‑video platforms to long‑form video, emphasizing the need for non‑intrusive, readable overlays that do not obstruct the viewing experience.

Technical implementation is described in detail. A unified Render class draws a canvas that abstracts screen size and orientation, using a Render object to control drawing. Platform‑specific differences (iPhone vs. iPad) such as font size are handled via a config class, keeping the canvas logic device‑agnostic.

To manage the horizontal flow of comments, the concept of a “track” is introduced. Each track represents a virtual line that holds an array of comment models, similar to music‑track logic in rhythm games. Tracks allow per‑line speed control, enabling effects like alternating odd/even line speeds.

Comment display is driven by a polling loop using CADisplayLink with frameInterval = 60 (once per second). The loop checks the dictionary of time‑keyed comment models, finds a free position on a track, and hands the comment to that track for rendering. If all tracks are occupied, the comment is discarded.

Views are built with plain UIView objects; layout and hierarchy are handled by UIKit while actual rendering is performed by the underlying CALayer . This separation follows Apple’s design guidelines.

A reuse pool, analogous to UITableView ’s cell reuse, stores off‑screen comment views. When a comment leaves the visible area it is placed in the pool for later reuse, avoiding costly view creation and removal.

System comments (e.g., introductory prompts) are displayed with higher precision using a faster CADisplayLink (frameInterval = 10) and the presentationLayer of the layer to obtain the exact on‑screen frame.

Gradient ("colorful") comments are rendered by drawing a gradient image with CAGradientLayer onto a UIImage and assigning it to the label’s textColor , avoiding off‑screen rendering that would hurt performance.

CGFloat scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(imageSize, NO, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
CGPoint start = CGPointMake(0.0, 0.0);
CGPoint end = CGPointMake(imageSize.width, 0.0);
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(gradient);
CGContextRestoreGState(context);
UIGraphicsEndImageContext();

Pause and resume of comments are handled via the CAMediaTiming protocol. The implementation adjusts layer.speed , timeOffset , and beginTime to avoid visual jumps, adding a small 0.05 s offset as a practical fix.

- (void)pauseAnimation {
    if (self.layer.speed == 0.f) { return; }
    CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
    self.layer.speed = 0.f;
    self.layer.timeOffset = pausedTime + 0.05f;
}

- (void)resumeAnimation {
    if (self.layer.speed == 1.f) { return; }
    CFTimeInterval pausedTime = self.layer.timeOffset;
    self.layer.speed = 1.0;
    self.layer.timeOffset = 0.0;
    self.layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    self.layer.beginTime = timeSincePause;
}

Sending a comment searches tracks from top to bottom using the presentationLayer to ensure the rightmost point is still on‑screen with a minimum gap (e.g., 10 pt). If no track has space, the comment is dropped, except for user‑generated comments which are prioritized and inserted ahead of others.

Comment configuration parameters differ between iPhone and iPad; a BarrageConfig class centralizes these values, allowing the Render class to layout comments without platform‑specific conditionals.

Likes and long‑press gestures are attached to the shared Render view rather than each comment view. Hit‑testing uses the presentationLayer to locate the target comment.

- (void)singleTapHandle:(UITapGestureRecognizer *)tapGestureRecognizer {
    CGPoint touchLocation = [tapGestureRecognizer locationInView:self.barrageRender];
    __block BOOL barrageLiked = NO;
    [self enumerateObjectsUsingBlock:^(SVBarrageItemLabelView *itemLabel, NSUInteger index, BOOL *stop) {
        if ([itemLabel.layer.presentationLayer hitTest:touchLocation] && !barrageLiked) {
            barrageLiked = YES;
            [self likeAction:itemLabel withTouchLocation:touchLocation];
            *stop = YES;
        }
    }];
}

Advertising comments are displayed in a dedicated high‑z‑order view supplied to the ad SDK, keeping them separate from the main Render hierarchy.

Layer management is abstracted by a custom manager inheriting from NSObject , which assigns views to predefined layers (e.g., controls, render, ads) based on an enum configuration.

Data handling follows a local‑server pattern: comment data is fetched in 5‑minute shards, stored in a local WCDB database, and read in 10‑second batches into a time‑keyed dictionary. The UI queries this dictionary every second to feed new comments to appropriate tracks.

For occlusion‑avoidance, a semantic‑segmentation pipeline (using Google’s Mask_RCNN ) extracts human silhouettes from video frames. The client then masks the video to keep comments below the person, either by compositing two video players or by applying an SVG mask‑image via CSS on the web.

Overall, the solution balances performance (reusing views, minimizing off‑screen rendering), flexibility (config‑driven parameters, platform‑agnostic canvas), and extensibility (support for system comments, ads, likes, and occlusion‑avoidance).

mobileanimationRenderingiOSvideodanmaku
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.