本页介绍通过 RTSA Lite SDK 实现媒体流传输涉及的 API 方法和调用流程。
RTSA 通过 License 对设备鉴权。License 与设备绑定,一个 License 在同一时间只能绑定一个设备。
自 v1.4.2 起,SDK 下载包中包含了许可证文件,可直接试用 RTSA Lite SDK 提供的示例项目。
调用 RTSA Lite 的其他 API 前,你需要调用 agora_rtc_license_verify
方法(Java 为 licenseVerify
方法)完成认证,传入许可证 Certificate 和相应长度 certificate_len 。
示例代码如下,仅供参考:
// C
char *str_certificate = util_get_string_from_file(DEFAULT_CERTIFACTE_FILENAME);
if (str_certificate) {
rval = agora_rtc_license_verify(str_certificate, strlen(str_certificate), NULL, 0);
if (rval < 0) {
LOGE("Failed to verify license, reason: %s", agora_rtc_err_2_str(rval));
return -1;
}
LOGI("Verify license successfully");
}
// Java
String rtcLicense = this.getString(R.string.rtc_license);
int licenseRet = mRtcService.licenseVerify(rtcLicense, null);
if (licenseRet < 0) {
Log.e(TAG, "<main> fail to licenseVerify(), licenseRet=" + licenseRet);
popupMessage("Fail to licenseVerify");
return;
}
调用 agora_rtc_init
方法(Java API 为 init
)初始化 RTSA 服务。
在该方法中,你需要:
agora_rtc_event_handler_t
,用以通知 SDK 在运行过程中发生的事件。NULL
表示使用默认目录)。rtc_service_option_t
结构体设置 RTSA 服务选项。 // C
// app_config_t 结构体,用于定义全局变量
typedef struct {
const char *p_sdk_log_dir;
const char *p_appid;
const char *p_token;
const char *p_channel;
const char *uid;
uint32_t area;
video_data_type_e video_data_type;
int send_video_frame_rate;
const char *send_video_file_path;
audio_data_type_e audio_data_type;
audio_codec_type_e audio_codec_type;
const char *send_audio_file_path;
uint32_t pcm_sample_rate;
uint32_t pcm_channel_num;
bool send_video_generic_flag;
bool enable_audio_mixer;
bool receive_data_only;
} app_config_t;
app_config_t config;
// 设置 App ID
int appid_len = strlen(config->p_appid);
void *p_appid = (void *)(appid_len == 0 ? NULL : config->p_appid);
// 设置事件回调 agora_rtc_event_handler_t
agora_rtc_event_handler_t event_handler = { 0 };
// 初始化事件回调
static void app_init_event_handler(agora_rtc_event_handler_t *event_handler, app_config_t *config)
{
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;
if (config->enable_audio_mixer) {
event_handler->on_mixed_audio_data = __on_mixed_audio_data;
} else {
event_handler->on_audio_data = __on_audio_data;
}
}
app_init_event_handler(&event_handler, config);
rtc_service_option_t service_opt = { 0 };
// 设置访问区域
service_opt.area_code = config->area;
// 设置日志文件路径
service_opt.log_cfg.log_path = config->p_sdk_log_dir;
// 调用 agora_rtc_init 方法初始化 RTSA 服务。
rval = agora_rtc_init(p_appid, &event_handler, &service_opt);
if (rval < 0) {
LOGE("Failed to initialize Agora sdk, reason: %s", agora_rtc_err_2_str(rval));
return -1;
}
// Java
// 初始化
RtcServiceOptions options = new RtcServiceOptions();
options.areaCode = AreaCode.AREA_CODE_GLOB;
options.productId = "MyDev01";
options.useStringUid = false;
options.logDisable = true;
options.logDisableDesensitize = true;
int ret = mRtcService.init(APPID, this, options);
if (ret != AgoraRtcService.ErrorCode.ERR_OKAY) {
Log.e(TAG, "<main> fail to init(), ret=" + ret);
popupMessage("Fail to initialize RTC sdk");
mRtcService = null;
return;
}
// 注册音视频事件回调
class MyRtcEvent implements AgoraRtcEvents {
@Override
public void onJoinChannelSuccess(String channel, int elapsed_ms) {
}
@Override
public void onConnectionLost(String channel) {
}
@Override
public void onRejoinChannelSuccess(String channel, int elapsed_ms) {
}
@Override
public void onError(String channel, int code, String msg) {
}
@Override
public void onUserJoined(String channel, int uid, int elapsed_ms) {
}
@Override
public void onUserOffline(String channel, int uid, int reason) {
}
@Override
public void onUserMuteAudio(String channel, int uid, boolean muted) {
}
@Override
public void onUserMuteVideo(String channel, int uid, boolean muted) {
}
@Override
public void onKeyFrameGenReq(String channel, int requested_uid, byte stream_id) {
}
@Override
public void onAudioData(String channel, int uid, char sent_ts, byte codec, byte[] data) {
}
@Override
public void onMixedAudioData(String channel, byte codec, byte[] data) {
}
@Override
public void onVideoData(String channel, int uid, char sent_ts, byte codec, byte stream_id,
boolean is_key_frame, byte[] data) {
}
@Override
public void onTargetBitrateChanged(String channel, int target_bps) {
}
@Override
public void onLocalUserRegistered(String uname, int uid) {
}
@Override
public void onRemoteUserRegistered(String uname, int uid) {
}
@Override
public void onTokenPrivilegeWillExpire(String token) {
}
}
// 注册云信令事件回调
class MyRtmEvent implements AgoraRtmEvents {
private final static String TAG = "DEMO/MyRtmEvent";
@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_join_channel
(Java API 为 joinChannel
) 方法加入 RTC 频道。
在该方法中,你需要:
token
设为空。用户与 RTC 频道的关系如下:
成功加入 RTC 频道后,SDK 会触发 on_join_channel_success
(Java API 为 onJoinChannelSuccess
) 回调。
示例代码如下:
// C
static void __on_join_channel_success(const char *channel, int elapsed)
{
g_app.b_connected_flag = true;
LOGI("Join the channel \"%s\" successfully, elapsed %d ms", channel, elapsed);
}
rval = agora_rtc_join_channel(config->p_channel, config->uid, p_token, token_len,
&channel_options);
// Java
ChannelOptions chnlOption = new ChannelOptions();
chnlOption.autoSubscribeAudio = true;
chnlOption.autoSubscribeVideo = true;
chnlOption.audioCodecOpt.audioCodecType = AudioCodecType.AUDIO_CODEC_TYPE_OPUS;
chnlOption.audioCodecOpt.pcmSampleRate = PCM_SAMPLE_RATE;
chnlOption.audioCodecOpt.pcmChannelNum = PCM_CHNL_NUMBER;
chnlOption.enableEncrypt = true;
ret = mRtcService.joinChannel(CHANNEL_NAME, HOST_USER_ID, null, chnlOption);
成功加入 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
// 接收所有你已加入的频道内的音频数据流。
static void __on_audio_data(const char *channel, const uint32_t uid, uint16_t sent_ts,
audio_data_type_e data_type, const void *data, size_t len)
{
//LOGD("on_audio_data, uid %u, sent_ts %d, data_type %d, len %zu", uid, sent_ts, data_type, len);
write_file(g_app.audio_file_writer, data_type, data, len);
}
// 接收所有你已加入的频道内的视频数据流。
static void __on_video_data(const char *channel, const uint32_t uid, uint16_t sent_ts,
video_data_type_e data_type, uint8_t stream_id, int is_key_frame,
const void *data, size_t len)
{
//LOGD("on_video_data: uid %u, key_frame %d, data_type %u, len %zu", uid, is_key_frame, data_type, len);
write_file(g_app.video_file_writer, data_type, data, len);
}
// 发送音频数据流
audio_frame_info_t info = { 0 };
info.data_type = config->audio_data_type;
int rval = agora_rtc_send_audio_data(config->p_channel, frame.ptr, frame.len, &info);
if (rval < 0) {
LOGE("Failed to send audio data, reason: %s", agora_rtc_err_2_str(rval));
return -1;
}
// 发送视频数据流
video_frame_info_t info;
info.type = frame.u.video.is_key_frame ? VIDEO_FRAME_KEY : VIDEO_FRAME_DELTA;
info.frames_per_sec = config->send_video_frame_rate;
info.data_type = config->video_data_type;
int rval = agora_rtc_send_video_data(config->p_channel, stream_id, frame.ptr, frame.len, &info);
if (rval < 0) {
LOGE("Failed to send video data, reason: %s", agora_rtc_err_2_str(rval));
return -1;
}
// Java
// 接收所有你已加入的频道内的音频数据流。
@Override
public void onAudioData(String channel, int uid, char sent_ts, byte codec, byte[] data) {
}
// 接收所有你已加入的频道内的视频数据流。
@Override
public void onVideoData(String channel, int uid, char sent_ts, byte codec, byte stream_id,
boolean is_key_frame, byte[] data) {
}
// 发送音频数据流
AudioFrameInfo audioFrameInfo = new AudioFrameInfo();
audioFrameInfo.audio_data_type = AudioDataType.AUDIO_DATA_TYPE_PCM;
int ret = mRtcService.sendAudioData(mChannelName, sendBuffer, audioFrameInfo);
if (ret < 0) {
Log.e(TAG, "<run> sendAudioData() failure, ret=" + ret);
}
// 发送视频数据流
int streamId = 0;
VideoFrameInfo videoFrameInfo = new VideoFrameInfo();
videoFrameInfo.video_data_type = VideoDataType.VIDEO_DATA_TYPE_H264;
videoFrameInfo.type = VideoFrameType.VIDEO_FRAME_KEY;
videoFrameInfo.frameRate = VideoFrameRate.VIDEO_FRAME_RATE_FPS_15;
int ret = mRtcService.sendVideoData(mChannelName, streamId, videoBuffer, videoFrameInfo);
调用 agora_rtc_login_rtm
(Java API 为 loginRtm
) 方法登录 RTM 系统。登录成功之后,你可以发送和接收信令。
在该方法中,你需要:
token
设空。// C
rval = agora_rtc_login_rtm(p_config->p_rtm_uid, p_config->p_token, &rtm_handler);
// Java
int rtmRet = mRtcService.loginRtm(HOST_USER_ID, RTM_TOKEN, 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
// 接收你登录的 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) {
LOGD("Receive data[%s] from user[%s] length[%lu]", (char *)data, user_id, data_len);
} else {
LOGD("data_callback %s data[], length[%lu]", 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) {
LOGE("write error");
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
// 向指定 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
agora_rtc_leave_channel(config->p_channel);
// Java
ret = mRtcService.leaveChannel(CHANNEL_NAME);
if (ret != ErrorCode.ERR_OKAY) {
RtsaLiteDemo.e(TAG, "<main> fail to leaveChannel(), ret=" + ret);
}
调用 agora_rtc_logout_rtm
方法(Java API 为 logoutRtm
)离开指定频道,结束在该频道的数据传输。
// C
agora_rtc_logout_rtm();
// 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();