
在直播APP开发中,“接入一个视频美颜SDK”看起来似乎只是增加几个接口的问题,但真正进入开发阶段后,很快就会发现,美颜并不是一个孤立的功能。
摄像头负责产生原始视频帧,视频处理模块负责对帧数据进行加工,美颜SDK负责完成实时图像处理,编码器再将处理后的数据压缩,最终通过直播链路发送出去。
因此,一个比较完整的处理流程可以抽象为:
Camera → Video Frame → Beauty SDK → Renderer → Encoder → Push Stream
本文就从这个数据链路出发,分析直播APP源码与视频美颜SDK在实际开发中的配合方式。
以Android为例,Camera2可以通过 SurfaceTexture 或 ImageReader 等方式获取摄像头数据。Android官方文档也明确提到,如果应用需要进行OpenGL ES相关的视频处理,可以使用 SurfaceTexture;如果需要直接访问图像数据,则可以使用 ImageReader 和 YUV_420_888。
从开发角度看,可以先把一帧视频简单抽象成:
class VideoFrame {
int width;
int height;
long timestamp;
ByteBuffer data;
int rotation;
}摄像头不断产生这样的Frame,后续模块则对Frame进行处理。
真正需要注意的是:不要把每一帧视频都当成普通Bitmap进行处理。
直播场景通常是连续的视频流,如果每帧都进行CPU侧的格式转换、Bitmap创建和回收,很容易产生大量内存分配和GC压力。
因此,实时直播场景更加倾向于使用GPU纹理进行处理。
比较合理的架构是:
Camera
↓
Camera Frame
↓
Texture / YUV
↓
Beauty SDK
↓
Processed Texture
↓
Preview
↓
Video Encoder
↓
RTMP / WebRTC / RTC这里有一个很重要的概念:
美颜SDK通常不是负责直播推流的。
它的主要工作是接收输入的视频帧或者纹理,对图像进行处理,然后输出处理后的结果。
例如:
int processedTexture =
beautySDK.process(
inputTexture,
width,
height
);得到新的Texture之后,再交给后续渲染或者编码模块。
这种方式可以避免业务层把摄像头、美颜、编码、推流全部耦合在一起。
目前不少实时音视频SDK也提供视频前处理回调,让开发者在编码之前插入第三方图像处理逻辑。例如部分Android音视频SDK会直接回调OpenGL纹理,并要求处理后的纹理继续返回给音视频SDK。
假设直播画面为1080×1920,30FPS。
这意味着每秒需要处理大约30帧图像。
如果全部通过CPU进行复杂的人脸检测、磨皮、滤镜以及几何变形,CPU压力会迅速增加。
而OpenGL ES可以将大量图像计算交给GPU完成。
简单来看:
CPU
│
├── 摄像头控制
├── 参数管理
├── 业务逻辑
│
└── GPU指令
↓
OpenGL ES
↓
Texture Processing
↓
Beauty RenderingAndroid图形系统本身也是围绕EGL和OpenGL ES进行GPU渲染的,OpenGL ES负责图形绘制,而EGL负责创建上下文和连接渲染目标。
因此,美颜SDK如果采用Texture作为输入输出,可以减少CPU与GPU之间不必要的数据搬运。
下面用一个简化的Java代码模拟直播APP中的视频前处理流程:
public class BeautyProcessor {
private BeautyEngine beautyEngine;
public void init(Context context) {
beautyEngine = new BeautyEngine(context);
beautyEngine.setSmoothLevel(0.6f);
beautyEngine.setWhiteLevel(0.3f);
beautyEngine.setFaceSlimLevel(0.2f);
}
public int process(
int textureId,
int width,
int height,
long timestamp) {
if (beautyEngine == null) {
return textureId;
}
return beautyEngine.processTexture(
textureId,
width,
height,
timestamp
);
}
public void release() {
if (beautyEngine != null) {
beautyEngine.release();
beautyEngine = null;
}
}
}这里的 BeautyEngine 只是示例接口。
实际项目中,不同视频美颜SDK提供的初始化方式、纹理类型和参数名称都会有所不同,因此不能直接照搬接口名称。
核心思想却是一致的:
输入Texture → SDK处理 → 输出Texture。
假设直播APP本身已经存在视频编码模块,那么可以增加一个视频前处理层:
public int onVideoFrame(
int textureId,
int width,
int height,
long timestamp) {
int outputTexture = beautyProcessor.process(
textureId,
width,
height,
timestamp
);
return outputTexture;
}随后:
Camera Texture
↓
onVideoFrame()
↓
BeautyProcessor
↓
Processed Texture
↓
Video Encoder
↓
Push这种架构最大的好处,是直播业务层不需要知道“磨皮到底怎么实现”。
直播源码只负责调用:
processedTexture = beauty.process(texture);至于人脸检测、滤镜、磨皮、美型等算法,则由美颜模块内部完成。
并不是所有美颜SDK都采用OpenGL Texture作为输入。
有些SDK直接接收YUV数据,例如:
Y Plane
U Plane
V Plane这种情况下,Camera2可以通过 ImageReader 获取YUV_420_888数据。
示例代码:
imageReader.setOnImageAvailableListener(
reader -> {
Image image = reader.acquireLatestImage();
if (image == null) {
return;
}
try {
Image.Plane[] planes = image.getPlanes();
ByteBuffer y = planes[0].getBuffer();
ByteBuffer u = planes[1].getBuffer();
ByteBuffer v = planes[2].getBuffer();
beautySDK.processYUV(
y,
u,
v,
image.getWidth(),
image.getHeight()
);
} finally {
image.close();
}
},
cameraHandler
);这里特别值得注意的是 acquireLatestImage()。
实时视频处理中,如果处理速度赶不上摄像头产生速度,没有必要把几秒前的旧画面全部处理完。Android官方文档也指出,acquireLatestImage() 更适合实时处理场景,因为它可以丢弃队列中较旧的图像,只获取最新帧。
这也是直播APP开发中比较典型的“实时性优先”思路。
这是很多初次做直播开发的人容易忽略的地方。
美颜SDK输出的通常只是处理后的图像数据或者Texture。
后面还需要经过:
图像处理
↓
颜色空间处理
↓
编码
↓
码率控制
↓
封装
↓
网络传输如果使用Texture作为中间数据,编码模块还需要与GPU处理链路正确衔接。
例如:
int texture = cameraTexture;
texture = beauty.processTexture(
texture,
width,
height,
timestamp
);
encoder.encodeTexture(
texture,
timestamp
);实际项目中的Encoder接口会根据采用的编码框架而变化,但整体数据关系基本如此。
美颜处理并不适合随意放在主线程。
如果直接在UI线程执行:
beauty.processTexture(texture);一旦某一帧处理耗时增加,就可能影响页面滑动、按钮响应甚至造成ANR。
更合理的设计是:
UI Thread
│
├── 修改美颜参数
│
↓
GL / Video Processing Thread
│
├── Texture输入
├── Beauty处理
├── Render
└── Encoder同时还要注意OpenGL上下文和线程之间的关系。
OpenGL ES资源通常与当前GL Context以及线程状态有关,因此不能简单地在一个线程创建Texture,然后随意在另一个线程操作。Android图形系统文档也说明,GLES操作依赖当前线程中的GL上下文。
如果第三方美颜SDK本身对线程有明确要求,就应该按照SDK的线程模型设计处理队列,而不是直接把回调扔给任意线程。
如果从工程架构角度设计,可以将直播APP的视频部分拆成几个独立模块:
camera/
CameraManager
beauty/
BeautyProcessor
render/
VideoRenderer
encode/
VideoEncoder
push/
StreamPusher
player/
LivePlayer业务层只负责:
camera.start();
beauty.enable(true);
encoder.start();
pusher.start();而底层负责具体的视频处理。
这样的模块化设计还有一个好处:以后更换美颜SDK时,不需要大面积修改直播业务代码,只需要替换BeautyProcessor这一层的实现。
从技术实现来看,直播APP源码与视频美颜SDK并不是简单的“SDK安装完成就结束”。
真正完整的链路应该考虑:
摄像头采集 → 视频帧 → 格式转换 → GPU/CPU处理 → 美颜 → 预览 → 编码 → 推流 → 播放。
其中任何一个环节处理不当,都可能表现为卡顿、延迟、花屏、颜色异常、旋转方向错误或者设备发热。
因此,在进行直播APP开发时,与其单独关注“美颜功能有多少”,不如先把视频数据从采集端到编码端的整个生命周期梳理清楚。
当数据流、线程模型、Texture/YUV格式以及编码接口全部确定以后,视频美颜SDK实际上只是这条链路中的一个处理节点。
这也是直播类APP开发中比较值得重视的一点:
真正稳定的直播体验,并不是某一个SDK单独完成的,而是整个视频处理链路共同完成的。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。