中文 | English
- Agora Recording Java SDK
- Table of Contents
- Introduction
- Development Environment Requirements
- SDK Download
- Integrating the SDK
- Quick Start
- Enable Service
- Run the Maven Project
- Recording via Command Line (Examples-Mvn)
- Recording via API
- API Reference
- Changelog
- Other References
The Agora Recording Java SDK (v4.4.151.1) provides powerful real-time audio and video recording capabilities that can be seamlessly integrated into Java applications on Linux servers. With this SDK, your server can join an Agora channel as a dummy client to pull, subscribe to, and record audio and video streams within the channel in real-time. The recorded files can be used for content archiving, moderation, analysis, or other business-related advanced features.
- Operating System: Ubuntu 14.04+ or CentOS 6.5+ (7.0 recommended)
- CPU Architecture: x86-64, arm64
- Public IP Address
- Domain Access: Allow access to
.agora.io
and.agoralab.co
The required bandwidth depends on the number of channels to be recorded simultaneously and the conditions within those channels. The following data serves as a reference:
- Recording a 640 × 480 resolution stream requires approximately 500 Kbps.
- Recording a channel with two participants requires approximately 1 Mbps.
- Recording 100 such channels simultaneously requires approximately 100 Mbps.
- Build Tool: Apache Maven or other build tools
- JDK: JDK 8+
<dependency>
<groupId>io.agora.rtc</groupId>
<artifactId>linux-recording-java-sdk</artifactId>
<version>4.4.151.1</version>
</dependency>
<dependency>
<groupId>io.agora.rtc</groupId>
<artifactId>linux-recording-java-sdk</artifactId>
<version>4.4.151-aarch64</version>
</dependency>
Agora-Linux-Recording-Java-SDK-v4.4.151.1-x86_64-891308-28c706d74a-20250919_142050
Agora-Linux-Recording-Java-SDK-v4.4.151-aarch64-891319-952e64402b-20250919_140753
There are two ways to integrate the SDK: via Maven integration or local SDK integration.
Maven integration is the simplest way, automatically managing Java dependencies.
Add the following dependency to your project's pom.xml
file:
<!-- x86_64 Platform -->
<dependency>
<groupId>io.agora.rtc</groupId>
<artifactId>linux-recording-java-sdk</artifactId>
<version>4.4.151.1</version>
</dependency>
<!-- arm64 Platform -->
<dependency>
<groupId>io.agora.rtc</groupId>
<artifactId>linux-recording-java-sdk</artifactId>
<version>4.4.151-aarch64</version>
</dependency>
The Maven dependency includes the required JAR file, but you still need to manually handle the .so
library files to run the application. Please refer to the Loading Native Libraries (.so files) section below.
The local SDK is a complete package containing all necessary files, suitable for scenarios requiring more flexible control.
The SDK package (zip format) downloaded from the official website contains the following:
- doc/ - JavaDoc documentation, detailed API descriptions
- examples/ - Example code and projects
- sdk/ - Core SDK files
agora-recording-sdk.jar
- Java libraryagora-recording-sdk-javadoc.jar
- JavaDoc documentation
You can integrate the JAR file in two ways:
Method 1: Install only the SDK JAR
mvn install:install-file \
-Dfile=sdk/agora-recording-sdk.jar \
-DgroupId=io.agora.rtc \
-DartifactId=linux-recording-java-sdk \
-Dversion=4.4.151.1 \
-Dpackaging=jar \
-DgeneratePom=true
Method 2: Install both SDK JAR and JavaDoc JAR
mvn install:install-file \
-Dfile=sdk/agora-recording-sdk.jar \
-DgroupId=io.agora.rtc \
-DartifactId=linux-recording-java-sdk \
-Dversion=4.4.151.1 \
-Dpackaging=jar \
-DgeneratePom=true \
-Djavadoc=sdk/agora-recording-sdk-javadoc.jar
After installation, add the dependency in pom.xml
:
<dependency>
<groupId>io.agora.rtc</groupId>
<artifactId>linux-recording-java-sdk</artifactId>
<version>4.4.151.1</version>
</dependency>
-
Copy the JAR files to your project's
libs
directory:mkdir -p libs cp sdk/agora-recording-sdk.jar libs/ cp sdk/agora-recording-sdk-javadoc.jar libs/ # Optional, for IDE support
-
Add the classpath reference in your Java project:
# Use the SDK JAR java -cp .:libs/agora-recording-sdk.jar YourMainClass # Configure JavaDoc in your IDE (common IDEs like IntelliJ IDEA or Eclipse support direct association of JavaDoc JARs)
The downloaded SDK package already includes the .so
files. You need to ensure that the Java program can find these files at runtime. Please refer to the Loading Native Libraries (.so files) section below.
The Agora Linux Recording Java SDK depends on underlying C++ native libraries (.so
files). Whether integrating via Maven or locally, you need to ensure the Java Virtual Machine (JVM) can find and load these libraries at runtime.
The .so
files are contained within the agora-recording-sdk.jar
or linux-recording-java-sdk-x.x.x.x.jar
file. You need to extract them first:
-
Create a directory in your project or deployment location to store the library files, for example,
libs
:mkdir -p libs cd libs
-
Use the
jar
command to extract the contents from the SDK's JAR file (assuming the JAR file is in thelibs
directory or Maven cache):# If using local integration, the JAR file is usually in the libs directory jar xvf agora-recording-sdk.jar # If using Maven integration, the JAR file is in the Maven cache, e.g.: # jar xvf ~/.m2/repository/io/agora/rtc/linux-recording-java-sdk/4.4.151.1/linux-recording-java-sdk-4.4.151.1.jar
-
After extraction, a
native/linux/x86_64
subdirectory (oraarch64
for ARM) will be generated in thelibs
directory, containing the required.so
files:libs/ ├── agora-recording-sdk.jar (or empty, if only used for extraction) ├── io/ # Java class files location, no need to worry about ├── META-INF/ # JAR file and application-related metadata, no need to worry about └── native/ # Native library files for the corresponding platform └── linux/ └── x86_64/ # x86_64 platform .so libraries ├── libagora_rtc_sdk.so ├── libagora-fdkaac.so ├── libaosl.so └── librecording.so └── aarch64/ # arm64 platform .so libraries (if available) ├── libagora_rtc_sdk.so ├── libagora-fdkaac.so ├── libaosl.so └── librecording.so
There are two main methods to let the JVM find the .so
files:
Method 1: Setting the LD_LIBRARY_PATH
Environment Variable (Recommended)
This is the most reliable way, especially when there are dependencies between .so
files.
# Determine the directory containing your .so files, assuming ./libs/native/linux/x86_64
LIB_DIR=$(pwd)/libs/native/linux/x86_64
# Set the LD_LIBRARY_PATH environment variable, adding the library directory to the front of the existing path
export LD_LIBRARY_PATH=$LIB_DIR:$LD_LIBRARY_PATH
# Run your Java application
java -jar YourApp.jar
# Or using classpath
# java -cp "YourClasspath" YourMainClass
Method 2: Using the JVM Parameter -Djava.library.path
This method directly tells the JVM where to look for library files.
# Determine the directory containing your .so files, assuming ./libs/native/linux/x86_64
LIB_DIR=$(pwd)/libs/native/linux/x86_64
# Run the Java application, specifying the library path via the -D parameter
java -Djava.library.path=$LIB_DIR -jar YourApp.jar
# Or using classpath
# java -Djava.library.path=$LIB_DIR -cp "YourClasspath" YourMainClass
Note:
- Method 1 (
LD_LIBRARY_PATH
) is recommended because it handles dependencies between libraries better. If you only use-Djava.library.path
, loading might fail sometimes because a library cannot find other libraries it depends on.- Ensure
$LIB_DIR
points to the exact directory containing files likelibagora_rtc_sdk.so
.- You can place the command to set the environment variable in a startup script so it's configured automatically each time the application runs.
Refer to the following script example, which combines both methods and sets the classpath:
#!/bin/bash
# Get the absolute path of the directory where the script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Determine the .so library file path (assuming in libs/native/linux/x86_64 relative to the script)
# Adjust x86_64 to your target architecture (e.g., aarch64) if needed
LIB_PATH="$SCRIPT_DIR/libs/native/linux/x86_64"
# SDK JAR path (assuming in libs relative to the script)
SDK_JAR="$SCRIPT_DIR/libs/agora-recording-sdk.jar"
# Your application's main class
MAIN_CLASS="YourMainClass"
# Your application's other dependency classpath (if any)
APP_CP="YourOtherClasspath"
# Check if the library directory exists
if [ ! -d "$LIB_PATH" ]; then
echo "Error: Library directory not found: $LIB_PATH" >&2
exit 1
fi
# Set the library path environment variable
export LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH
# Combine the classpath
CLASSPATH=".:$SDK_JAR:$APP_CP" # '.' represents the current directory
# Execute the Java program
# Use both LD_LIBRARY_PATH and -Djava.library.path for compatibility
java -Djava.library.path=$LIB_PATH -cp "$CLASSPATH" $MAIN_CLASS
Refer to Enable Service on the official website
The SDK provides a Spring Boot-based Maven example project for quick verification and secondary development. Steps to run Examples-Mvn
:
Create a .keys
file under Examples-Mvn
(replace with your values):
APP_ID=YourAppId
TOKEN=YourToken
-
Configure JAR (choose one of the following):
- Use online Maven version: edit
Examples-Mvn/pom.xml
and choose version by platform (x86_64 uses4.4.151.1
, arm64 uses4.4.151-aarch64
):<dependency> <groupId>io.agora.rtc</groupId> <artifactId>linux-recording-java-sdk</artifactId> <version>4.4.151.1</version> </dependency> <!-- For arm64 platform, replace with 4.4.151-aarch64 -->
- Use local version: in the
Examples-Mvn
directory, run the script to install the local JAR into your local Maven repository (the script readslibs/agora-recording-sdk.jar
and its javadoc):cd Examples-Mvn ./build_install_local_maven.sh
- Use online Maven version: edit
-
Prepare .so libraries: ensure
libs/native/linux/x86_64/
(oraarch64/
) contains the required.so
files (e.g.libagora_rtc_sdk.so
,librecording.so
, etc.).
In Examples-Mvn
:
./build.sh
After building, target/agora-example.jar
will be generated.
In Examples-Mvn
:
./build.sh start
- This starts the Spring Boot service on port 18080.
- To change the port, modify
-Dserver.port
.
-
Start recording:
http://<server_ip>:18080/api/recording/start?configFileName=mix_stream_recorder_audio_video_water_marks.json
-
Stop recording:
http://<server_ip>:18080/api/recording/stop?taskId=<task_id>
Place recording config files in
Examples-Mvn/src/main/resources/
.
These two parameters control the recording file writing method and video stream subscription processing respectively:
- recordEncodedOnly: Controls whether to write encoded data directly to MP4 file
- subscribeEncodedFrameOnly: Controls whether to decode the subscribed video stream
Parameter | Value | Meaning | Description |
---|---|---|---|
recordEncodedOnly | true | Write encoded data to MP4 | Write H.264/H.265 encoded data directly to MP4 file without decoding, high performance but no watermark support, single-stream only |
recordEncodedOnly | false | Decode then re-encode write | Decode first then re-encode to write MP4, supports watermark overlay and mixed-stream recording but consumes more CPU resources |
subscribeEncodedFrameOnly | true | Subscribe without decoding | Subscribe without decoding video stream, get encoded data directly, suitable for encoded frame capture |
subscribeEncodedFrameOnly | false | Subscribe with decoding | Decode video stream during subscription, get YUV raw data, suitable for scenarios requiring raw video processing |
recordEncodedOnly | subscribeEncodedFrameOnly | Mode Description | Features | Use Cases |
---|---|---|---|---|
false | false | Standard Mode | Subscribe with decoding + Decode then re-encode recording | Standard recording scenarios requiring watermarks, mixed-stream recording, video processing, YUV capture |
false | true | ❌ Not Feasible | Subscribe without decoding, no YUV data for recording | This combination is not feasible: no decoded data available for re-encoding recording |
true | false | Performance Mode | Subscribe with decoding + Write encoded data directly | High-performance single-stream recording, supports YUV processing but no watermarks or mixed-stream recording |
true | true | Speed Mode | Subscribe without decoding + Write encoded data directly | Highest performance single-stream recording, supports only encoded frame capture, no watermarks, mixed-stream or YUV processing |
1. Setting recordEncodedOnly (when initializing recorder)
// Create recorder
AgoraMediaRtcRecorder agoraMediaRtcRecorder = agoraService.createMediaRtcRecorder();
// Method 1: Use default value (recordEncodedOnly = false)
boolean enableMix = false; // Whether to enable mixed recording
agoraMediaRtcRecorder.initialize(agoraService, enableMix);
// Method 2: Explicitly set recordEncodedOnly
boolean enableMix = false; // Whether to enable mixed recording
boolean recordEncodedOnly = true; // Record encoded frames only for better performance
agoraMediaRtcRecorder.initialize(agoraService, enableMix, recordEncodedOnly);
2. Setting subscribeEncodedFrameOnly (when subscribing video)
// Create video subscription options
VideoSubscriptionOptions options = new VideoSubscriptionOptions();
// Set whether to subscribe encoded frames only
boolean subscribeEncodedFrameOnly = true; // Subscribe encoded frames only for encoded frame capture
options.setEncodedFrameOnly(subscribeEncodedFrameOnly);
options.setType(VideoStreamType.VIDEO_STREAM_HIGH);
// Subscribe video
if (subscribeAllVideo) {
agoraMediaRtcRecorder.subscribeAllVideo(options);
} else {
agoraMediaRtcRecorder.subscribeVideo("USER_ID", options);
}
3. Configuration in JSON file
Set these parameters in JSON configuration file:
{
"recordEncodedOnly": true, // Record encoded frames only
"subscribeEncodedFrameOnly": true, // Subscribe encoded frames only
"videoFrameCaptureType": 0, // 0=ENCODED (encoded frame capture)
"enableRecording": true,
"enableCapture": true
}
✅ Feasible Combinations:
-
Standard Recording:
recordEncodedOnly=false
+subscribeEncodedFrameOnly=false
- Supports watermarks, mixed-stream recording, video processing, YUV capture and other complete features
- Higher CPU consumption, suitable for scenarios requiring full functionality
-
High-Performance Single-Stream Recording:
recordEncodedOnly=true
+subscribeEncodedFrameOnly=false
- High recording performance, supports YUV processing but only single-stream recording, no watermarks or mixed-stream
- Suitable for scenarios requiring YUV data processing with high recording performance demands for single streams
-
Speed Single-Stream Recording:
recordEncodedOnly=true
+subscribeEncodedFrameOnly=true
- Highest performance with lowest CPU consumption
- Supports only single-stream recording and encoded frame capture, no watermarks, mixed-stream or YUV processing
- Suitable for large-scale concurrent single-stream recording scenarios
❌ Not Feasible Combination:
recordEncodedOnly=false
+subscribeEncodedFrameOnly=true
- This combination is logically contradictory: subscribing without decoding provides no YUV data for re-encoding recording
- For encoded frame capture functionality, use combinations with
recordEncodedOnly=true
- If the service fails to start, check
.so
paths,.keys
content, and port usage. - If there is no recording output, ensure there are active users in the channel and that AppId/Token/channel name are correct.
Ensure SDK integration, .keys
configuration, and .so
libraries are prepared (see “Run the Maven Project”).
In Examples-Mvn
:
./build.sh cli <configFileName>
# Example:
./build.sh cli capture_type_encoded_frame_mix_stream.json
After startup, enter 1
in the terminal to stop and exit.
-
Mixed-stream Recording:
mix_stream_recorder_audio_video.json
: mixed recording (audio + video)mix_stream_recorder_audio.json
: mixed, audio onlymix_stream_recorder_video.json
: mixed, video onlymix_stream_recorder_audio_video_encryption.json
: mixed, with encryptionmix_stream_recorder_audio_video_string_uid.json
: mixed, with string UIDmix_stream_recorder_audio_video_water_marks.json
: mixed, with watermarksmix_stream_recorder_audio_video_water_marks_bg.json
: mixed, watermarks + backgroundmix_stream_recorder_audio_video_water_marks_recover.json
: mixed, write h264/aac to allow MP4 recovery
-
Single-stream Recording:
single_stream_recorder_audio_video.json
: single, audio + videosingle_stream_recorder_audio.json
: single, audio onlysingle_stream_recorder_video.json
: single, video onlysingle_stream_recorder_audio_video_water_marks.json
: single, with watermarks
-
Snapshot Feature Examples:
capture_type_encoded_frame_mix_stream.json
: mixed recording + encoded frame snapshots (H.264, etc.), outputs MP4 and snapshot data;videoFrameCaptureType=ENCODED
,isMix=true
.capture_type_yuv_frame_single_stream.json
: single recording + YUV frame snapshots, outputs single MP4 and YUV snapshots;videoFrameCaptureType=YUV
,isMix=false
.capture_type_encoded_frame.json
: encoded frame snapshots only (no recording), mixed;videoFrameCaptureType=ENCODED
,enableRecording=false
.capture_type_yuv_frame.json
: YUV frame snapshots only (no recording), mixed;videoFrameCaptureType=YUV
,enableRecording=false
.capture_type_jpg_frame.json
: JPG frame snapshots (memory callback and save), mixed;videoFrameCaptureType=JPG_FRAME
,enableRecording=false
.capture_type_jpg_file.json
: JPG files written directly by SDK, mixed;videoFrameCaptureType=JPG_FILE
,enableRecording=false
.
Tip:
recorderPath
is the MP4 output path (file for mixed, for single it is the per-uid file prefix).capturePath
is the snapshot output prefix or directory. SeeConstants.VideoFrameCaptureType
for enum values.
Parameter | Type | Description |
---|---|---|
appId | String | Project App ID (can also be specified in .keys , which has priority). |
token | String | Channel Token; can be empty if token authentication is disabled (can also be in .keys , which has priority). |
channelName | String | Channel name, must match the client. |
useStringUid | Boolean | Whether to use string UID. false means numeric UID. |
useCloudProxy | Boolean | Whether to enable cloud proxy. |
userId | String | Recorder user ID; when set to "0" it will be assigned automatically. |
subAllAudio | Boolean | Subscribe to all audio; when false , use subAudioUserList . |
subAudioUserList | String[] | List of user IDs to subscribe audio from (effective when subAllAudio=false ). |
subAllVideo | Boolean | Subscribe to all video; when false , use subVideoUserList . |
subVideoUserList | String[] | List of user IDs to subscribe video from (effective when subAllVideo=false ). |
subStreamType | String | Stream type: high (high stream) or low (low stream). |
enableRecording | Boolean | Whether to record MP4. When false , you can snapshot only. |
enableCapture | Boolean | Whether to enable snapshot capability. |
videoFrameCaptureType | Integer | Snapshot type: 0=ENCODED, 1=YUV, 2=JPG_FRAME (memory callback & save), 3=JPG_FILE (SDK writes JPG). Maps to Constants.VideoFrameType /VideoFrameCaptureType . |
jpgCaptureIntervalInSec | Integer | JPG capture interval in seconds (default: 5). Only effective when videoFrameCaptureType=3 (JPG_FILE). |
isMix | Boolean | Whether to use mixed recording; false means single-stream. |
recordEncodedOnly | Boolean | Whether to record encoded frames only. When true , H.264/H.265 bitstream is written directly to MP4 without decoding (default: false). |
subscribeEncodedFrameOnly | Boolean | Whether to subscribe encoded frames only. When true , only encoded frames are subscribed without decoding (default: false). |
backgroundColor | Long | Mixed background color (0xRRGGBB as long). Effective when isMix=true . |
backgroundImage | String | Mixed background image (PNG/JPG). Takes precedence over backgroundColor when both are set. |
layoutMode | String | Mixed layout: default , bestfit , vertical . |
maxResolutionUid | String | UID displayed at maximum resolution in vertical layout. |
recorderStreamType | String | Recording type: audio_only , video_only , both . |
recorderPath | String | Output path: for mixed it's a file path; for single it's a directory (each UID generates a separate MP4). Ensure parent directories exist. |
capturePath | String | Snapshot output prefix or directory: for JPG_FILE it's a directory; for others it's a file prefix. |
maxDuration | Integer | Recording duration (seconds). Stops automatically when reached. |
recoverFile | Boolean | Whether to write h264/aac simultaneously to allow MP4 recovery after crashes (recording-related). |
audio.sampleRate | Integer | Audio sample rate (Hz). |
audio.numOfChannels | Integer | Number of audio channels. |
video.width | Integer | Video width (pixels). |
video.height | Integer | Video height (pixels). |
video.fps | Integer | Video frame rate (fps). |
waterMark[].type | String | Watermark type: litera (text), time (timestamp), picture (image). |
waterMark[].litera | String | Text watermark content (when type=litera). |
waterMark[].fontFilePath | String | Font file path (for litera/time). |
waterMark[].fontSize | Integer | Font size. |
waterMark[].x/y/width/height | Integer | Watermark rectangle position and size. |
waterMark[].zorder | Integer | Watermark layer order. |
waterMark[].imgUrl | String | Image watermark path (when type=picture). |
encryption.mode | String | Encryption type: AES_128_XTS , AES_128_ECB , AES_256_XTS , SM4_128_ECB , AES_128_GCM , AES_256_GCM , AES_128_GCM2 , AES_256_GCM2 . |
encryption.key | String | Encryption key. |
encryption.salt | String | Encryption salt (32 characters; required by some modes). |
rotation[].uid | String | UID whose video requires rotation. |
rotation[].degree | Integer | Rotation angle: 0, 90, 180, 270. |
stressTest.enable | Boolean | Whether to enable stress test mode (default: false). |
stressTest.enableSingleChannel | Boolean | Whether to use single channel for all threads in stress test (default: false). When false, each thread uses a separate channel. |
stressTest.threadNum | Integer | Number of concurrent threads for stress test (default: 1). |
stressTest.testTime | Integer | Total stress test duration in seconds (default: 10). |
stressTest.oneTestTime | Integer | Duration of each recording session in seconds (default: 3). |
stressTest.sleepTime | Integer | Sleep interval between recording sessions in seconds (default: 1). |
Before starting, ensure SDK environment and integration are complete, including the JAR and .so
libraries.
The following example comes from the actual Examples-Mvn
project and shows how to use the Recording SDK through API calls.
// Create AgoraService instance
AgoraService agoraService = new AgoraService();
// Configure local access point (must be set before initialize)
LocalAccessPointConfiguration localAccessPointConfig = new LocalAccessPointConfiguration();
localAccessPointConfig.setMode(Constants.LocalProxyMode.LocalOnly);
localAccessPointConfig.setIpList(new String[] { "10.xx.xx.xx" });
localAccessPointConfig.setIpListSize(1);
localAccessPointConfig.setVerifyDomainName("ap.xxx.agora.local");
int setGlobalLocalAccessPointRet = agoraService.setGlobalLocalAccessPoint(localAccessPointConfig);
// Create and configure service
AgoraServiceConfiguration config = new AgoraServiceConfiguration();
config.setEnableAudioDevice(false);
config.setEnableAudioProcessor(true);
config.setEnableVideo(true);
config.setAppId("YOUR_APPID");
config.setUseStringUid(false);
agoraService.initialize(config);
// Optional: set cloud proxy
AgoraParameter parameter = agoraService.getAgoraParameter();
if (parameter != null) {
parameter.setBool("rtc.enable_proxy", true);
}
// Create and initialize recorder
AgoraMediaRtcRecorder agoraMediaRtcRecorder = agoraService.createMediaRtcRecorder();
// The second parameter indicates whether to enable mixed recording: true=mixed, false=single
agoraMediaRtcRecorder.initialize(agoraService, false);
// Create and register event handler
IAgoraMediaRtcRecorderEventHandler handler = new AgoraMediaRtcRecorderEventHandler();
agoraMediaRtcRecorder.registerRecorderEventHandler(handler);
// Join channel
agoraMediaRtcRecorder.joinChannel(
"YOUR_TOKEN", // Channel token; can be null if not enforced
"YOUR_CHANNEL", // Channel name
"0" // User ID; "0" lets the system assign one
);
// Subscribe audio
if (subscribeAllAudio) {
agoraMediaRtcRecorder.subscribeAllAudio();
} else {
agoraMediaRtcRecorder.subscribeAudio("USER_ID");
}
// Subscribe video
VideoSubscriptionOptions options = new VideoSubscriptionOptions();
options.setEncodedFrameOnly(false);
options.setType(VideoStreamType.VIDEO_STREAM_HIGH);
if (subscribeAllVideo) {
agoraMediaRtcRecorder.subscribeAllVideo(options);
} else {
agoraMediaRtcRecorder.subscribeVideo("USER_ID", options);
}
// Mixed layout (only when mixed is enabled)
if (enableMix) {
VideoMixingLayout layout = new VideoMixingLayout();
// configure ...
agoraMediaRtcRecorder.setVideoMixingLayout(layout);
}
// Recorder configuration
MediaRecorderConfiguration mediaRecorderConfiguration = new MediaRecorderConfiguration();
mediaRecorderConfiguration.setWidth(640);
mediaRecorderConfiguration.setHeight(480);
mediaRecorderConfiguration.setFps(15);
mediaRecorderConfiguration.setMaxDurationMs(60 * 60 * 1000);
mediaRecorderConfiguration.setStoragePath("/path/to/save/recording.mp4");
// Mixed
agoraMediaRtcRecorder.setRecorderConfig(mediaRecorderConfiguration);
// Or single (uncomment if needed)
// agoraMediaRtcRecorder.setRecorderConfigByUid(mediaRecorderConfiguration, "USER_ID");
// Watermark (optional)
WatermarkConfig[] watermarks = new WatermarkConfig[1];
watermarks[0] = new WatermarkConfig();
// configure ...
agoraMediaRtcRecorder.enableAndUpdateVideoWatermarks(watermarks);
// Encryption (optional)
if (enableEncryption) {
EncryptionConfig encryptionConfig = new EncryptionConfig();
encryptionConfig.setEncryptionMode(EncryptionMode.AES_128_GCM);
encryptionConfig.setEncryptionKey("YOUR_ENCRYPTION_KEY");
agoraMediaRtcRecorder.enableEncryption(true, encryptionConfig);
}
// Start
if (enableMix) {
agoraMediaRtcRecorder.startRecording();
} else {
agoraMediaRtcRecorder.startSingleRecordingByUid("USER_ID");
}
public static class AgoraMediaRtcRecorderEventHandler implements IAgoraMediaRtcRecorderEventHandler {
@Override
public void onFirstRemoteAudioDecoded(String channelId, String userId, int elapsed) {
// Start single audio recording here when first remote audio is decoded
new Thread() {
@Override
public void run() {
MediaRecorderConfiguration mediaRecorderConfiguration = new MediaRecorderConfiguration();
// configure ...
agoraMediaRtcRecorder.setRecorderConfigByUid(mediaRecorderConfiguration, userId);
agoraMediaRtcRecorder.startSingleRecordingByUid(userId);
}
}.start();
}
@Override
public void onFirstRemoteVideoDecoded(String channelId, String userId, int width, int height, int elapsed) {
// Update mixed layout or start single video recording
new Thread() {
@Override
public void run() {
if (enableMix) {
VideoMixingLayout layout = new VideoMixingLayout();
// configure ...
agoraMediaRtcRecorder.setVideoMixingLayout(layout);
} else {
MediaRecorderConfiguration mediaRecorderConfiguration = new MediaRecorderConfiguration();
// configure ...
agoraMediaRtcRecorder.setRecorderConfigByUid(mediaRecorderConfiguration, userId);
agoraMediaRtcRecorder.startSingleRecordingByUid(userId);
}
}
}.start();
}
@Override
public void onRecorderStateChanged(String channelId, String userId, Constants.RecorderState state,
Constants.RecorderReasonCode reason, String fileName) {
// Observe state changes
}
// Other events ...
}
// Unsubscribe
agoraMediaRtcRecorder.unsubscribeAllAudio();
agoraMediaRtcRecorder.unsubscribeAllVideo();
// Stop
if (enableMix) {
agoraMediaRtcRecorder.stopRecording();
} else {
agoraMediaRtcRecorder.stopSingleRecordingByUid("USER_ID");
}
// Unregister handler
agoraMediaRtcRecorder.unregisterRecorderEventHandler(handler);
// Leave and release
agoraMediaRtcRecorder.leaveChannel();
agoraMediaRtcRecorder.release();
// Release service
agoraService.release();
Recorded files are saved depending on recording type:
-
Single-stream: MP4 files in the directory under
Examples-Mvn
, e.g.recorder_result/single/recorder_audio_video_uid_123456_timestamp.mp4
. -
Mixed-stream: a single MP4 in the path specified by
MediaRecorderConfiguration#storagePath
, e.g.recorder_result/mix/mix_audio_video_water_marks_timestamp.mp4
.
It is recommended to set a unique file path per session (channel name, timestamp, etc.) to avoid overwriting.
More options are available in the MediaRecorderConfiguration
API.
// 1) Implement snapshot observer
public static class MySnapshotObserver implements io.agora.recording.IRecorderVideoFrameObserver {
@Override
public void onYuvFrameCaptured(String channelId, String userId, io.agora.recording.VideoFrame frame) {
System.out.println("YUV frame: " + frame.getWidth() + "x" + frame.getHeight() + ", uid=" + userId);
// TODO: handle YUV data
}
@Override
public void onEncodedFrameReceived(String channelId, String userId, byte[] imageBuffer,
io.agora.recording.EncodedVideoFrameInfo info) {
System.out.println("Encoded frame: type=" + info.getFrameType() + ", codec=" + info.getCodecType()
+ ", uid=" + userId + ", size=" + (imageBuffer != null ? imageBuffer.length : 0));
// TODO: persist .h264 / .jpg buffer, or post-process
}
@Override
public void onJPGFileSaved(String channelId, String userId, String filename) {
System.out.println("JPG saved: " + filename + ", uid=" + userId);
}
}
// 2) Build capture config and enable
io.agora.recording.RecorderVideoFrameCaptureConfig capCfg = new io.agora.recording.RecorderVideoFrameCaptureConfig();
capCfg.setObserver(new MySnapshotObserver());
capCfg.setVideoFrameType(io.agora.recording.Constants.VideoFrameType.VIDEO_FRAME_TYPE_ENCODED); // ENCODED/YUV/JPG/JPG_FILE
capCfg.setJpgFileStorePath("/path/to/snapshots/"); // JPG_FILE mode only
capCfg.setJpgCaptureIntervalInSec(5); // interval (sec) for JPG/JPG_FILE modes
int ret = agoraMediaRtcRecorder.enableRecorderVideoFrameCapture(true, capCfg);
if (ret != 0) {
System.err.println("enableRecorderVideoFrameCapture failed: " + ret);
}
// 3) Disable when needed
// agoraMediaRtcRecorder.enableRecorderVideoFrameCapture(false, capCfg);
Note: For encoded frame capture (ENCODED), the following settings are required:
- Set
setEncodedFrameOnly(true)
in video subscription options, which is necessary for encoded frame capture
// Set video subscription options to support encoded frame capture
VideoSubscriptionOptions options = new VideoSubscriptionOptions();
boolean encodedFrameOnly = true; // Set to true to enable encoded frame capture
options.setEncodedFrameOnly(encodedFrameOnly);
options.setType(Utils.convertToVideoStreamType(subStreamType));
See API-reference.md for detailed SDK APIs.
- Removed:
AgoraMediaComponentFactory
class. Create recorder viaAgoraService#createMediaRtcRecorder()
. - Added:
AgoraMediaRtcRecorder#enableRecorderVideoFrameCapture(boolean, RecorderVideoFrameCaptureConfig)
to support ENCODED/YUV/JPG/JPG_FILE snapshots. - Added:
AgoraMediaRtcRecorder#initialize(AgoraService, boolean, boolean recordEncodedOnly)
; withrecordEncodedOnly=true
, video is not decoded and H.264 or H.265 bitstream is written directly to MP4 file. - Added:
IRecorderVideoFrameObserver
callbacks:onYuvFrameCaptured
,onEncodedFrameReceived
,onJPGFileSaved
. - Added:
RecorderVideoFrameCaptureConfig
fieldsvideoFrameType
,jpgFileStorePath
,jpgCaptureIntervalInSec
,observer
. - Added:
Constants
enumsVideoCodecType
,VideoFrameType
,VideoOrientation
.
- Changed:
unregisterRecorderEventHandle
method inAgoraMediaRtcRecorder
class renamed tounregisterRecorderEventHandler
to unify method naming conventions
- Fixed: Fixed the thread safety issue of the
setVideoMixingLayout
method in theAgoraMediaRtcRecorder
class - Changed: The maximum size of a single log file is changed to 1GB
- Added: Added
renewToken
method toAgoraMediaRtcRecorder
class to support dynamic channel token renewal, preventing recording interruption due to token expiration - Changed: Renamed
Constants.WaterMaskFitMode
toConstants.WatermarkFitMode
to fix spelling error and maintain naming consistency
- Fixed: Fixed
imagePath
property setting issue inMixerLayoutConfig
class to ensure proper background image path configuration
- Added:
onError
、onTokenPrivilegeWillExpire
、onTokenPrivilegeDidExpire
callback methods inIAgoraMediaRtcRecorderEventHandler
for error notifications and token expiration notifications.
- Added: Added the
setGlobalLocalAccessPoint
function to theAgoraService
class for configuring the global local access point.
- Fixed: Fixed the callback handling issue when packaging with SpringBot.
- Added:
onEncryptionError
callback inIAgoraMediaRtcRecorderEventHandler
for encryption error notifications. - Added:
setAudioVolumeIndicationParameters
method inAgoraMediaRtcRecorder
to configure the interval for remote user volume callbacks. - Refactored: Optimized the parameter structure of the
onAudioVolumeIndication
callback inIAgoraMediaRtcRecorderEventHandler
.
- Enhanced: Improved
VideoMixingLayout
, fixedbackgroundColor
property. - New Feature: Added
backgroundImage
property toVideoMixingLayout
to support setting background images.
- Compatibility: API compatible with v4.4.150.
- Platform: Initial support for ARM64 architecture.
- Initial Release: Basic API structure published.
- Performance: Basic functionality and performance optimizations.
Refer to the official documentation website for details: (e.g., https://docs.agora.io/en/recording/java/landing-page) (Update link as necessary)