本页介绍通过 RTSA Lite SDK 实现媒体流和信令传输涉及的 API 方法和调用流程。
RTSA 通过 License 对设备鉴权。License 与设备绑定,一个 License 在同一时间只能绑定一个设备。详见 License 使用指南。
调用 agora_rtc_init
方法(Java API 为 init
)初始化 RTSA 服务。
在该方法中,你需要:
agora_rtc_event_handler_t
,用以通知 SDK 在运行过程中发生的事件。NULL
表示使用默认目录)。rtc_service_option_t
结构体设置 RTSA 服务选项。C 示例代码
// C
// 初始化事件回调
static void __on_join_channel_success(connection_id_t conn_id, uint32_t uid, int elapsed)
{
g_connected_flag = true;
agora_rtc_get_connection_info(conn_id, &g_conn_info);
printf("[conn-%u] Join the channel %s successfully, uid %u elapsed %d ms\n", conn_id, g_conn_info.channel_name, uid,
elapsed);
}
static void __on_connection_lost(connection_id_t conn_id)
{
g_connected_flag = false;
printf("[conn-%u] Lost connection from the channel\n", conn_id);
}
static void __on_rejoin_channel_success(connection_id_t conn_id, uint32_t uid, int elapsed_ms)
{
g_connected_flag = true;
printf("[conn-%u] Rejoin the channel successfully, uid %u elapsed %d ms\n", conn_id, uid, elapsed_ms);
}
static void __on_user_joined(connection_id_t conn_id, uint32_t uid, int elapsed_ms)
{
printf("[conn-%u] Remote user \"%u\" has joined the channel, elapsed %d ms\n", uid, conn_id, elapsed_ms);
}
static void __on_user_offline(connection_id_t conn_id, uint32_t uid, int reason)
{
printf("[conn-%u] Remote user \"%u\" has left the channel, reason %d\n", conn_id, uid, reason);
}
static void __on_user_mute_audio(connection_id_t conn_id, uint32_t uid, bool muted)
{
printf("[conn-%u] audio: uid=%u muted=%d\n", conn_id, uid, muted);
}
static void __on_user_mute_video(connection_id_t conn_id, uint32_t uid, bool muted)
{
printf("[conn-%u] video: uid=%u muted=%d\n", conn_id, uid, muted);
}
static void __on_error(connection_id_t conn_id, int code, const char *msg)
{
if (code == ERR_SEND_VIDEO_OVER_BANDWIDTH_LIMIT) {
printf("Not enough uplink bandwdith. Error msg \"%s\"\n", msg);
return;
}
if (code == ERR_INVALID_APP_ID) {
printf("Invalid App ID. Please double check. Error msg \"%s\"\n", msg);
} else if (code == ERR_INVALID_CHANNEL_NAME) {
printf("Invalid channel name. Please double check. Error msg \"%s\"\n", msg);
} else if (code == ERR_INVALID_TOKEN || code == ERR_TOKEN_EXPIRED) {
printf("Invalid token. Please double check. Error msg \"%s\"\n", msg);
} else if (code == ERR_DYNAMIC_TOKEN_BUT_USE_STATIC_KEY) {
printf("Dynamic token is enabled but is not provided. Error msg \"%s\"\n", msg);
} else {
printf("Error %d is captured. Error msg \"%s\"\n", code, msg);
}
g_stop_flag = true;
}
static void __on_audio_data(connection_id_t conn_id, const uint32_t uid, uint16_t sent_ts, const void *data, size_t len,
const audio_frame_info_t *info_ptr)
{
}
static void __on_mixed_audio_data(connection_id_t conn_id, const void *data, size_t len,
const audio_frame_info_t *info_ptr)
{
}
static void __on_video_data(connection_id_t conn_id, const uint32_t uid, uint16_t sent_ts, const void *data, size_t len,
const video_frame_info_t *info_ptr)
{
}
static void __on_target_bitrate_changed(connection_id_t conn_id, uint32_t target_bps)
{
printf("[conn-%u] Bandwidth change detected. Please adjust encoder bitrate to %u kbps\n", conn_id, target_bps / 1000);
}
static void __on_key_frame_gen_req(connection_id_t conn_id, uint32_t uid, video_stream_type_e stream_type)
{
printf("[conn-%u] Frame loss detected. Please notify the encoder to generate key frame immediately\n", conn_id);
}
static void app_init_event_handler(agora_rtc_event_handler_t *event_handler)
{
event_handler->on_join_channel_success = __on_join_channel_success;
event_handler->on_connection_lost = __on_connection_lost;
event_handler->on_rejoin_channel_success = __on_rejoin_channel_success;
event_handler->on_user_joined = __on_user_joined;
event_handler->on_user_offline = __on_user_offline;
event_handler->on_user_mute_audio = __on_user_mute_audio;
event_handler->on_user_mute_video = __on_user_mute_video;
event_handler->on_target_bitrate_changed = __on_target_bitrate_changed;
event_handler->on_key_frame_gen_req = __on_key_frame_gen_req;
event_handler->on_video_data = __on_video_data;
event_handler->on_error = __on_error;
event_handler->on_mixed_audio_data = __on_mixed_audio_data;
event_handler->on_audio_data = __on_audio_data;
}
agora_rtc_event_handler_t event_handler = { 0 };
app_init_event_handler(&event_handler);
rtc_service_option_t service_opt = { 0 };
service_opt.area_code = DEFAULT_AREA_CODE;
service_opt.log_cfg.log_path = DEFAULT_SDK_LOG_PATH;
rval = agora_rtc_init(AGORA_APP_ID_FOR_TEST, &event_handler, &service_opt);
if (rval < 0) {
printf("Failed to initialize声网sdk, reason: %s\n", agora_rtc_err_2_str(rval));
return -1;
}
Java 示例代码
// 初始化
RtcServiceOptions options = new RtcServiceOptions();
options.areaCode = AreaCode.AREA_CODE_GLOB;
options.productId = "MyDevice01";
options.logCfg.logDisable = false;
options.logCfg.logDisableDesensitize = false;
options.logCfg.logLevel = LogLevel.RTC_LOG_DEFAULT;
options.logCfg.logPath = "./rtcsdk.log";
MyRtcEvent rtcEvent = new MyRtcEvent();
rtcEvent.mRtcService = mRtcService;
int ret = mRtcService.init(APPID, rtcEvent, options);
if (ret != ErrorCode.ERR_OKAY) {
RtsaLiteDemo.e(TAG, "<main> fail to init(), ret=" + ret);
return;
}
// 注册音视频事件回调
class MyRtcEvent implements AgoraRtcEvents {
private final static String TAG = "DEMO/MyRtcEvent";
public AgoraRtcService mRtcService = null;
public ConnectionInfo connInfo = new ConnectionInfo();
@Override
public void onJoinChannelSuccess(int connId, int uid, int elapsed_ms) {
RtsaLiteDemo.d(TAG, "<onJoinChannelSuccess> connId=" + connId +
" uid=" + uid + " elapsed_ms=" + elapsed_ms);
if (mRtcService.getConnectionInfo(connId, connInfo) == 0) {
RtsaLiteDemo.d(TAG, "get connInfo: connId=" + connInfo.connId +
" uid=" + connInfo.uid + " cname=" + connInfo.channelName);
}
synchronized (RtsaLiteDemo.mJoinedEvent) {
RtsaLiteDemo.mJoinedEvent.notify(); // 事件通知
}
}
@Override
public void onConnectionLost(int connId) {
RtsaLiteDemo.d(TAG, "<onConnectionLost> connId=" + connId);
}
@Override
public void onRejoinChannelSuccess(int connId, int uid, int elapsed_ms) {
RtsaLiteDemo.d(TAG,
"<onRejoinChannelSuccess> connId=" + connId + ", elapsed_ms=" + elapsed_ms);
synchronized (RtsaLiteDemo.mJoinedEvent) {
RtsaLiteDemo.mJoinedEvent.notify(); // 事件通知
}
}
@Override
public void onError(int connId, int code, String msg) {
RtsaLiteDemo.d(TAG, "<onError> connId=" + connId + ", code=" + code + ", msg=" + msg);
}
@Override
public void onUserJoined(int connId, int uid, int elapsed_ms) {
RtsaLiteDemo.d(TAG, "<onUserJoined> connId=" + connId + ", uid=" + uid + ", elapsed_ms="
+ elapsed_ms);
}
@Override
public void onUserOffline(int connId, int uid, int reason) {
RtsaLiteDemo.d(TAG,
"<onUserOffline> connId=" + connId + ", uid=" + uid + ", reason=" + reason);
}
@Override
public void onUserMuteAudio(int connId, int uid, boolean muted) {
RtsaLiteDemo.d(TAG,
"<onUserMuteAudio> connId=" + connId + ", uid=" + uid + ", muted=" + muted);
}
@Override
public void onUserMuteVideo(int connId, int uid, boolean muted) {
RtsaLiteDemo.d(TAG,
"<onUserMuteVideo> connId=" + connId + ", uid=" + uid + ", muted=" + muted);
}
@Override
public void onKeyFrameGenReq(int connId, int requestedUid, int streamType) {
}
@Override
public void onAudioData(int connId, int uid, int sent_ts, byte[] data, AudioFrameInfo info) {
RtsaLiteDemo.d(TAG, "<onAudioData> connId=" + connId + " uid=" + uid
+ " dataType=" + info.dataType);
}
@Override
public void onMixedAudioData(int connId, byte[] data, AudioFrameInfo info) {
RtsaLiteDemo.d(TAG, "<onMixedAudioData> connId=" + connId
+ " dataType=" + info.dataType);
}
@Override
public void onVideoData(int connId, int uid, int sent_ts, byte[] data, VideoFrameInfo info) {
RtsaLiteDemo.d(TAG, "<onVideoData> connId=" + connId + " uid=" + uid
+ " dataType=" + info.dataType + " streamType=" + info.streamType
+ " frameType=" + info.frameType + " frameRate=" + info.frameRate);
}
@Override
public void onTargetBitrateChanged(int connId, int targetBps) {
}
@Override
public void onTokenPrivilegeWillExpire(int connId, String token) {
RtsaLiteDemo.d(TAG, "<onTokenPrivilegeWillExpire> token=" + token);
}
@Override
public void onMediaCtrlReceive(int connId, int uid, byte[] payload) {
RtsaLiteDemo.d(TAG, "<onMediaCtrlReceive> connId=" + connId + ", uid=" + uid);
}
}
// 注册云信令事件回调
class MyRtmEvent implements AgoraRtmEvents {
private final static String TAG = "DEMO/MyRtmEvent";
@Override
public void onRtmData(String rtm_uid, byte[] data) {
String dataText = "";
for (int i = 0; i < data.length; i++) {
dataText = dataText + data[i] + " ";
}
RtsaLiteDemo.d(TAG, "<onRtmData> rtm_uid=" + rtm_uid + ", dataSize=" + data.length
+ ", data=" + dataText);
}
@Override
public void onRtmEvent(String rtm_uid, int event_type, int err_code) {
RtsaLiteDemo.d(TAG, "<onRtmEvent> rtm_uid=" + rtm_uid + ", event_type=" + event_type
+ ", err_code=" + err_code);
if (event_type == AgoraRtmEvents.RtmEventType.RTM_EVENT_TYPE_LOGIN) {
synchronized (RtsaLiteDemo.mRtmLoginEvent) {
RtsaLiteDemo.mRtmLoginEvent.notify(); // 事件通知
}
}
}
@Override
public void onSendRtmDataResult(int message_id, int err_code) {
RtsaLiteDemo.d(TAG,
"<onSendRtmDataResult> message_id=" + message_id + ", err_code=" + err_code);
}
}
调用 agora_rtc_create_connection
(Java API 为 createConnection
) 方法创建 Connection。调用 agora_rtc_join_channel
(Java API 为 joinChannel
) 方法关联 Connection 并加入 RTC 频道。
在该方法中,你需要:
token
设为空。用户与 RTC 频道的关系如下:
成功加入 RTC 频道后,SDK 会触发 on_join_channel_success
(Java API 为 onJoinChannelSuccess
) 回调。
示例代码如下:
C 示例代码
// C
// 创建 Connection
rval = agora_rtc_create_connection(&g_conn_id);
if (rval < 0) {
printf("Failed to create connection, reason: %s\n", agora_rtc_err_2_str(rval));
return -1;
}
rtc_channel_options_t channel_options = { 0 };
channel_options.auto_subscribe_audio = true;
channel_options.auto_subscribe_video = true;
// 示例使用 SDK 内置编码器,编码格式为 Opus
channel_options.audio_codec_opt.audio_codec_type = AUDIO_CODEC_TYPE_OPUS;
channel_options.audio_codec_opt.pcm_sample_rate = 16000;
channel_options.audio_codec_opt.pcm_channel_num = 1;
// 加入 RTC 频道
rval = agora_rtc_join_channel(g_conn_id, DEFAULT_CHANNEL_NAME, DEFAULT_USER_ID, DEFAULT_TOKEN, &channel_options);
if (rval < 0) {
printf("Failed to join channel \"%s\", reason: %s\n", DEFAULT_CHANNEL_NAME, agora_rtc_err_2_str(rval));
return -1;
}
while (!g_connected_flag) {
usleep(100 * 1000);
}
Java 示例代码
// Java
// 创建 Connection
CONN_ID = mRtcService.createConnection();
if (CONN_ID == ConnectionIdSepcial.CONNECTION_ID_INVALID) {
RtsaLiteDemo.e(TAG, "<main> fail to createConnection(), ret=" + CONN_ID);
return;
}
// 设置 RTC 频道属性
ChannelOptions chnlOption = new ChannelOptions();
chnlOption.autoSubscribeAudio = true;
chnlOption.autoSubscribeVideo = true;
// 示例使用 SDK 内置编码器,编码格式为 Opus
chnlOption.audioCodecOpt.audioCodecType = AudioCodecType.AUDIO_CODEC_TYPE_OPUS;
chnlOption.audioCodecOpt.pcmSampleRate = 16000;
chnlOption.audioCodecOpt.pcmChannelNum = 1;
// 加入 RTC 频道
ret = mRtcService.joinChannel(CONN_ID, CHANNEL_NAME, RTC_HOST_USER_ID, RTC_TOKEN,
chnlOption);
if (ret != ErrorCode.ERR_OKAY) {
RtsaLiteDemo.e(TAG, "<main> fail to joinChannel(), ret=" + ret);
mRtcService.fini();
return;
}
synchronized (mJoinedEvent) {
try {
mJoinedEvent.wait(10000);
} catch (InterruptedException e) {
e.printStackTrace();
RtsaLiteDemo.e(TAG, "<main> join channel timeout");
return;
}
}
RtsaLiteDemo.d(TAG, "<main> join channel: " + CHANNEL_NAME + " successful");
成功加入 RTC 频道后,可以:
on_audio_data
回调(Java API 为 onAudioData
)接收所有你已加入的频道内的音频数据流。on_video_data
回调(Java API 为 onVideoData
)接收所有你已加入的频道内的视频数据流。agora_rtc_send_audio_data
方法(Java API 为 sendAudioData
)向指定或所有你已加入的频道发送音频数据流。agora_rtc_send_video_data
方法(Java API 为 sendVideoData
)向指定或所有你已加入的频道发送视频数据流。示例代码如下:
C 示例代码
// C
static void __on_audio_data(connection_id_t conn_id, const uint32_t uid, uint16_t sent_ts, const void *data, size_t len,
const audio_frame_info_t *info_ptr)
{
}
static void __on_video_data(connection_id_t conn_id, const uint32_t uid, uint16_t sent_ts, const void *data, size_t len,
const video_frame_info_t *info_ptr)
{
}
// 发送音频数据流
static int send_audio_frame(uint8_t *data, uint32_t len)
{
// API: send audio data
audio_frame_info_t info = { 0 };
info.data_type = AUDIO_DATA_TYPE_PCM;
int rval = agora_rtc_send_audio_data(g_conn_id, data, len, &info);
if (rval < 0) {
printf("Failed to send audio data, reason: %s\n", agora_rtc_err_2_str(rval));
return -1;
}
return 0;
}
// 发送视频数据流
static int send_video_frame(uint8_t *data, uint32_t len)
{
video_frame_info_t info = { 0 };
info.frame_type = VIDEO_FRAME_KEY;
info.frame_rate = CONFIG_SEND_FRAME_RATE;
info.stream_type = VIDEO_STREAM_HIGH;
info.data_type = VIDEO_DATA_TYPE_H264;
int rval = agora_rtc_send_video_data(g_conn_id, data, len, &info);
if (rval < 0) {
printf("Failed to send video data, reason: %s\n", agora_rtc_err_2_str(rval));
return -1;
}
return 0;
}
发送音视频数据时,需要设置发送间隔。对于视频,发送间隔长度需要和帧率一致;对于音频,发送间隔长度需要和音频帧长度一致。
// 视频发送线程
#ifndef CONFIG_AUDIO_ONLY
static void *video_send_thread(void *threadid)
{
int video_send_interval_ms = 1000 / CONFIG_SEND_FRAME_RATE;
void *pacer = pacer_create(video_send_interval_ms);
uint32_t frame_count = 0;
int num_frames = sizeof(test_video_frames) / sizeof(test_video_frames[0]);
while (g_connected_flag && !g_stop_flag) {
int i = (frame_count++ % num_frames); // calculate frame index
send_video_frame(test_video_frames[i].data, test_video_frames[i].len);
wait_for_next_pace(pacer);
}
pacer_destroy(pacer);
return NULL;
}
#endif
#define CONFIG_PCM_FRAME_LEN (640) // 示例为 640 KB,实际值取决于 PCM 数据的采集设备
#define CONFIG_PCM_SAMPLE_RATE (16000)
#define CONFIG_PCM_CHANNEL_NUM (1)
#define CONFIG_AUDIO_FRAME_DURATION_MS \
(CONFIG_PCM_FRAME_LEN * 1000 / CONFIG_PCM_SAMPLE_RATE / CONFIG_PCM_CHANNEL_NUM / sizeof(int16_t))
// 音频发送线程
static void *audio_send_thread(void *threadid)
{
int audio_send_interval_ms = CONFIG_AUDIO_FRAME_DURATION_MS;
void *pacer = pacer_create(audio_send_interval_ms);
uint32_t pcm_offset = 0;
while (g_connected_flag && !g_stop_flag) {
send_audio_frame((uint8_t *)pcm_test_data + pcm_offset, CONFIG_PCM_FRAME_LEN);
pcm_offset += CONFIG_PCM_FRAME_LEN;
if ((pcm_offset + CONFIG_PCM_FRAME_LEN) > sizeof(pcm_test_data)) {
pcm_offset = 0;
}
// sleep and wait until time is up for next send
wait_for_next_pace(pacer);
}
pacer_destroy(pacer);
return NULL;
}
Java 示例代码
发送音视频数据时,需要设置发送间隔。对于视频,发送间隔长度需要和帧率一致;对于音频,发送间隔长度需要和音频帧长度一致。
// Java
@Override
public void onAudioData(int connId, int uid, int sent_ts, byte[] data, AudioFrameInfo info) {
RtsaLiteDemo.d(TAG, "<onAudioData> connId=" + connId + " uid=" + uid
+ " dataType=" + info.dataType);
}
@Override
public void onVideoData(int connId, int uid, int sent_ts, byte[] data, VideoFrameInfo info) {
RtsaLiteDemo.d(TAG, "<onVideoData> connId=" + connId + " uid=" + uid
+ " dataType=" + info.dataType + " streamType=" + info.streamType
+ " frameType=" + info.frameType + " frameRate=" + info.frameRate);
}
// 发送视频
@Override
public void run() {
RtsaLiteDemo.d(TAG, "<VideoSendThread.run> ==>Enter");
StreamFile videoStream = new StreamFile();
videoStream.open(VIDEO_FILE);
int frameIndex = 0;
while (mVideoSending && (videoStream.isOpened())) {
// read video frame
if (frameIndex >= FRAME_COUNT) {
RtsaLiteDemo.d(TAG, "<VideoSendThread.run> read video frame EOF");
mVideoSending = false;
break;
}
int frameSize = FRAME_SIZE_ARR[frameIndex];
byte[] videoBuffer = new byte[frameSize];
int readSize = videoStream.readData(videoBuffer);
if (readSize <= 0) {
RtsaLiteDemo.e(TAG,
"<VideoSendThread.run> read video frame error, readSize=" + readSize);
}
// 发送视频
VideoFrameInfo videoFrameInfo = new VideoFrameInfo();
videoFrameInfo.dataType = VideoDataType.VIDEO_DATA_TYPE_H264;
videoFrameInfo.streamType = VideoStreamType.VIDEO_STREAM_HIGH;
videoFrameInfo.frameType = VideoFrameType.VIDEO_FRAME_KEY;
videoFrameInfo.frameRate = VideoFrameRate.VIDEO_FRAME_RATE_FPS_15;
int ret = mRtcService.sendVideoData(CONN_ID, videoBuffer, videoFrameInfo);
if (ret < 0) {
RtsaLiteDemo.e(TAG, "<VideoSendThread.run> sendVideoData() failure, ret=" + ret
+ ", dataSize=" + videoBuffer.length);
} else {
}
frameIndex++;
videoBuffer = null;
// 线程每个循环 sleep 66 ms,即 1000/15。15 为视频的帧率。
sleepCurrThread(66);
}
videoStream.close();
RtsaLiteDemo.d(TAG, "<VideoSendThread.run> <==Exit");
// Notify: exit video thread
synchronized (mVideoExitEvent) {
mVideoExitEvent.notify();
}
}
// 发送音频
private final static int PCM_SAMPLE_RATE = 16000;
private final static int PCM_CHNL_NUMBER = 1;
private final static int PCM_SMPL_BYTES = 2;
@Override
public void run() {
RtsaLiteDemo.d(TAG, "<AudioSendThread.run> ==>Enter");
StreamFile audioStream = new StreamFile();
audioStream.open(AUDIO_FILE);
int bytesPerSec = PCM_SAMPLE_RATE * PCM_CHNL_NUMBER * PCM_SMPL_BYTES;
int bufferSize = bytesPerSec / 50; // 假设每个音频帧的字节数为 640,则发送间隔为 640/32000 s = 20 ms,那么一秒钟需要发送 50 次,因此需要长度为 50 的 bye array
byte[] readBuffer = new byte[bufferSize];
byte[] sendBuffer = new byte[bufferSize];
while (mAudioSending) {
// read audio frame
int readSize = audioStream.readData(readBuffer);
if (readSize <= 0) {
RtsaLiteDemo.d(TAG, "<AudioSendThread.run> read audio frame EOF");
mAudioSending = false;
break;
}
if (readSize != sendBuffer.length) {
sendBuffer = new byte[readSize];
}
System.arraycopy(readBuffer, 0, sendBuffer, 0, readSize);
AudioFrameInfo audioFrameInfo = new AudioFrameInfo();
audioFrameInfo.dataType = AudioDataType.AUDIO_DATA_TYPE_PCM;
int ret = mRtcService.sendAudioData(CONN_ID, sendBuffer, audioFrameInfo);
if (ret < 0) {
RtsaLiteDemo.e(TAG,
"<AudioSendThread.run> sendAudioData() failure, ret=" + ret);
}
// 线程每个循环 sleep 20 ms,即每次发送音频数据长度为 20 ms。
sleepCurrThread(20);
}
audioStream.close();
RtsaLiteDemo.d(TAG, "<AudioSendThread.run> <==Exit");
synchronized (mAudioExitEvent) {
mAudioExitEvent.notify();
}
}
调用 agora_rtc_login_rtm
(Java API 为 loginRtm
) 方法登录 RTM 系统。登录成功之后,你可以发送和接收信令。
在该方法中,你需要:
token
设空。C 示例代码
// C
rval = agora_rtc_login_rtm(p_config->p_rtm_uid, p_config->p_token, &rtm_handler);
if (rval < 0) {
printf("login rtm failed\n");
goto EXIT;
}
Java 示例代码
// Java
MyRtmEvent rtmEvent = new MyRtmEvent();
int rtmRet = mRtcService.loginRtm(RTM_HOST_USER_ID, null, rtmEvent);
成功登录 RTM 系统后,你可以:
on_rtm_data
回调(Java API 为 onRtmData
)接收你登录的 RTM 系统中对端发送信令。on_rtm_event
回调(Java API 为 onRtmEvent
)监听本地用户状态。on_send_rtm_data_result
回调(Java API 为 onSendRtmDataResult
)监听本地信令发送结果。agora_rtc_send_rtm_data
方法(Java API 为 sendRtm
)向指定 RTM 用户发送信令。C 示例代码
// c
// 接收你登录的 RTM 系统中对端发送信令
static void __on_rtm_data(const char *user_id, const void *data, size_t data_len)
{
app_t *p_app = app_get_instance();
app_config_t *p_config = &p_app->config;
if (p_config->rtm_role == 3) {
// printf("Receive data[%s] from user[%s] length[%lu]\n", (char *)data, user_id, data_len);
printf("Receive data[%s] length[%d]\n", (char *)data, (int)data_len);
} else {
printf("data_callback %s data[], length[%lu]\n", user_id, data_len);
}
if (p_app->config.rtm_recv_dump_flag && p_app->rtm_recv_file_fd != INVALID_FD) {
if (write(p_app->rtm_recv_file_fd, data, data_len) != data_len) {
printf("write error\n");
return;
}
}
}
// 监听本地用户状态
static void __on_rtm_event(const char *user_id, uint32_t event_id, uint32_t event_code)
{
LOGD("%s event id[%u], event code[%u]", user_id, event_id, event_code);
if (event_id == 0 && event_code == 0) {
app_t *p_app = app_get_instance();
p_app->b_rtm_login_success_flag = 1;
}
}
// 监听本地信令发送结果
static void __on_rtm_send_data_res(uint32_t msg_id, uint32_t error_code)
{
LOGD("msg id [%u], error_code[%u]", msg_id, error_code);
}
// 向指定 RTM 用户发送信令
rval = agora_rtc_send_rtm_data(p_app->config.p_peer_uid, ++message_id, buffer, rval);
if (rval < 0) {
LOGE("%s send data failed, rval=%d", TAG_API, rval);
goto EXIT;
}
Java 示例代码
// Java
// 向指定 RTM 用户发送信令
rtmRet = mRtcService.sendRtm(PEER_USER_ID, message_id, message);
if (rtmRet != RtmErrCode.ERR_RTM_OK) {
RtsaLiteDemo.e(TAG, "<main> sendRtm() failure, rtmRet=" + rtmRet
+ ", message_id=" + message_id);
} else {
RtsaLiteDemo.d(TAG, "<main> sendRtm() success, rtmRet=" + rtmRet
+ ", message_id=" + message_id);
}
// 注册云信令事件回调
class MyRtmEvent implements AgoraRtmEvents {
private final static String TAG = "DEMO/MyRtmEvent";
// 接收你登录的 RTM 系统中对端发送信令
@Override
public void onRtmData(String rtm_uid, byte[] data) {
}
// 监听本地用户状态
@Override
public void onRtmEvent(String rtm_uid, int event_type, int err_code) {
}
// 监听本地信令发送结果
@Override
public void onSendRtmDataResult(int message_id, int err_code) {
}
}
调用 agora_rtc_leave_channel
方法(Java API 为 leaveChannel
)离开指定频道,结束在该频道的数据传输。
示例代码如下:
C 示例代码
// C
agora_rtc_leave_channel(g_conn_id);
agora_rtc_destroy_connection(g_conn_id);
Java 示例代码
// Java
ret = mRtcService.leaveChannel(CONN_ID);
if (ret != ErrorCode.ERR_OKAY) {
RtsaLiteDemo.e(TAG, "<main> fail to leaveChannel(), ret=" + ret);
}
// Destroy connection
ret = mRtcService.destroyConnection(CONN_ID);
CONN_ID = ConnectionIdSepcial.CONNECTION_ID_INVALID;
调用 agora_rtc_logout_rtm
方法(Java API 为 logoutRtm
)登出 RTM,结束在 RTM 系统中的消息传输。
C 示例代码
// C
agora_rtc_logout_rtm();
Java 示例代码
// Java
ret = mRtcService.logoutRtm();
if (ret != RtmErrCode.ERR_RTM_OK) {
RtsaLiteDemo.e(TAG, "<main> fail to logoutRtm(), ret=" + ret);
}
当你不再需要使用 SDK 的功能时,调用 agora_rtc_fini
(Java API 为 fini
)销毁 SDK 实例释放资源。
// C
agora_rtc_fini();
// Java
mRtcService.fini();
RTSA Lite SDK 包含实时音视频 API 和实时信令 API。
实时音视频 API 时序图
下图展示了从设备端向客户端发送实时音视频的 API 时序。
实时信令 API 时序图
下图展示了设备端和客户端进行实时信令交互的 API 时序。
你可以通过 C API 与 Java API 的参考文档了解更多信息。