20、视频水印与字幕:文字叠加、图片水印、SRT字幕解析与渲染、动态水印
视频水印和字幕,说白了就是给视频画面「加料」。一个是防搬运、做品牌曝光,另一个是提升观看体验。我在做短视频SDK的时候,这两块几乎是天天打交道。今天咱们就一次性把它们讲透。
20.1 文字水印叠加
文字水印是最基础的操作。Android上通常用Canvas直接绘制,或者用MediaCodec配合OpenGL做GPU加速。我个人习惯用Canvas,简单直接,适合大多数场景。
核心思路:从视频帧中解码出Bitmap,然后在Bitmap上绘制文字,最后再编码回去。
// 文字水印绘制示例
public Bitmap addTextWatermark(Bitmap source, String text, int x, int y) {
Bitmap result = source.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setTextSize(48);
paint.setShadowLayer(2f, 1f, 1f, Color.BLACK);
canvas.drawText(text, x, y, paint);
return result;
}
嗯,这里要注意:文字位置要避开画面主体。我在项目中遇到过,把水印放在右下角,结果用户视频里正好有个LOGO,叠在一起特别难看。后来我改成左上角+半透明,效果好很多。
20.2 图片水印叠加
图片水印比文字稍微复杂一点。你得考虑图片的缩放、旋转、透明度。说白了就是两个Bitmap的合成。
// 图片水印叠加
public Bitmap addImageWatermark(Bitmap source, Bitmap watermark,
int x, int y, float alpha) {
Bitmap result = source.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setAlpha((int) (alpha * 255));
canvas.drawBitmap(watermark, x, y, paint);
return result;
}
我的经验:图片水印最好用PNG格式,支持透明通道。JPG会有白底,特别丑。另外,水印图片不要太大,建议控制在视频宽度的10%以内。
20.3 SRT字幕解析与渲染
SRT字幕格式其实很简单。你想想看,它就是一个文本文件,里面按时间轴分段。解析起来不复杂,但渲染时要注意同步。
SRT文件格式长这样:
1
00:00:01,000 --> 00:00:04,000
这是第一句字幕
2
00:00:05,000 --> 00:00:08,500
这是第二句字幕,带换行
解析代码也不复杂:
public class SrtParser {
public static List<SubtitleItem> parse(String srtContent) {
List<SubtitleItem> items = new ArrayList<>();
String[] blocks = srtContent.split("\\n\\n");
for (String block : blocks) {
String[] lines = block.split("\\n");
if (lines.length < 3) continue;
// 解析时间轴
String[] times = lines[1].split(" --> ");
long start = parseTime(times[0]);
long end = parseTime(times[1]);
// 解析字幕文本
StringBuilder text = new StringBuilder();
for (int i = 2; i < lines.length; i++) {
text.append(lines[i]).append("\n");
}
items.add(new SubtitleItem(start, end, text.toString().trim()));
}
return items;
}
}
我曾经踩过的坑:SRT文件编码问题。很多用户上传的字幕是GBK编码,而Android默认是UTF-8。解析出来全是乱码。后来我加了个编码检测逻辑,优先尝试UTF-8,失败再试GBK。
渲染字幕时,我建议用自定义View,在视频播放的同步回调里更新字幕文本。用Handler或者Choreographer都可以,关键是要跟视频帧率对齐。
20.4 动态水印
动态水印,就是水印会动。比如从左往右飘,或者旋转缩放。这个用Canvas也能做,但性能一般。我推荐用OpenGL做,效率高很多。
动态水印的核心逻辑:
- 位置变化:每一帧更新水印的x、y坐标
- 旋转变化:每一帧增加旋转角度
- 透明度变化:淡入淡出效果
- 缩放变化:忽大忽小,增加视觉干扰
// 动态水印位置计算
float time = (System.currentTimeMillis() - startTime) / 1000f;
float x = (float) (Math.sin(time * 0.5) * 100 + 200);
float y = (float) (Math.cos(time * 0.3) * 50 + 300);
float rotation = time * 30; // 每秒转30度
float alpha = (float) (Math.sin(time * 0.8) * 0.3 + 0.5); // 0.2~0.8变化
我个人的建议:动态水印不要做得太花哨,否则影响观看体验。一般用半透明+缓慢移动就够了。防搬运的话,可以加多个水印,分布在画面不同位置。
20.5 知识体系总览
下面这张图,是我梳理的本章知识结构。你可以看到,水印和字幕虽然都是叠加,但技术路径完全不同。
20.6 性能对比
不同的叠加方式,性能差异很大。我整理了一张表,方便你选型时参考。
| 方案 | CPU消耗 | GPU消耗 | 适用场景 |
|---|---|---|---|
| Canvas绘制文字 | 低 | 无 | 简单文字水印 |
| Bitmap合成 | 中 | 无 | 图片水印 |
| OpenGL渲染 | 低 | 中 | 动态水印、多水印 |
| MediaCodec+Surface | 低 | 高 | 实时视频处理 |
注意:如果你要做实时视频处理(比如直播),千万别用Bitmap方式。每一帧都做Bitmap拷贝,内存和CPU都扛不住。用OpenGL或者Shader才是正道。
好了,水印和字幕这块就聊到这儿。核心就三点:解析要准、渲染要快、位置要稳。你动手试试,遇到问题再回来翻翻这篇文章。