Topik ini menjelaskan cara mengimplementasikan rendering video kustom.
Deskripsi fitur
Alibaba Real-Time Communication (ARTC) mencakup modul rendering video yang telah terbukti di pasar. Modul bawaan ini menyediakan pengalaman pemutaran video yang stabil dan efisien.
Jika Anda memiliki modul rendering buatan sendiri yang sudah matang atau kebutuhan khusus terkait akurasi warna dan kontrol laju frame, kit pengembangan perangkat lunak (SDK) ARTC menyediakan antarmuka yang fleksibel. Antarmuka ini memungkinkan Anda mengintegrasikan modul rendering video kustom untuk memenuhi kebutuhan bisnis Anda.
Prasyarat
Sebelum mengonfigurasi pengaturan video, pastikan prasyarat berikut terpenuhi:
Anda telah membuat aplikasi ApsaraVideo Real-time Communication dengan Akun Alibaba Cloud yang valid. Untuk informasi lebih lanjut, lihat Create an application. Anda juga harus memperoleh App ID dan App Key dari Application Management console.
Anda telah mengintegrasikan SDK ARTC ke dalam proyek Anda dan mengimplementasikan fitur komunikasi audio dan video dasar. Untuk informasi lebih lanjut tentang integrasi SDK, lihat Download and integrate the SDK. Untuk mempelajari cara mengimplementasikan panggilan audio dan video, lihat Implement an audio and video call.
Prinsip teknis
Rendering video remote
Rendering video lokal berbasis buffer
Rendering video lokal berbasis ID texture
Implementasi
Implementasi Android
1. Daftarkan observer
Pertama, panggil metode registerVideoSampleObserver dari AliRtcEngine untuk mendaftarkan observer pemrosesan kustom. Lalu, implementasikan metode dari kelas abstrak AliRtcEngine.AliRtcVideoObserver. Hal ini memungkinkan Anda menerapkan retouching di seluruh pipeline pemrosesan AliRtcEngine.
Contoh pendaftaran AliRtcVideoObserver:
mAliRtcEngine.registerVideoSampleObserver(mAliRtcVideoObserver);Implementasikan antarmuka abstrak sesuai kebutuhan. ARTC mendukung pemrosesan kustom pada tiga tahap: setelah penangkapan video lokal, sebelum encoding lokal, dan setelah decoding video remote.
public enum AliRtcVideoObserPosition{
/*! Data video yang ditangkap. Sesuai dengan callback onLocalVideoSample. */
AliRtcPositionPostCapture(1),
/*! Data video yang dirender. Sesuai dengan callback onRemoteVideoSample. */
AliRtcPositionPreRender(2),
/*! Data video sebelum encoding. Sesuai dengan callback onPreEncodeVideoSample. */
AliRtcPositionPreEncoder(4);
}public static abstract class AliRtcVideoObserver {
// Callback untuk data tangkapan video lokal yang berlangganan.
public boolean onLocalVideoSample(AliRtcVideoSourceType sourceType, AliRtcVideoSample videoSample){
// TODO: Jika diperlukan retouching dan efek selama penangkapan video lokal, proses di sini.
}
// Callback untuk data video remote yang berlangganan.
public boolean onRemoteVideoSample(String callId,AliRtcVideoSourceType sourceType, AliRtcVideoSample videoSample){
// TODO: Jika diperlukan retouching dan efek sebelum menampilkan frame yang ditarik dari ujung remote, proses di sini.
}
// Callback untuk data video lokal sebelum encoding.
public boolean onPreEncodeVideoSample(AliRtcVideoSourceType sourceType, AliRtcVideoSample videoRawData){
// TODO: Jika diperlukan retouching dan efek sebelum meng-encode frame video lokal, proses di sini.
}
...
public int onGetObservedFramePosition(){
// TODO: Tentukan waktu callback berdasarkan kebutuhan bisnis Anda. Gunakan nilai AliRtcVideoObserPosition yang didefinisikan di atas.
// Misalnya, untuk melakukan pemrosesan kustom sebelum capture dan pre-rendering, definisikan nilai berikut.
// return AliRtcVideoObserPosition.AliRtcPositionPostCapture.getValue() | AliRtcVideoObserPosition.AliRtcPositionPreRender.getValue();
}
}Untuk menggunakan Queen SDK, Anda harus memproses aliran video di antarmuka yang sesuai.
Unregister listener
Saat pemrosesan kustom tidak lagi diperlukan, unregister listener untuk mengurangi panggilan dari lapisan SDK, meningkatkan efisiensi pemrosesan, dan mencegah kebocoran memori.
// TODO: Lakukan tugas pelepasan resource.
mAliRtcEngine.unRegisterVideoSampleObserver()2. Implementasikan pemrosesan kustom
Antarmuka callback di atas dapat diimplementasikan dengan memanggil metode handleBeautyProcess.
private boolean handleBeautyProcess(AliRtcEngine.AliRtcVideoSample videoSample) {
if (!isAdvanceBeautifyOn) { // Menentukan apakah retouching diaktifkan.
return false;
}
if (mQueenBeautyImp == null) {
mQueenBeautyImp = new QueenBeautyImp(getContext(), videoSample.glContex);
}
return mQueenBeautyImp.onBeautyProcess(videoSample);
}QueenBeautyImp adalah kelas wrapper sederhana untuk QueenBeautyEffector.
Buat prosesor retouching
Buat prosesor sebagai berikut:
// Tambahkan lock sinkronisasi untuk mencegah pembuatan ganda.
private synchronized void ensureQueenEngine(Context context, long glShareContext) {
if (mQueenBeautyEffector == null) {
try {
QueenConfig queenConfig = new QueenConfig();
bool isNeedCreateNewThread = glShareContext != 0; // Menentukan apakah perlu membuat thread terpisah. Kami merekomendasikan mengatur nilai ini ke true untuk mode texture dan false untuk mode buffer.
bool isNeedCreateNewGLContext = true; // Menentukan apakah perlu membuat konteks GL. Untuk mode texture, pertahankan nilai ini konsisten dengan isNeedCreateNewThread. Untuk mode buffer, kami merekomendasikan mengatur nilai ini ke true.
queenConfig.withNewGlThread = isNeedCreateNewThread;
queenConfig.withContext = isNeedCreateNewGLContext;
queenConfig.shareGlContext = glShareContext;
// queenConfig.enableDebugLog = true; // Fitur debugging: Aktifkan log.
mQueenBeautyEffector = new QueenBeautyEffector(context, queenConfig);
// Fitur debugging retouching tingkat lanjut.
// mQueenBeautyEffector.getEngine().enableFacePointDebug(true); // Aktifkan debugging landmark wajah.
// mQueenBeautyEffector.getEngine().enableFaceDetectGPUMode(false); // Nonaktifkan mode GPU untuk pengenalan wajah.
} catch (Exception ex) {
ex.printStackTrace();
}
}
}Atur parameter retouching
private void updateQueenEngineParams() {
mQueenBeautyEffector.onUpdateParams(() -> {
QueenEngine queenEngine = mQueenBeautyEffector.getEngine();
// Penghalusan dan ketajaman kulit berbagi satu sakelar.
queenEngine.enableBeautyType(BeautyFilterType.kSkinBuffing, true); // Sakelar penghalusan kulit.
queenEngine.setBeautyParam(com.aliyun.android.libqueen.models.BeautyParams.kBPSkinBuffing, 0.85f); // Penghalusan kulit. Nilai valid: [0,1].
queenEngine.setBeautyParam(com.aliyun.android.libqueen.models.BeautyParams.kBPSkinSharpen, 0.2f); // Ketajaman. Nilai valid: [0,1].
// Pemutihan kulit dan pipi kemerahan berbagi satu sakelar.
queenEngine.enableBeautyType(BeautyFilterType.kSkinWhiting, true); // Sakelar pemutihan kulit.
queenEngine.setBeautyParam(BeautyParams.kBPSkinWhitening, 0.5f); // Pemutihan kulit. Nilai valid: [0,1].
// Pembesaran mata dan pelangsingan wajah.
queenEngine.enableBeautyType(BeautyFilterType.kFaceShape, true);
queenEngine.updateFaceShape(FaceShapeType.typeBigEye,1.0f);
queenEngine.updateFaceShape(FaceShapeType.typeCutFace,1.0f);
});
}Proses frame
Proses data sebagai texture atau buffer berdasarkan jenis data callback.
// Tambahkan lock sinkronisasi untuk mencegah mQueenBeautyEffector dihancurkan di lingkungan multi-threaded.
public synchronized boolean onBeautyProcess(AliRtcEngine.AliRtcVideoSample videoSample) {
// Perbarui parameter retouching.
updateQueenEngineParams();
boolean result = false;
if (videoSample.glContex != 0 && videoSample.textureid > 0) {
// Mode texture.
result = onProcessBeautyTexture(videoSample);
} else {
// Mode buffer.
result = onProcessBeautyBuffer(videoSample);
}
return result;
}Memproses callback tekstur:
private boolean onProcessBeautyTexture(AliRtcEngine.AliRtcVideoSample videoSample) {
boolean result = false;
boolean isOesTexture = videoSample.format == AliRtcEngine.AliRtcVideoFormat.AliRtcVideoFormatTextureOES;
// Texture yang ditangkap oleh kamera Android secara default merupakan gambar landscape yang diputar 270 derajat. Queen SDK secara otomatis menukar lebar dan tinggi.
// Namun, lebar dan tinggi dalam callback videoSample telah dikoreksi oleh SDK ARTC. Oleh karena itu, Anda harus secara manual menukar lebar dan tinggi di sini.
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 menunjukkan QueenResult.QUEEN_OK.
// Ubah ID texture.
videoSample.textureid = newTextId;
videoSample.format = AliRtcEngine.AliRtcVideoFormat.AliRtcVideoFormatTexture2D;
result = true;
}
return result;
}Proses callback 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. Keluar dan hancurkan resource
Saat Anda meninggalkan rapat atau keluar dari antarmuka panggilan video, segera hancurkan engine pemrosesan kustom.
// Tambahkan lock sinkronisasi untuk mencegah mQueenBeautyEffector dihancurkan di lingkungan multi-threaded.
public synchronized void release() {
if (mQueenBeautyEffector != null) {
mQueenBeautyEffector.onReleaseEngine();
mQueenBeautyEffector = null;
}
}