本文将为您介绍如何实现外部视频渲染。
功能介绍
ARTC 内置了经过市场广泛验证的视频渲染模块,推荐客户优先使用,以确保获得稳定、高效的视频播放体验。
对于已具备成熟自研渲染能力的客户,或在色彩精度、帧率控制等方面有特殊需求的场景,ARTC SDK 也提供灵活的接口支持,允许接入自定义视频渲染模块,满足多样化的业务需求。
前提条件
在设置视频配置之前,请确保达成以下条件:
技术原理
远端视频渲染
本地视频渲染-Buffer
本地视频渲染-textureID
功能实现
Android端实现
1 注册监听
首先通过AliRtcEngine所提供的registerVideoSampleObserver接口来注册自定义处理观察类,然后实现这个抽象类AliRtcEngine.AliRtcVideoObserver的各方法。即可在AliRtcEngine全流程链路中,进行美颜处理。
注册监听接口AliRtcVideoObserver代码示例:
mAliRtcEngine.registerVideoSampleObserver(mAliRtcVideoObserver);根据实际业务需要,实现抽象接口类。ARTC支持本地视频采集后、本地编码前、远程视频解码后三个阶段的自定义处理。
public enum AliRtcVideoObserPosition{
/*! 采集视频数据,对应输出回调 onLocalVideoSample */
AliRtcPositionPostCapture(1),
/*! 渲染视频数据,对应输出回调 onRemoteVideoSample */
AliRtcPositionPreRender(2),
/*! 编码前视频数据,对应输出回调 onPreEncodeVideoSample */
AliRtcPositionPreEncoder(4);
}public static abstract class AliRtcVideoObserver {
// 订阅的本地采集视频数据回调
public boolean onLocalVideoSample(AliRtcVideoSourceType sourceType, AliRtcVideoSample videoSample){
// TODO: 如果需要本地视频采集环节,需要美颜特效处理,在此处处理。
}
// 订阅的远端视频数据回调
public boolean onRemoteVideoSample(String callId,AliRtcVideoSourceType sourceType, AliRtcVideoSample videoSample){
// TODO: 如果需要远端拉取后的画面在显示之前,需要美颜特效处理,在此处处理。
}
// 订阅的本地编码前视频数据回调
public boolean onPreEncodeVideoSample(AliRtcVideoSourceType sourceType, AliRtcVideoSample videoRawData){
// TODO: 如果需要在本地视频画面进行编码之前,需要美颜特效处理,在此处处理。
}
...
public int onGetObservedFramePosition(){
// TODO: 此处根据业务需要,参照上面定义的AliRtcVideoObserPosition值,指定需要回调的处理时机。
// 例如,需要在采集和预渲染前进行自定义处理,定义以下值。
// return AliRtcVideoObserPosition.AliRtcPositionPostCapture.getValue() | AliRtcVideoObserPosition.AliRtcPositionPreRender.getValue();
}
}说明
美颜SDK的引入使用,根据业务需要,分别在对应的接口处理视频流即可。
关闭监听
在不需要自定义处理的情况下,可通过关闭自定义处理监听,来减少SDK层对外调用传递,提升处理效率,避免内存泄漏。
mAliRtcEngine.unRegisterVideoSampleObserver(
// TODO:作资源释放相关工作
)2 自定义处理
上述回调接口都可调用handleBeautyProcess方法实现。
private boolean handleBeautyProcess(AliRtcEngine.AliRtcVideoSample videoSample) {
if (!isAdvanceBeautifyOn) { // 是否开启美颜
return false;
}
if (mQueenBeautyImp == null) {
mQueenBeautyImp = new QueenBeautyImp(getContext(), videoSample.glContex);
}
return mQueenBeautyImp.onBeautyProcess(videoSample);
}说明
QueenBeautyImp是对QueenBeautyEffector的简单包装类
2.1 创建美颜处理器
创建方法如下:
// 增加同步锁,防止创建多个
private synchronized void ensureQueenEngine(Context context, long glShareContext) {
if (mQueenBeautyEffector == null) {
try {
QueenConfig queenConfig = new QueenConfig();
bool isNeedCreateNewThread = glShareContext != 0;// 是否需要创建独立线程,纹理模式推荐为true,buffer模式推荐为false
bool isNeedCreateNewGLContext = true; // 是否需要创建GL上下文,纹理模式保持与isNeedCreateNewThread的值一致,buffer模式推荐为true。
queenConfig.withNewGlThread = isNeedCreateNewThread;
queenConfig.withContext = isNeedCreateNewGLContext;
queenConfig.shareGlContext = glShareContext;
// queenConfig.enableDebugLog = true; // 调试功能-打开日志
mQueenBeautyEffector = new QueenBeautyEffector(context, queenConfig);
// 高级美颜调试功能
// mQueenBeautyEffector.getEngine().enableFacePointDebug(true); // 开启人脸关键点调试
// mQueenBeautyEffector.getEngine().enableFaceDetectGPUMode(false); // 关闭人脸检测GPU模式
} catch (Exception ex) {
ex.printStackTrace();
}
}
}2.2 美颜参数设置
private void updateQueenEngineParams() {
mQueenBeautyEffector.onUpdateParams(() -> {
QueenEngine queenEngine = mQueenBeautyEffector.getEngine();
// 磨皮&锐化,共用一个功能开关
queenEngine.enableBeautyType(BeautyFilterType.kSkinBuffing, true);//磨皮开关
queenEngine.setBeautyParam(com.aliyun.android.libqueen.models.BeautyParams.kBPSkinBuffing, 0.85f); //磨皮 [0,1]
queenEngine.setBeautyParam(com.aliyun.android.libqueen.models.BeautyParams.kBPSkinSharpen, 0.2f); //锐化 [0,1]
// 美白&红润,共用一个功能开关
queenEngine.enableBeautyType(BeautyFilterType.kSkinWhiting, true);//美白开关
queenEngine.setBeautyParam(BeautyParams.kBPSkinWhitening, 0.5f); //美白范围 [0,1]
// 大眼,瘦脸
queenEngine.enableBeautyType(BeautyFilterType.kFaceShape, true);
queenEngine.updateFaceShape(FaceShapeType.typeBigEye,1.0f);
queenEngine.updateFaceShape(FaceShapeType.typeCutFace,1.0f);
});
}2.3 处理帧
根据回调数据类型,区分处理是纹理,还是buffer。
// 增加同步锁,防止多线程下mQueenBeautyEffector已被销毁
public synchronized boolean onBeautyProcess(AliRtcEngine.AliRtcVideoSample videoSample) {
// 更新美颜参数
updateQueenEngineParams();
boolean result = false;
if (videoSample.glContex != 0 && videoSample.textureid > 0) {
// 纹理模式
result = onProcessBeautyTexture(videoSample);
} else {
// buffer模式
result = onProcessBeautyBuffer(videoSample);
}
return result;
}纹理回调的处理:
private boolean onProcessBeautyTexture(AliRtcEngine.AliRtcVideoSample videoSample) {
boolean result = false;
boolean isOesTexture = videoSample.format == AliRtcEngine.AliRtcVideoFormat.AliRtcVideoFormatTextureOES;
// 因Android相机采集纹理默认是旋转270度后的横屏画面,Queen-sdk内部会自动进行宽高互换。
// 但此处videoSample回调的宽高,rtc-sdk内部也已进行修正,因此此处需要手动进行宽高互换
int w = isOesTexture ? videoSample.height : videoSample.width;
int h = isOesTexture ? videoSample.width : videoSample.height;
int newTextId = mQueenBeautyEffector.onProcessTexture((int)videoSample.textureid, isOesTexture, videoSample.matrix, w, h, 270, 0, 0);
if (newTextId != videoSample.textureid) { // 0-QueenResult.QUEEN_OK
// 修改纹理id
videoSample.textureid = newTextId;
videoSample.format = AliRtcEngine.AliRtcVideoFormat.AliRtcVideoFormatTexture2D;
result = true;
}
return result;
}buffer回调的处理:
private boolean onProcessBeautyBuffer(AliRtcEngine.AliRtcVideoSample videoSample) {
boolean result = false;
int queenResult = mQueenBeautyEffector.onProcessDataBuf(videoSample.data, videoSample.data, ImageFormat.I420, videoSample.width, videoSample.height, 0, 0, 0, 0);
if (queenResult == 0) {
result = true;
}
return result;
}3 退出销毁
在离会,或者离开视频通话界面时,及时销毁自定义处理引擎。
// 增加同步锁,防止多线程下mQueenBeautyEffector已被销毁
public synchronized void release() {
if (mQueenBeautyEffector != null) {
mQueenBeautyEffector.onReleaseEngine();
mQueenBeautyEffector = null;
}
}iOS端实现
该文章对您有帮助吗?