Durante uma sessão de comunicação em tempo real (RTC), utilize observadores de vídeo para acessar e processar dados de vídeo brutos em diferentes estágios do pipeline de processamento do SDK.
Casos de uso
Os cenários mais comuns para processamento personalizado de dados de vídeo incluem:
Retoque personalizado: capture o fluxo de vídeo local e processe-o com um SDK de retoque de terceiros antes de enviá-lo aos demais usuários no canal.
Edição personalizada de vídeo: modifique o fluxo de vídeo em várias etapas do pipeline de processamento, aplicando efeitos especiais, marcas d'água, recortes ou cortes. O fluxo alterado pode ser transmitido ou salvo posteriormente.
Visualização ou monitoramento de fluxo: obtenha o fluxo de vídeo em distintos pontos do pipeline para monitoramento e pré-visualização local ou na nuvem, permitindo verificar a qualidade do conteúdo, a transmissão e a reprodução em tempo real.
Antes de começar
Certifique-se de ter concluído as seguintes tarefas:
Crie um aplicativo ARTC e obtenha o AppID e a AppKey no console do ApsaraVideo Live.
Integre o SDK ARTC ao seu projeto e implemente os recursos básicos de chamada de áudio e vídeo.
Como funciona
Captura de vídeo:
Decodificação e renderização:
O SDK ARTC oferece pontos de observação em diversas fases do pipeline de processamento de vídeo.
Esses pontos correspondem aos seguintes callbacks:
Ponto de observação 1: obtenha os dados originais do quadro de vídeo, sem redimensionamento, por meio de
onCaptureVideoSample(iOS) ouonLocalVideoSample(Android) logo após a captura.Ponto de observação 2: acesse os dados anteriores à codificação através de
onPreEncodeVideoSample.Ponto de observação 3: receba os dados decodificados via
onRemoteVideoSample.Ponto de observação 4: obtenha o contexto OpenGL (
glcontext) usandoonTextureCreatee vincule-o ao seu módulo de retoque.Ponto de observação 5: receba os dados de textura atualizados por meio de
onTextureUpdatepara aplicar seus efeitos.Ponto de observação 6: receba uma notificação para liberar o
glcontextatravés deonTextureDestroy.
Nos pontos de observação 1, 2 e 3, retornar
trueindica que você modificou os dados. Retornarfalsemantém os dados originais inalterados.No ponto de observação 5, o método deve retornar um
textureIdválido. Retornar um novotextureIdinstrui o SDK a usar sua textura modificada para codificação e processamento. Caso não realize nenhum processamento personalizado, retorne otextureIdoriginal.
Implementação
Android
Callbacks de quadros de vídeo
1. Registre os callbacks
Para receber dados de vídeo brutos, implemente a interface AliRtcEngine.AliRtcVideoObserver e registre sua instância chamando o método registerVideoSampleObserver().
public abstract void registerVideoSampleObserver(AliRtcVideoObserver observer);
2. Especifique os pontos de observação
O SDK define quais callbacks acionar com base na máscara de bits retornada por este método.
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. Especifique o formato de saída
/**
* @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. Especifique o alinhamento de memória
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. Especifique se deseja aplicar efeito espelhado
public boolean onGetObserverDataMirrorApplied(){
return false;
}
6. Implemente os callbacks
/*
* 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. Cancele o registro do callback
Quando não for mais necessário observar os quadros de vídeo, cancele o registro do observador para evitar processamento desnecessário.
unregisterVideoSampleObserver
Processamento de texturas
Todos os callbacks relacionados a texturas são executados na mesma thread dedicada do OpenGL. Para utilizá-los, implemente a interface AliRtcTextureObserver.
Pré-requisitos
Ative a captura e a codificação de texturas definindo as opções abaixo durante a inicialização do mecanismo.
String extras = "{\"user_specified_camera_texture_capture\":\"TRUE\",\"user_specified_texture_encode\":\"TRUE\" }";
_engine = AliRtcEngine.getInstance(getApplicationContext(), extras);
1. Registre o callback de textura
public abstract void registerLocalVideoTextureObserver(AliRtcTextureObserver observer);
2. Implemente o callback
Callback após criação do contexto OpenGL
@Override
public void onTextureCreate(long context) {
context_ = context ;
Log.d(TAG, "texture context: "+context_+" create!") ;
}
Callback de atualização de textura OpenGL
@Override
public int onTextureUpdate(int textureId, int width, int height, AliRtcEngine.AliRtcVideoSample videoSample) {
/*
* Process the textureid.
*/
++log_ref ;
return textureId;
}
Callback de destruição do contexto OpenGL
@Override
public void onTextureDestroy() {
Log.d(TAG, "texture context: "+context_+" destory!") ;
}
3. Cancele o registro do callback de textura
public abstract void unRegisterLocalVideoTextureObserver();
iOS
Callbacks de quadros de vídeo
1. Registre os callbacks
Implemente AliRtcEngineDelegate e chame registerVideoSampleObserver para registrar os callbacks de quadros de vídeo.
registerVideoSampleObserver
2. Especifique os pontos de observação
O SDK define quais callbacks acionar com base na máscara de bits retornada por este método.
/**
* @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. Especifique o formato de saída
Para receber dados de vídeo como CVPixelBuffer, defina a opção user_specified_native_buffer_observer como TRUE no campo extra ao criar o mecanismo.
/**
* @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. Especifique o alinhamento de memória
/**
* @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. Especifique se deseja aplicar efeito espelhado
/**
* @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. Implemente os callbacks
/**
* @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. Cancele o registro do callback
Quando não for mais necessário observar os quadros de vídeo, cancele o registro do observador para evitar processamento desnecessário.
unregisterVideoSampleObserver
Processamento de texturas
Todos os callbacks relacionados a texturas são executados na mesma thread dedicada do OpenGL.
1. Registre o callback de textura
registerLocalVideoTexture
2. Implemente o callback
Callback após criação do contexto 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];
}
Callback de atualização de textura 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;
}
Callback de destruição do contexto OpenGL
- (void)onTextureDestory
{
if (self.settingModel.chnnelType == ChannelTypePrimary) {
[[beautifyMoudle_ shared] destroy];
}
}
3. Cancele o registro do callback de textura
unregisterLocalVideoTexture