DanmakuRenderEngine
DanmakuRenderEngine is a lightweight and scalable danmaku library.
- High-performance rendering and low memory usage
- Easy-to-use interface with high scalability
- Support complex custom danmaku styles
- Support custom danmaku motion modes
Apps Using DanmakuRenderEngine
Xigua Video | Douyin | Toutiao | DongCheDi |
Quick Startup
Add it to your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://artifact.bytedance.com/repository/releases/' }
}
}
Add dependency to your build.gradle:
implementation 'com.github.bytedance:danmaku-render-engine:$latest_version'
In the simplest case, it only takes four steps to start playing danmakus:
- Place
DanmakuView
to the same size as the video view in the XML layout - Get the
DanmakuController
through thedanmakuView.controller
method - Construct a
TextData
for each danmaku. Only the fields oftext
,LayerType
andshowAtTime
need to be filled in by default - Call
controller.setData()
to set the data, and then usecontroller.start()
to start playing
Timeline Synchronization
Control the Danmaku
DanmakuController
provides four methods related to the timeline control:
fun start(playTime: Long) // Play or resume from a paused state.
fun pause() // Pause and the danmakus on the screen will stop in place.
fun stop() // Stop playing which will clear the danmakus on the screen and reset the timeline.
fun clear() // Clear the danmakus that has been displayed on the screen alone.
Play Synchronization
According to the industry's common implementation logic, the callback of the player and methods of DanmakuController
are corresponding as following which can be customized to meet specific needs.
Callback of the Player | DanmakuController | Comment |
---|---|---|
RenderStart | start | |
VideoPause | pause | |
VideoResume | start | |
VideoStop | pause | Let the rest danmakus stay in place after the video is played over |
VideoReplay | stop | |
VideoError | pause | |
onSeekStart | pause | |
onSeekComplete | clear; if (isPlaying) {start} | The screen should be cleared after seek completed |
onBufferStart | pause | |
onBufferEnd | start |
Other Forms of Business
DanmakuRenderEngine library is not coupled with the video. If you want to achieve a picture danmaku similar to Qzone, the approach is similar.
Custom Motion Modes
The rendering structure of DanmakuRenderEngine is divided into different layers by motion modes. Each rendering layer manages the layout and rendering by itself, and they are controlled by the z-index
attribute.
DanmakuRenderEngine supports three motion modes by default:
- ScrollLayer: scroll from the right to the left
- TopCenterLayer: display in the top middle of the screen
- BottomCenterLayer: display in the bottom middle of the screen
Implement IRenderLayer
interface to develop your custom layer and then call DanmakuController.addRenderLayer()
to register it. After that, DanmakuRenderEngine will automatically delivery every DanmakuData
with the specify LayerType
to the corresponding Layer
for typesetting and drawing.
Custom Danmaku Style
Definition
DanmakuRenderEngine library divides a danmaku into two part during design: a data entity (DanmakuData) and a drawing entity (DrawItem):
- DanmakuData only contains the data attribute of this danmaku, which will not change unless modified manually
- DrawItem defines how this danmaku will be drawn, and also contains the attributes will be using by drawing (x/y/width/height, etc.)
- DrawItem properties will be continuously modified by RenderLayer logic during the rendering process, and will be recycled and reused after it been moved out of the screen
To make a custom danmaku you need to inherit the three classes below:
- CustomDanmakuData
- CustomDrawItem
- CustomDrawItemFactory
Then call DanmakuController.registerDrawItemFactory()
to register your custom factory, and DanmakuRenderEngine will use the registered factory to generate a CustomDrawItem
automatically when there is a custom CustomDanmakuData
.
Implementation
Based on the idea that combination is better than inheritance, DanmakuRenderEngine only provides two types of danmaku by default: text (TextData) and picture (BitmapData). Most advanced types of danmaku can be implemented by combining the two basic ones.
The custom DanmakuData and DrawItem need to be defined by the same structure. When the x, y and other attributes is changing, the new value will be distributed downward.
Take the danmaku style of the Xigua Video as an example, it has three parts: text, like icon and the number of likes:
The structure combination of DanmakuData is as follows:
Note that the canvas is accessible in the DrawItem which means you can draw directly with it. The background of the above-mentioned like icon is drawn by the canvas.
Danmaku Configuration
Requirements
Under typical circumstances, we have to modify the properties of danmaku sometimes:
- The font size and line height of a danmaku should be larger after entering the full screen, and smaller after exiting.
- The font color of a danmaku should be changed after being give a like.
- Changing the font size, transparency, etc. of all danmaku from the setting panel.
Similar requirements can mainly achieved through the DanmakuController.config
in DanmakuRenderEngine.
Cascade and Priority
DanmakuRenderEngine is designed to use the concept of cascade and priority in configuration similar to CSS according to actual needs.
Config
is the global configuration with the lowest priority, which is divided into:
val debug = DebugConfig(this)
val common = CommonConfig(this)
val text = TextConfig(this)
val underline = UnderlineConfig(this)
val scroll = ScrollLayerConfig(this)
val top = TopCenterLayerConfig(this)
val bottom = BottomCenterLayerConfig(this)
For example, TextConfig
in Config
control the global size
and color
of all text. Meanwhile the TextData
of each danmaku can also control the TextSize
and TextColor
of itself which has higher priority.
If a property is defined in TextData
, then this one takes effect.
If it is not defined in TextData
, then the one defined in Config
takes effect.
Commands and Events
For a danmaku library, there are usually two requirements:
- Let the library to do some things, such as:
- pause a danmaku.
- change the color of a danmaku.
- The library informs the outside that something has happened, such as:
- a danmaku has been displayed.
- a danmaku has been removed from the screen.
- the size of a danmaku changed.
In response to these requirements, DanmakuRenderEngine has designed two sets of mechanisms: command and event
Command
DanmakuCommand
is used to let DanmakuRenderEngine to do something which main entrance isDanmakuController.executeCommand()
, such as:
mController?.executeCommand(CMD_PAUSE_ITEM, data) // pause a danmaku
mController?.executeCommand(CMD_RESUME_ITEM, data) // resume a danmaku
mController?.executeCommand(CMD_SET_TOUCHABLE, null, false) // make danmaku view unclickable
Event
DanmakuEvent
is used to notify something has happend, you can use the method of DanmakuController.addEventListener()
to register listeners. And use the DanmakuEvent.what
to determine the type of Event
, for example:
override fun onEvent(event: DanmakuEvent) {
when (event.what) {
EVENT_DANMAKU_SHOW -> {} // event.data shows
EVENT_DANMAKU_DISMISS -> {} // event.data dismisses
EVENT_DANMAKU_REMEASURE -> {
(event.param as? RectF)?.let { rectF ->
// event.data has been remeasured and the new size is rectF
}
}
}
}
Masked Danmaku
"Masked Danmaku" is an advanced feature that can achieve the effect of "not blocking the portrait". After turning on this function, the danmakus will no longer cover the portrait, but will pass through behind the human body.
DanmakuRenderEngine has built-in the realization of "Masked Danmaku", and it only takes a few simple steps to turn it on.
- Analyze the content of the video content in advance, and calculate the closed SVG path that needs to be cut out for each frame.
- Assemble each mask
Path
data into aMaskData
structure. - Set these
MaskData
toDanmakuController
like other danmaku data - Turn on the switch:
DanmakuController.config.mask.enable = true
(The above code can refer to SmartMaskActivity.kt)
Requires attention:
- DanmakuRenderEngine only provides the implementation of "Masked Danmaku" on Android, and does not provide the function of content analyzing or mask path generating. You need to analyzing the screen content on server side by using AI tools in advance and deliver the data to client.
- DanmakuRenderEngine has no restrictions on the frame rate of the
MaskData
. But it is recommended not to be higher than 30Hz for performance and data transmission considerations (the effect is already good enough when greater than 10Hz). At the same time, DanmakuRenderEngine also supports dynamic frame rate masks by specifying the start and end time of eachMaskData
.
Q&A
DanmakuView
?
Q1:Can I dynamically insert the A:Yes. You can create a DanmakuView
and overlay it on the top of the video screen whenever you want, as long as the size of DanmakuView
and the video screen are always the same (for example, when switching full screen state).
Q2:Can I set or add danmaku data after play it?
A:Yes. But the danmakus which showAtTime
is before the set time will not be displayed this time (if you seek forward, they can still be displayed).
License
Copyright (c) 2022 ByteDance Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.