All Products
Search
Document Center

ApsaraVideo Live:Raw video data processing

Last Updated:Oct 21, 2025

During a real-time communication (RTC) session, you can process raw video data from the SDK or access it at different stages of the processing pipeline. This topic explains how to use video observers to access and process this data.

Use cases

Common use cases for custom video data processing include:

  1. Custom retouching: Pull the local video stream and process it using a third-party retouching SDK before sending it to other users in the channel.

  2. Custom video editing: Edit the video stream at different stages of the processing pipeline, such as adding special effects, watermarks, cropping, or clipping. The modified stream can then be broadcast or saved.

  3. Stream preview or monitoring: Pull the video stream at different stages of the processing pipeline for local or cloud-based monitoring and preview. You can monitor the content, quality, and status of the video stream in real time to ensure proper transmission and playback.

Before you begin

Ensure you have completed the following preparations:

How it works

Video capture:

image

Decoding and rendering:

image

The ARTC SDK provides observation points at different stages of the video processing pipeline.

The observation points correspond to the following callbacks:

  • Observation point 1: Get the original, unscaled video frame data through onCaptureVideoSample (iOS) or onLocalVideoSample (Android) right after capture.

  • Observation point 2: Get pre-encoding data through onPreEncodeVideoSample.

  • Observation point 3: Get decoded data through onRemoteVideoSample.

  • Observation point 4: Get the OpenGL context (glcontext) through onTextureCreate and bind it to your retouching module.

  • Observation point 5: Receive the updated texture data through onTextureUpdate to apply your effects.

  • Observation point 6: Receive a notification to release glcontext through onTextureDestroy.

Note
  • For observation points 1, 2, and 3, returning true indicates that you have modified the data. Returning false leaves the original data unchanged.

  • For observation point 5, the method must return a valid textureId. Returning a new textureId tells the SDK to use your modified texture for encoding and processing. If you do not perform any custom processing, return the original textureId.

Implementation

Android

Video frame callbacks

1. Register callbacks

To receive raw video data, implement the AliRtcEngine.AliRtcVideoObserver interface and register your instance by calling the registerVideoSampleObserver() method.

public abstract void registerVideoSampleObserver(AliRtcVideoObserver observer);

2. Specify observation points

The SDK determines which callbacks to trigger based on the bitmask returned by this method.

 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. Specify the output format

/**
 * @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. Specify the memory alignment

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. Specify whether to apply a mirror effect

public boolean onGetObserverDataMirrorApplied(){
  return false;
}

6. Implement the 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. Unregister the callback

When you no longer need to observe video frames, unregister the observer to avoid unnecessary processing.

unregisterVideoSampleObserver

Texture processing

All texture-related callbacks are invoked on the same dedicated OpenGL thread. To use them, implement the AliRtcTextureObserver interface.

Prerequisites

Enable texture capture and texture encoding by setting the following options when you initialize the engine.

String extras = "{\"user_specified_camera_texture_capture\":\"TRUE\",\"user_specified_texture_encode\":\"TRUE\" }";
_engine = AliRtcEngine.getInstance(getApplicationContext(), extras);

1. Register the texture callback

public abstract void registerLocalVideoTextureObserver(AliRtcTextureObserver observer);

2. Implement the callback

Callback after OpenGL context creation
@Override
public void onTextureCreate(long context) {
      context_ = context ;
      Log.d(TAG, "texture context: "+context_+" create!") ;
}
OpenGL texture update callback
@Override
public int onTextureUpdate(int textureId, int width, int height, AliRtcEngine.AliRtcVideoSample videoSample) {
    /*
    *  Process the textureid.
    */
     ++log_ref ;
     return textureId;
}       
OpenGL context destruction callback
@Override
public void onTextureDestroy() {
     Log.d(TAG, "texture context: "+context_+" destory!") ;
}

3. Unregister the texture callback

public abstract void  unRegisterLocalVideoTextureObserver();

iOS

Video frame callbacks

1. Register callbacks

Implement AliRtcEngineDelegate and call registerVideoSampleObserver to register video frame callbacks.

registerVideoSampleObserver

2. Specify observation points

The SDK determines which callbacks to trigger based on the bitmask returned by this method.

/**
 * @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. Specify the output format

To receive video data as a CVPixelBuffer, set the user_specified_native_buffer_observer option to TRUE in the extra field when you create the engine.

/**
 * @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. Specify the memory alignment

/**
 * @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. Specify whether to apply a mirror effect

/**
 * @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. Implement the 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. Unregister the callback

When you no longer need to observe video frames, unregister the observer to avoid unnecessary processing.

unregisterVideoSampleObserver

Texture processing

All texture-related callbacks are invoked on the same dedicated OpenGL thread.

1. Register the texture callback

registerLocalVideoTexture

2. Implement the callback

Callback after OpenGL context creation
/**
 * @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];
}
OpenGL texture update callback
/**
 * @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;
}
OpenGL context destruction callback
- (void)onTextureDestory
{
    if (self.settingModel.chnnelType == ChannelTypePrimary) {
        [[beautifyMoudle_ shared] destroy];
    }
    
}

3. Unregister the texture callback

unregisterLocalVideoTexture