All Products
Search
Document Center

ApsaraVideo Live:Java

Last Updated:Mar 20, 2026

This topic describes how to integrate the Alibaba Real-Time Communication (ARTC) SDK into a Linux Java project to build a simple application for real-time audio and video interaction. This guide is intended for server-side scenarios, such as video conferencing, interactive streaming, and cloud recording.

Feature description

Before you begin, understand the following key concepts:

  • ARTC SDK: An SDK provided by Alibaba Cloud that helps developers quickly implement real-time audio and video interaction.

  • Global Realtime Transport Network (GRTN): A globally distributed network engineered for real-time media, ensuring ultra-low latency, high-quality, and secure communication.

  • Channel: A virtual room that users join to communicate with each other. All users in the same channel can interact in real time.

  • Host: A user who can publish audio and video streams in a channel and subscribe to streams published by other hosts.

  • Viewer: A user who can subscribe to audio and video streams in a channel but cannot publish their own.

Basic process for implementing real-time audio and video interaction:

image
  1. Call setChannelProfile to set the scenario, and call joinChannel to join a channel:

    • Video call scenario: All users are hosts and can both publish and subscribe to streams.

    • Interactive streaming scenario: Roles must be set using setClientRole before joining a channel. For users who will publish streams, set the role to host. If a user only needs to subscribe to streams, set the role to viewer.

  2. After joining the channel, users have different publishing and subscribing behaviors based on their roles:

    • All users can receive audio and video streams within that channel.

    • A host can publish audio and video streams in the channel.

    • If a viewer wants to publish streams, call the setClientRole method to switch the role to host.

Sample project

The SDK package provides a sample program:

Example

File

Description

Feature demo

Demo/MainTest.java

Includes a complete demo of features such as stream ingest and pulling, and token generation.

Run the sample:

cd Demo
sh run.sh

To exit the sample, enter exit in the command line.

Prerequisites

  • Operating system: Linux (kernel 2.6+).

  • Java runtime: JDK 8+.

  • Network: A stable connection to the Internet.

  • Application preparation: Obtain an AppID and AppKey for your ApsaraVideo Real-time Communication application. For more information, see Create an application.

Implementation steps

Step 1: Import the SDK

The SDK package has the following directory structure:

AliRTCSDK_Linux/
└── Java/
    ├── libs/
    │   ├── AliRtcCoreService                      # Background service process (must specify an absolute path)
    │   ├── alirtc_linux_java_multiprocess.jar      # JAR bridge between the Java layer and the C++ engine
    │   ├── gson-2.11.0.jar                        # JSON serialization dependency
    │   └── libAliRtcLinuxEngine.so                # Complete dynamic library for the SDK
    └── Demo/
        ├── MainTest.java                          # Feature demo sample
        └── run.sh                                 # Script to compile and run

Compile and run the sample:

cd Demo
javac -g -cp ../libs/alirtc_linux_java_multiprocess.jar:../libs/gson-2.11.0.jar \
      -encoding utf-8 MainTest.java
java -cp .:../libs/alirtc_linux_java_multiprocess.jar:../libs/gson-2.11.0.jar MainTest

Before running, make sure the dynamic library can be found:

export LD_LIBRARY_PATH=/path/to/Java/libs:$LD_LIBRARY_PATH

Step 2: Implement the event callback class

Implement the AliRTCLinuxEngineListener interface to receive notifications from the SDK.

import com.alivc.rtc.multiprocess.AliRTCLinuxEngine;
import com.alivc.rtc.multiprocess.AliRTCLinuxEngineListener;

class VideoCallEventHandler implements AliRTCLinuxEngineListener {

    @Override
    public void onJoinChannelResult(int result, String channel, String userId) {
        if (result == 0) {
            System.out.printf("[onJoinChannelResult] User %s joined channel %s successfully%n",
                    userId, channel);
        } else {
            System.out.printf("[onJoinChannelResult] Failed to join, error: %d%n", result);
        }
    }

    @Override
    public void onRemoteUserOnLineNotify(String uid) {
        System.out.printf("[onRemoteUserOnLineNotify] uid: %s%n", uid);
    }

    @Override
    public void onRemoteUserOffLineNotify(String uid) {
        System.out.printf("[onRemoteUserOffLineNotify] uid: %s%n", uid);
    }

    @Override
    public void onRemoteTrackAvailableNotify(String uid,
            AliRTCLinuxEngine.AudioTrack audioTrack,
            AliRTCLinuxEngine.VideoTrack videoTrack) {
        System.out.printf("[onRemoteTrackAvailableNotify] uid: %s, audio: %s, video: %s%n",
                uid, audioTrack, videoTrack);
    }

    // onSubscribeMixedAudioFrame: Receives remote PCM audio frames after mixing.
    // Triggered when subscribeAudioFormat is set to AudioFormatMixedPcm in the subscription configuration.
    @Override
    public void onSubscribeMixedAudioFrame(AliRTCLinuxEngine.AudioFrame frame) {
        if (frame != null && frame.type == AliRTCLinuxEngine.AudioFrameType.AudioFrameRawPcm) {
            AliRTCLinuxEngine.AudioPcmFrame pcmFrame = frame.pcm;

            // pcmFrame.data        PCM data (byte[], int16_t format)

            // pcmFrame.channels    The number of sound channels
            // pcmFrame.sampleRates Sample rate
            // Write to a file, send to an audio device, or decode for playback here.
        }
    }

    // onSubscribeAudioFrame: Receives unmixed PCM frames from each remote user. The uid distinguishes the audio streams of different remote users.
    // Triggered when subscribeAudioFormat is set to AudioFormatPcmBeforeMixing in the subscription configuration.
    @Override
    public void onSubscribeAudioFrame(String uid, AliRTCLinuxEngine.AudioFrame frame) {
        if (frame != null && frame.type == AliRTCLinuxEngine.AudioFrameType.AudioFrameRawPcm) {
            AliRTCLinuxEngine.AudioPcmFrame pcmFrame = frame.pcm;
            // The uid identifies which remote user the frame is from.
            // Process audio data for each user separately here.
        }
    }

    // onRemoteVideoSample: Receives remote video frames.
    // The uid distinguishes the video streams of different remote users.
    @Override
    public void onRemoteVideoSample(String uid, AliRTCLinuxEngine.VideoFrame frame) {
        if (frame != null && frame.type == AliRTCLinuxEngine.VideoFrameType.VideoFrameH264) {
            AliRTCLinuxEngine.VideoH264Frame h264Frame = frame.h264;
            // The uid identifies which remote user the frame is from.
            // Write to a file, send to a renderer, or send to a video decoder here.
        }
    }

    @Override
    public void onError(int errorCode) {
        System.out.printf("[onError] error_code: 0x%X%n", errorCode);
    }
}

Step 3: Authentication token

The Java SDK has a built-in generateToken method. You can use it to directly generate a single-parameter token for joining a channel on the client.

Token generation process:

  1. Concatenate the string: appId + appKey + channelId + userId + nonce + timestamp

  2. Use SHA-256 hashing to generate a hexadecimal string.

  3. Assemble the JSON: {"appid":..., "channelid":..., "userid":..., "nonce":..., "timestamp":..., "token":<sha256>}

  4. Encode with Base64.

import java.time.Instant;

AliRTCLinuxEngine.AuthInfo authInfo = new AliRTCLinuxEngine.AuthInfo();
authInfo.appid    = "your_app_id";
authInfo.channel  = "your_channel_id";
authInfo.userid   = "your_user_id";
authInfo.username = "your_user_id";
authInfo.nonce    = null;
authInfo.timestamp = Instant.now().getEpochSecond() + 24 * 60 * 60; // Expires after 24 hours

String appKey = "your_app_key"; // In a production environment, do not expose the AppKey in the client code.

// Call generateToken to generate a single-parameter token for joining a channel (an engine instance must be created first).
authInfo.token = engineIns.generateToken(authInfo, appKey);
Note

Token security: The sample code generates a token locally on the client. This method is for development and testing only. In a production environment, your business server must generate and issue the token. This prevents exposing the AppKey in your client code.

Step 4: Create and initialize the audio and video engine

You can call the AliRTCLinuxEngine.createInstance method to create an engine instance and pass in the event callback object.

VideoCallEventHandler listener = new VideoCallEventHandler();

String coreServicePath = "/path/to/Java/libs/AliRtcCoreService"; // Specify the absolute path of AliRtcCoreService
boolean h5mode = false; // Set to true to interoperate with web clients

String extra = "{\"user_specified_disable_audio_ranking\":\"true\"}";

AliRTCLinuxEngine engineIns = AliRTCLinuxEngine.createInstance(
    listener,
    42000, 45000,   // IPC port range for communication between the Java layer and the AliRtcCoreService process
    "/tmp",         // Log file directory
    coreServicePath,
    h5mode,
    extra
);

if (engineIns == null) {
    System.err.println("Failed to create RTC engine");
    return;
}
Note

The Java SDK uses TCP for inter-process communication. Each time you create an engine instance, the SDK starts a corresponding AliRtcCoreService background process. This process represents a virtual user.

Step 5: Set audio and video properties

You can call the setClientRole method to set the user role. You can also call the setVideoEncoderConfiguration method to configure video encoding parameters.

// Call setClientRole to set the user role to interactive mode (streamer) to publish and subscribe at the same time.
engineIns.setClientRole(AliRTCLinuxEngine.AliEngineClientRole.AliEngineClientRoleInteractive);

// Call setVideoEncoderConfiguration to set video encoding parameters.
AliRTCLinuxEngine.AliEngineVideoEncoderConfiguration videoConfig =
        new AliRTCLinuxEngine.AliEngineVideoEncoderConfiguration();
videoConfig.dimensions = new AliRTCLinuxEngine.AliEngineVideoDimensions(720, 1280);
videoConfig.frameRate  = AliRTCLinuxEngine.AliEngineFrameRate.AliEngineFrameRateFps15;
videoConfig.bitrate    = 1200;
engineIns.setVideoEncoderConfiguration(videoConfig);

Step 6: Set stream ingest and pulling properties

Configure the publishing and subscription behavior for audio and video. Enable the external audio and video source mode, which is specific to the Linux platform.

// Call publishLocalVideoStream / publishLocalAudioStream to enable local audio and video publishing.
engineIns.publishLocalVideoStream(true);
engineIns.publishLocalAudioStream(true);

// Linux has no built-in camera or microphone. Call setExternalVideoSource to enable an external video source.
// Input YUV frame data using pushExternalVideoFrame.
engineIns.setExternalVideoSource(true,
        AliRTCLinuxEngine.VideoSource.VideoSourceCamera,
        AliRTCLinuxEngine.RenderMode.RenderModeFill);

// Call setExternalAudioSource to enable an external audio source. Input PCM frame data using pushExternalAudioFrameRawData.
engineIns.setExternalAudioSource(true, 16000 /* Sample rate */, 1 /* The number of sound channels */);

Configure the subscription mode for joining the channel. Set this in JoinChannelConfig in Step 7:

AliRTCLinuxEngine.JoinChannelConfig joinConfig = new AliRTCLinuxEngine.JoinChannelConfig();
joinConfig.channelProfile    = AliRTCLinuxEngine.ChannelProfile.ChannelProfileInteractiveLive;
joinConfig.publishMode       = AliRTCLinuxEngine.PublishMode.PublishAutomatically;     // Automatically publish streams
joinConfig.subscribeMode     = AliRTCLinuxEngine.SubscribeMode.SubscribeAutomatically; // Automatically subscribe to streams
joinConfig.publishAvsyncMode = AliRTCLinuxEngine.PublishAvsyncMode.PublishAvsyncWithPts;

// There are two options for the audio subscription format:
// AudioFormatMixedPcm: Receives mixed PCM data for the entire channel. This triggers onSubscribeMixedAudioFrame.
// AudioFormatPcmBeforeMixing: Receives individual PCM data from each remote user. This triggers onSubscribeAudioFrame (includes uid).
joinConfig.subscribeAudioFormat = AliRTCLinuxEngine.AudioFormat.AudioFormatMixedPcm;

// Receives H.264 video frames. This triggers onRemoteVideoSample.
joinConfig.subscribeVideoFormat = AliRTCLinuxEngine.VideoFormat.VideoFormatH264;

Step 7: Join the channel

You can call the joinChannel method and pass the token generated in Step 3 and the channel configuration to join the channel.

// Call joinChannel to join the channel.
engineIns.joinChannel(authInfo, joinConfig);
Note

Do not call the joinChannel method repeatedly. The generateToken interface is for development and testing only. In a production environment, obtain the token from your business server to prevent AppKey leaks.

Step 8: Push external video frames

The Linux platform does not have a built-in camera driver interface. You must use an external input to send YUV video data to the SDK. The following example reads frame data in a loop from an I420 format YUV file and pushes the data. In a real-world business scenario, you can replace this with output from a camera driver or a video decoder.

Thread videoThread = new Thread(() -> {
    int width     = 720;
    int height    = 1280;
    int fps       = 15;
    int frameSize = width * height * 3 / 2; // I420

    try (FileInputStream fis = new FileInputStream("/tmp/test_720p.yuv")) {

        byte[ ] buf = new byte[frameSize];

        AliRTCLinuxEngine.VideoDataSample sample = new AliRTCLinuxEngine.VideoDataSample();

        while (running && !Thread.currentThread().isInterrupted()) {
            int readSize = fis.read(buf, 0, frameSize);
            if (readSize != frameSize) {
                fis.getChannel().position(0); // After the file is read, loop back to the beginning.
                continue;
            }

            sample.data       = buf;
            sample.format     = AliRTCLinuxEngine.VideoDataFormat.VideoDataFormatI420;
            sample.bufferType = AliRTCLinuxEngine.VideoBufferType.VideoBufferTypeRawData;
            sample.width      = width;
            sample.height     = height;
            sample.strideY    = width;
            sample.strideU    = width / 2;
            sample.strideV    = width / 2;
            sample.dataLen    = frameSize;
            sample.rotation   = 0;
            sample.timeStamp  = System.currentTimeMillis();

            int ret = engineIns.pushExternalVideoFrame(sample,
                    AliRTCLinuxEngine.VideoSource.VideoSourceCamera);
            if (ret < 0) break;

            Thread.sleep(1000 / fps);
        }
    } catch (IOException | InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});
videoThread.start();

Step 9: Push external audio frames

The Linux platform does not have a built-in microphone recording interface. You must use an external input to send PCM audio data to the SDK. The following example reads frame data in a loop from a PCM file (16-bit integer, 16 kHz, mono) and pushes the data. In a real-world business scenario, you can replace this with output from a microphone driver or an audio decoder.

Thread audioThread = new Thread(() -> {
    int sampleRate = 16000;
    int channels   = 1;
    int frameMs    = 20;  // 20 ms per frame
    int frameSize  = (sampleRate / 1000) * frameMs * 2 * channels; // int16_t = 2 bytes

    try (FileInputStream fis = new FileInputStream("/tmp/test_16k_mono.pcm")) {

        byte[ ] buf = new byte[frameSize];


        while (running && !Thread.currentThread().isInterrupted()) {
            int readSize = fis.read(buf, 0, frameSize);
            if (readSize != frameSize) {
                fis.getChannel().position(0); // After the file is read, loop back to the beginning.
                continue;
            }

            int ret = engineIns.pushExternalAudioFrameRawData(buf, frameSize, 0);
            if (ret != 0) {
                // The SDK buffer is full. Move the file pointer back and retry later.
                fis.getChannel().position(fis.getChannel().position() - readSize);
                Thread.sleep(20);
                continue;
            }

            Thread.sleep(frameMs);
        }
    } catch (IOException | InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});
audioThread.start();

Step 10: Handle remote audio and video playback

The Linux platform does not have built-in audio and video playback devices. Remote audio and video data is passed to the application layer through frame callbacks for custom processing. For example, you can write the data to a file, send it to a decoder, or connect it to a playback device.

Audio playback

Based on the subscribeAudioFormat configuration in Step 6, one of the following callbacks is triggered when a remote audio frame is received:

// AudioFormatMixedPcm mode: Receives mixed PCM data for the entire channel from all remote users.
@Override
public void onSubscribeMixedAudioFrame(AliRTCLinuxEngine.AudioFrame frame) {
    if (frame != null && frame.type == AliRTCLinuxEngine.AudioFrameType.AudioFrameRawPcm) {
        AliRTCLinuxEngine.AudioPcmFrame pcmFrame = frame.pcm;

        // pcmFrame.data        PCM data (byte[], int16_t format)

        // pcmFrame.channels    The number of sound channels
        // pcmFrame.sampleRates Sample rate
        // Write to a file, send to an audio device, or decode for playback here.
    }
}

// AudioFormatPcmBeforeMixing mode: Receives unmixed, individual PCM data from each user.
@Override
public void onSubscribeAudioFrame(String uid, AliRTCLinuxEngine.AudioFrame frame) {
    if (frame != null && frame.type == AliRTCLinuxEngine.AudioFrameType.AudioFrameRawPcm) {
        // The uid identifies which remote user the frame is from.
        // Process audio data for each user separately here.
    }
}

Video playback

When a remote video frame is received, the onRemoteVideoSample callback is triggered. The frame format is determined by the subscribeVideoFormat setting in Step 6:

@Override
public void onRemoteVideoSample(String uid, AliRTCLinuxEngine.VideoFrame frame) {
    if (frame != null && frame.type == AliRTCLinuxEngine.VideoFrameType.VideoFrameH264) {
        AliRTCLinuxEngine.VideoH264Frame h264Frame = frame.h264;
        // The uid identifies which remote user the frame is from.
        // Write to a file, send to a renderer, or send to a video decoder here.
    }
}

Step 11: Leave the channel and destroy the engine

To release resources correctly, you must stop stream ingest, leave the channel, and then destroy the engine, in that order.

// Stop the external stream ingest threads
running = false;
videoThread.interrupt();
audioThread.interrupt();
videoThread.join(1000);
audioThread.join(1000);

// Call publishLocalVideoStream(false) / publishLocalAudioStream(false) to stop publishing.
engineIns.publishLocalVideoStream(false);
engineIns.publishLocalAudioStream(false);

// Call leaveChannel to leave the channel.
engineIns.leaveChannel();

// Wait for the onLeaveChannelResult callback before destroying the engine.
// Call release to destroy the engine (must be called after leaveChannel).
engineIns.release();
engineIns = null;

FAQ

Q: What should I do if creating the engine returns null?

Confirm that the path to AliRtcCoreService is correct and that it has execute permissions:

chmod +x /path/to/Java/libs/AliRtcCoreService

Q: When should h5mode be set to true?

Enable this mode only when interoperating with web clients (H5 pages). For pure Linux-to-Linux interoperability, set it to false.

Q: How do I handle an expired token?

Listen for the onAuthInfoWillExpire callback (token is about to expire). Regenerate the token and call the engine's refresh interface to update the credentials. You do not need to rejoin the channel.

Listen for the onAuthInfoExpired callback (token has expired). You must leave the channel and rejoin with a new token.

Q: What should I do if a "shared library not found" error occurs at runtime?

error while loading shared libraries: libAliRtcLinuxEngine.so: cannot open shared object file

Solution: Run export LD_LIBRARY_PATH=/path/to/Java/libs:$LD_LIBRARY_PATH.