Lors d'une session de communication en temps réel (RTC), utilisez des observateurs vidéo pour accéder aux données vidéo brutes et les traiter à différentes étapes du pipeline de traitement du SDK.
Cas d'utilisation
Voici les cas d'utilisation courants du traitement personnalisé des données vidéo :
Retaillage personnalisé : récupérez le flux vidéo local et traitez-le avec un SDK de retouche tiers avant de l'envoyer aux autres utilisateurs du canal.
Montage vidéo personnalisé : modifiez le flux vidéo à différentes étapes du pipeline de traitement, par exemple en ajoutant des effets spéciaux ou des filigranes, ou en recadrant et découpant l'image. Vous pouvez ensuite diffuser ou enregistrer le flux modifié.
Aperçu ou surveillance du flux : récupérez le flux vidéo à différentes étapes du pipeline de traitement pour une surveillance et un aperçu locaux ou cloud, afin de vérifier la qualité du contenu, la transmission et la lecture en temps réel.
Prérequis
Assurez-vous d'avoir effectué les tâches suivantes :
Créez une application ARTC et obtenez l'AppID et l'AppKey depuis la console ApsaraVideo Live.
Intégrez le SDK ARTC dans votre projet et mettez en œuvre les fonctionnalités de base d'appels audio et vidéo.
Fonctionnement
Capture vidéo :
Décodage et rendu :
Le SDK ARTC fournit des points d'observation à différentes étapes du pipeline de traitement vidéo.
Les points d'observation correspondent aux rappels suivants :
Point d'observation 1 : obtenez les données de trame vidéo originales non mises à l'échelle via
onCaptureVideoSample(iOS) ouonLocalVideoSample(Android) immédiatement après la capture.Point d'observation 2 : obtenez les données pré-encodage via
onPreEncodeVideoSample.Point d'observation 3 : obtenez les données décodées via
onRemoteVideoSample.Point d'observation 4 : obtenez le contexte OpenGL (
glcontext) viaonTextureCreateet liez-le à votre module de retouche.Point d'observation 5 : recevez les données de texture mises à jour via
onTextureUpdatepour appliquer vos effets.Point d'observation 6 : recevez une notification pour libérer le
glcontextviaonTextureDestroy.
Pour les points d'observation 1, 2 et 3, retournez
truesi vous avez modifié les données. Retournezfalsepour laisser les données originales inchangées.Pour le point d'observation 5, la méthode doit retourner un
textureIdvalide. Retournez un nouveautextureIdpour indiquer au SDK d'utiliser votre texture modifiée pour l'encodage et le traitement. Si vous n'effectuez aucun traitement personnalisé, retournez letextureIdoriginal.
Implémentation
Android
Rappels de trames vidéo
1. Enregistrez les rappels
Pour recevoir les données vidéo brutes, implémentez l'interface AliRtcEngine.AliRtcVideoObserver et enregistrez votre instance en appelant la méthode registerVideoSampleObserver().
public abstract void registerVideoSampleObserver(AliRtcVideoObserver observer);
2. Spécifiez les points d'observation
Le SDK détermine quels rappels déclencher en fonction du masque de bits renvoyé par cette méthode.
public enum AliRtcVideoObserPosition{
/*! Locally captured video data, corresponds to the onLocalVideoSample callback */
AliRtcPositionPostCapture(1),
/*! Decoded remote video data, corresponds to the onRemoteVideoSample callback */
AliRtcPositionPreRender(2),
/*! Pre-encoding video data, corresponds to the onPreEncodeVideoSample callback */
AliRtcPositionPreEncoder(4);
}
public int onGetObservedFramePosition(){
return AliRtcVideoObserPosition.AliRtcPositionPostCapture.getValue() | AliRtcVideoObserPosition.AliRtcPositionPreRender.getValue();
}
3. Spécifiez le format de sortie
/**
* @brief Video data format
*/
public enum AliRtcVideoFormat{
AliRtcVideoFormatUNKNOW(-1),
AliRtcVideoFormatBGRA(0),
AliRtcVideoFormatI420(1),
AliRtcVideoFormatNV21(2),
AliRtcVideoFormatNV12 (3),
AliRtcVideoFormatRGBA(4),
AliRtcVideoFormatI422 (5),
AliRtcVideoFormatARGB(6),
AliRtcVideoFormatABGR (7),
AliRtcVideoFormatRGB24(8),
AliRtcVideoFormatBGR24(9),
AliRtcVideoFormatRGB565(10),
AliRtcVideoFormatTextureOES(11),
AliRtcVideoFormatTexture2D(12),
AliRtcVideoFormatH264(13),
AliRtcVideoFormatH265(14),
AliRtcVideoFormatFile(15);
};
/*
* The SDK calls this method after you call AliRtcEngine::registerVideoSampleObserver.
*/
/**
* @brief Video data output format
* @return The desired video output format. See {@link AliRtcVideoFormat}.
*/
public AliRtcVideoFormat onGetVideoFormatPreference(){
return AliRtcVideoFormat.AliRtcVideoFormatI420;
}
4. Spécifiez l'alignement mémoire
public enum AliRtcVideoObserAlignment{
/*! Keep the original video width (default) */
AliRtcAlignmentDefault(0),
/*! Align the stride to a multiple of 2 bytes */
AliRtcAlignmentEven(1),
/*! Align the stride to a multiple of 4 bytes */
AliRtcAlignment4(2),
/*! Align the stride to a multiple of 8 bytes */
AliRtcAlignment8(3),
/*! Align the stride to a multiple of 16 bytes */
AliRtcAlignment16(4);
};
/*
* The SDK calls this method after you call AliRtcEngine::registerVideoSampleObserver.
*/
public int onGetVideoAlignment(){
return AliRtcVideoObserAlignment.AliRtcAlignmentDefault.getValue();
}
5. Indiquez si un effet miroir doit être appliqué
public boolean onGetObserverDataMirrorApplied(){
return false;
}
6. Implémentez les rappels
/*
* Return true to write your modifications back to the SDK's processing pipeline.
* This is required if you modify AliRtcVideoSample.data.
*/
@Override
public boolean onLocalVideoSample(AliRtcEngine.AliRtcVideoSourceType sourceType, AliRtcEngine.AliRtcVideoSample videoSample) {
boolean ret = false;
/*
* Process locally captured data.
*/
return ret;
}
@Override
public boolean onRemoteVideoSample(String userId, AliRtcEngine.AliRtcVideoSourceType sourceType, AliRtcEngine.AliRtcVideoSample videoSample) {
/*
* Process the decoded data from a remote user.
*/
return false;
}
@Override
public boolean onPreEncodeVideoSample(AliRtcEngine.AliRtcVideoSourceType aliVideoSourceType, AliRtcEngine.AliRtcVideoSample videoSample) {
boolean ret = false;
/*
* Process pre-encoding data.
*/
return false ;
}
7. Désenregistrez le rappel
Lorsque vous n'avez plus besoin d'observer les trames vidéo, désenregistrez l'observateur pour éviter tout traitement inutile.
unregisterVideoSampleObserver
Traitement des textures
Tous les rappels liés aux textures sont invoqués sur le même thread OpenGL dédié. Pour les utiliser, implémentez l'interface AliRtcTextureObserver.
Prérequis
Activez la capture de texture et l'encodage de texture en définissant les options suivantes lors de l'initialisation du moteur.
String extras = "{\"user_specified_camera_texture_capture\":\"TRUE\",\"user_specified_texture_encode\":\"TRUE\" }";
_engine = AliRtcEngine.getInstance(getApplicationContext(), extras);
1. Enregistrez le rappel de texture
public abstract void registerLocalVideoTextureObserver(AliRtcTextureObserver observer);
2. Implémentez le rappel
Rappel après la création du contexte OpenGL
@Override
public void onTextureCreate(long context) {
context_ = context ;
Log.d(TAG, "texture context: "+context_+" create!") ;
}
Rappel de mise à jour de la texture OpenGL
@Override
public int onTextureUpdate(int textureId, int width, int height, AliRtcEngine.AliRtcVideoSample videoSample) {
/*
* Process the textureid.
*/
++log_ref ;
return textureId;
}
Rappel de destruction du contexte OpenGL
@Override
public void onTextureDestroy() {
Log.d(TAG, "texture context: "+context_+" destory!") ;
}
3. Désenregistrez le rappel de texture
public abstract void unRegisterLocalVideoTextureObserver();
iOS
Rappels de trames vidéo
1. Enregistrez les rappels
Implémentez AliRtcEngineDelegate et appelez registerVideoSampleObserver pour enregistrer les rappels de trames vidéo.
registerVideoSampleObserver
2. Spécifiez les points d'observation
Le SDK détermine quels rappels déclencher en fonction du masque de bits renvoyé par cette méthode.
/**
* @brief Video data output position
*/
typedef NS_ENUM(NSInteger, AliRtcVideoObserPosition) {
/** Captured video data, corresponds to onCaptureVideoSample callback */
AliRtcPositionPostCapture = 1 << 0,
/** Decoded remote video data, corresponds to onRemoteVideoSample callback */
AliRtcPositionPreRender = 1 << 1,
/** Pre-encoding video data, corresponds to onPreEncodeVideoSample callback */
AliRtcPositionPreEncoder = 1 << 2,
};
/*
* The SDK calls this method after you call AliRtcEngine::registerVideoSampleObserver.
*/
- (NSInteger)onGetVideoObservedFramePosition;
3. Spécifiez le format de sortie
Pour recevoir les données vidéo sous forme de CVPixelBuffer, définissez l'option user_specified_native_buffer_observer sur TRUE dans le champ extra lors de la création du moteur.
/**
* @brief Video data format
*/
typedef NS_ENUM(NSInteger, AliRtcVideoFormat) {
AliRtcVideoFormat_UNKNOW = -1,
AliRtcVideoFormat_BGRA = 0,
AliRtcVideoFormat_I420,
AliRtcVideoFormat_NV21,
AliRtcVideoFormat_NV12,
AliRtcVideoFormat_RGBA,
AliRtcVideoFormat_I422,
AliRtcVideoFormat_ARGB,
AliRtcVideoFormat_ABGR,
AliRtcVideoFormat_RGB24,
AliRtcVideoFormat_BGR24,
AliRtcVideoFormat_RGB565,
AliRtcVideoFormat_TextureOES,
AliRtcVideoFormat_Texture2D,
AliRtcVideoFormat_H264,
AliRtcVideoFormat_H265,
AliRtcVideoFormat_File,
AliRtcVideoFormat_cvPixelBuffer,
};
/*
* The SDK calls this method after you call AliRtcEngine::registerVideoSampleObserver.
*/
- (AliRtcVideoFormat)onGetVideoFormatPreference {
return AliRtcVideoFormat_I420;
}
4. Spécifiez l'alignement mémoire
/**
* @brief Video output width alignment
*/
typedef enum {
/** Keep the original video width (default) */
AliRtcAlignmentDefault = 0,
/** Align the stride to a multiple of 2 bytes */
AliRtcAlignmentEven = 1,
/** Align the stride to a multiple of 4 bytes */
AliRtcAlignment4 = 2,
/** Align the stride to a multiple of 8 bytes */
AliRtcAlignment8 = 3,
/** Align the stride to a multiple of 16 bytes */
AliRtcAlignment16 = 4,
} AliRtcVideoObserAlignment;
/*
* The SDK calls this method after you call AliRtcEngine::registerVideoSampleObserver.
*/
- (AliRtcVideoObserAlignment)onGetVideoAlignment {
return AliRtcAlignmentDefault;
}
5. Indiquez si un effet miroir doit être appliqué
/**
* @brief Specifies whether the output video data should be mirrored.
* @return
* - YES: Mirrored
* - NO: Not mirrored (default)
*/
/*
* The SDK calls this method after you call AliRtcEngine::registerVideoSampleObserver.
*/
- (BOOL)onGetObserverDataMirrorApplied {
return FLASE ;
}
6. Implémentez les rappels
/**
* @brief Callback for subscribed local video data.
* @param videoSource The type of video stream.
* @param videoSample The raw video data.
* @return
* - YES: Write the modified data back to the SDK. On iOS and macOS, writing back data by returning YES is only supported for the I420 and CVPixelBuffer formats.
* - NO: Do not write data back to the SDK.
*/
- (BOOL)onCaptureVideoSample:(AliRtcVideoSource)videoSource videoSample:(AliRtcVideoDataSample *_Nonnull)videoSample {
/*
* do....
*/
return FALSE ;
}
/**
* @brief Callback for subscribed local pre-encoding video data.
* @param videoSource The type of video stream.
* @param videoSample The raw video data.
* @return
* - YES: Write the modified data back to the SDK. On iOS and macOS, writing back data by returning YES is only supported for the I420 and CVPixelBuffer formats.
* - NO: Do not write data back to the SDK.
*/
- (BOOL)onPreEncodeVideoSample:(AliRtcVideoSource)videoSource videoSample:(AliRtcVideoDataSample *_Nonnull)videoSample {
/*
* do....
*/
return FALSE ;
}
/**
* @brief Callback for subscribed remote video data.
* @param uid The user ID.
* @param videoSource The type of video stream.
* @param videoSample The raw video data.
* @return
* - YES: Write the modified data back to the SDK. On iOS and macOS, writing back data by returning YES is only supported for the I420 and CVPixelBuffer formats.
* - NO: Do not write data back to the SDK.
*/
- (BOOL)onRemoteVideoSample:(NSString *_Nonnull)uid videoSource:(AliRtcVideoSource)videoSource videoSample:(AliRtcVideoDataSample *_Nonnull)videoSample {
/*
* do....
*/
return TRUE ;
}
7. Désenregistrez le rappel
Lorsque vous n'avez plus besoin d'observer les trames vidéo, désenregistrez l'observateur pour éviter tout traitement inutile.
unregisterVideoSampleObserver
Traitement des textures
Tous les rappels liés aux textures sont invoqués sur le même thread OpenGL dédié.
1. Enregistrez le rappel de texture
registerLocalVideoTexture
2. Implémentez le rappel
Rappel après la création du contexte OpenGL
/**
* @brief Callback for OpenGL context creation.
* @param context The OpenGL context.
* @note This callback is triggered when the SDK's internal OpenGL context is created.
*/
- (void)onTextureCreate:(void *_Nullable)context {
[[beautifyMoudle_ shared] create];
}
Rappel de mise à jour de la texture OpenGL
/**
* @brief Callback for OpenGL texture updates.
* @param textureId The ID of the OpenGL texture.
* @param width The width of the texture.
* @param height The height of the texture.
* @param videoSample The video frame data, see {@link AliRtcVideoDataSample}.
* @return The ID of the processed OpenGL texture.
* @note
* - The callback is invoked after the SDK uploads the current video frame to a GPU texture. If you registered the OpenGL texture observer, you can process the texture and return the texture ID to be used downstream after your processing.
* - You must return a valid texture ID. If you do not modify the texture, return the incoming textureId unchanged.
* The callback's textureId is in AliRtcVideoFormat_Texture2D format.
*/
- (int)onTextureUpdate:(int)textureId width:(int)width height:(int)height videoSample:(AliRtcVideoDataSample *_Nonnull)videoSample {
if ( [[beautifyMoudle_ shared] enabled] == NO) {
return textureId;
}
int texId = [[beautifyMoudle_ shared] processTextureToTexture:textureId Width:width Height:height];
if(texId<0) {
texId = textureId;
}
return texId;
}
Rappel de destruction du contexte OpenGL
- (void)onTextureDestory
{
if (self.settingModel.chnnelType == ChannelTypePrimary) {
[[beautifyMoudle_ shared] destroy];
}
}
3. Désenregistrez le rappel de texture
unregisterLocalVideoTexture