本文將為您介紹如何?外部視頻渲染。
功能介紹
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層對外調用傳遞,提升處理效率,避免記憶體流失。
// TODO:作資源釋放相關工作
mAliRtcEngine.unRegisterVideoSampleObserver()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;
}
}