1 Star 0 Fork 4

agoraio-community / API-Examples

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
CustomAudioSource.java 16.85 KB
一键复制 编辑 原始数据 按行查看 历史
xucz 提交于 2024-02-23 11:45 . Dev/4.3.0 (#365)
package io.agora.api.example.examples.advanced.customaudio;
import static io.agora.api.example.common.model.Examples.ADVANCED;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.runtime.Permission;
import io.agora.api.example.MainApplication;
import io.agora.api.example.R;
import io.agora.api.example.annotation.Example;
import io.agora.api.example.common.BaseFragment;
import io.agora.api.example.common.widget.AudioSeatManager;
import io.agora.api.example.utils.AudioFileReader;
import io.agora.api.example.utils.CommonUtil;
import io.agora.api.example.utils.TokenUtils;
import io.agora.rtc2.ChannelMediaOptions;
import io.agora.rtc2.Constants;
import io.agora.rtc2.IRtcEngineEventHandler;
import io.agora.rtc2.RtcEngine;
import io.agora.rtc2.RtcEngineConfig;
import io.agora.rtc2.RtcEngineEx;
import io.agora.rtc2.audio.AudioTrackConfig;
import io.agora.rtc2.proxy.LocalAccessPointConfiguration;
/**
* This demo demonstrates how to make a one-to-one voice call
*/
@Example(index = 5, group = ADVANCED, name = R.string.item_customaudiosource, actionId = R.id.action_mainFragment_to_CustomAudioSource, tipsId = R.string.customaudio)
public class CustomAudioSource extends BaseFragment implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private static final String TAG = CustomAudioSource.class.getSimpleName();
private EditText et_channel;
private Button join;
private int myUid;
private boolean joined = false;
/**
* The constant engine.
*/
public static RtcEngineEx engine;
private Switch mic, pcm;
private ChannelMediaOptions option = new ChannelMediaOptions();
private int pushTimes = 0;
private AudioSeatManager audioSeatManager;
private AudioFileReader audioPushingHelper;
private int customAudioTrack = -1;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
initMediaOption();
}
private void initMediaOption() {
option.autoSubscribeAudio = true;
option.autoSubscribeVideo = true;
option.publishMicrophoneTrack = true;
option.publishCustomAudioTrack = false;
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
option.enableAudioRecordingOrPlayout = true;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_custom_audio_source, container, false);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
join = view.findViewById(R.id.btn_join);
et_channel = view.findViewById(R.id.et_channel);
view.findViewById(R.id.btn_join).setOnClickListener(this);
mic = view.findViewById(R.id.microphone);
pcm = view.findViewById(R.id.localAudio);
mic.setOnCheckedChangeListener(this);
pcm.setOnCheckedChangeListener(this);
audioSeatManager = new AudioSeatManager(
view.findViewById(R.id.audio_place_01),
view.findViewById(R.id.audio_place_02),
view.findViewById(R.id.audio_place_03),
view.findViewById(R.id.audio_place_04),
view.findViewById(R.id.audio_place_05),
view.findViewById(R.id.audio_place_06),
view.findViewById(R.id.audio_place_07),
view.findViewById(R.id.audio_place_08),
view.findViewById(R.id.audio_place_09)
);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Check if the context is valid
Context context = getContext();
if (context == null) {
return;
}
try {
RtcEngineConfig config = new RtcEngineConfig();
/*
* The context of Android Activity
*/
config.mContext = context.getApplicationContext();
/*
* The App ID issued to you by Agora. See <a href="https://docs.agora.io/en/Agora%20Platform/token#get-an-app-id"> How to get the App ID</a>
*/
config.mAppId = getString(R.string.agora_app_id);
/* Sets the channel profile of the Agora RtcEngine.
CHANNEL_PROFILE_COMMUNICATION(0): (Default) The Communication profile.
Use this profile in one-on-one calls or group calls, where all users can talk freely.
CHANNEL_PROFILE_LIVE_BROADCASTING(1): The Live-Broadcast profile. Users in a live-broadcast
channel have a role as either broadcaster or audience. A broadcaster can both send and receive streams;
an audience can only receive streams.*/
config.mChannelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
/*
* IRtcEngineEventHandler is an abstract class providing default implementation.
* The SDK uses this class to report to the app on SDK runtime events.
*/
config.mEventHandler = iRtcEngineEventHandler;
config.mAudioScenario = Constants.AudioScenario.getValue(Constants.AudioScenario.DEFAULT);
config.mAreaCode = ((MainApplication) getActivity().getApplication()).getGlobalSettings().getAreaCode();
engine = (RtcEngineEx) RtcEngine.create(config);
/*
* This parameter is for reporting the usages of APIExample to agora background.
* Generally, it is not necessary for you to set this parameter.
*/
engine.setParameters("{" + "\"rtc.report_app_scenario\":"
+ "{" + "\"appScenario\":"
+ 100 + "," + "\"serviceType\":" + 11 + ","
+ "\"appVersion\":\"" + RtcEngine.getSdkVersion() + "\"" + "}" + "}");
/* setting the local access point if the private cloud ip was set, otherwise the config will be invalid.*/
LocalAccessPointConfiguration localAccessPointConfiguration = ((MainApplication) getActivity().getApplication()).getGlobalSettings().getPrivateCloudConfig();
if (localAccessPointConfiguration != null) {
// This api can only be used in the private media server scenario, otherwise some problems may occur.
engine.setLocalAccessPoint(localAccessPointConfiguration);
}
audioPushingHelper = new AudioFileReader(requireContext(), (buffer, timestamp) -> {
if (joined && engine != null && customAudioTrack != -1) {
int ret = engine.pushExternalAudioFrame(buffer, timestamp,
AudioFileReader.SAMPLE_RATE,
AudioFileReader.SAMPLE_NUM_OF_CHANNEL,
Constants.BytesPerSample.TWO_BYTES_PER_SAMPLE,
customAudioTrack);
Log.i(TAG, "pushExternalAudioFrame times:" + (++pushTimes) + ", ret=" + ret);
}
});
} catch (Exception e) {
e.printStackTrace();
getActivity().onBackPressed();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (customAudioTrack != -1) {
engine.destroyCustomAudioTrack(customAudioTrack);
customAudioTrack = -1;
}
if (audioPushingHelper != null) {
audioPushingHelper.stop();
}
/*leaveChannel and Destroy the RtcEngine instance*/
if (engine != null) {
engine.leaveChannel();
}
handler.post(RtcEngine::destroy);
engine = null;
}
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (compoundButton.getId() == R.id.microphone) {
option.publishMicrophoneTrack = checked;
engine.updateChannelMediaOptions(option);
} else if (compoundButton.getId() == R.id.localAudio) {
option.publishCustomAudioTrackId = customAudioTrack;
option.publishCustomAudioTrack = checked;
engine.updateChannelMediaOptions(option);
engine.enableCustomAudioLocalPlayback(customAudioTrack, checked);
}
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_join) {
if (!joined) {
CommonUtil.hideInputBoard(getActivity(), et_channel);
// call when join button hit
String channelId = et_channel.getText().toString();
// Check permission
if (AndPermission.hasPermissions(this, Permission.Group.STORAGE, Permission.Group.MICROPHONE, Permission.Group.CAMERA)) {
joinChannel(channelId);
return;
}
// Request permission
AndPermission.with(this).runtime().permission(Permission.Group.STORAGE, Permission.Group.MICROPHONE).onGranted(permissions -> {
// Permissions Granted
joinChannel(channelId);
}).start();
} else {
joined = false;
/*After joining a channel, the user must call the leaveChannel method to end the
* call before joining another channel. This method returns 0 if the user leaves the
* channel and releases all resources related to the call. This method call is
* asynchronous, and the user has not exited the channel when the method call returns.
* Once the user leaves the channel, the SDK triggers the onLeaveChannel callback.
* A successful leaveChannel method call triggers the following callbacks:
* 1:The local client: onLeaveChannel.
* 2:The remote client: onUserOffline, if the user leaving the channel is in the
* Communication channel, or is a BROADCASTER in the Live Broadcast profile.
* @returns 0: Success.
* < 0: Failure.
* PS:
* 1:If you call the destroy method immediately after calling the leaveChannel
* method, the leaveChannel process interrupts, and the SDK does not trigger
* the onLeaveChannel callback.
* 2:If you call the leaveChannel method during CDN live streaming, the SDK
* triggers the removeInjectStreamUrl method.*/
engine.leaveChannel();
join.setText(getString(R.string.join));
mic.setEnabled(false);
pcm.setEnabled(false);
pcm.setChecked(false);
mic.setChecked(true);
if (audioPushingHelper != null) {
audioPushingHelper.stop();
}
audioSeatManager.downAllSeats();
}
}
}
/**
* @param channelId Specify the channel name that you want to join.
* Users that input the same channel name join the same channel.
*/
private void joinChannel(String channelId) {
/*In the demo, the default is to enter as the anchor.*/
engine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);
/*Sets the external audio source.
* @param enabled Sets whether to enable/disable the external audio source:
* true: Enable the external audio source.
* false: (Default) Disable the external audio source.
* @param sampleRate Sets the sample rate (Hz) of the external audio source, which can be
* set as 8000, 16000, 32000, 44100, or 48000 Hz.
* @param channels Sets the number of channels of the external audio source:
* 1: Mono.
* 2: Stereo.
* @return
* 0: Success.
* < 0: Failure.
* PS: Ensure that you call this method before the joinChannel method.*/
AudioTrackConfig config = new AudioTrackConfig();
config.enableLocalPlayback = false;
customAudioTrack = engine.createCustomAudioTrack(Constants.AudioTrackType.AUDIO_TRACK_MIXABLE, config);
/*Please configure accessToken in the string_config file.
* A temporary token generated in Console. A temporary token is valid for 24 hours. For details, see
* https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token
* A token generated at the server. This applies to scenarios with high-security requirements. For details, see
* https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/
TokenUtils.gen(requireContext(), channelId, 0, ret -> {
/* Allows a user to join a channel.
if you do not specify the uid, we will generate the uid for you*/
int res = engine.joinChannel(ret, channelId, 0, option);
if (res != 0) {
// Usually happens with invalid parameters
// Error code description can be found at:
// en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
// cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
showAlert(RtcEngine.getErrorDescription(Math.abs(res)));
return;
}
// Prevent repeated entry
join.setEnabled(false);
});
}
/**
* IRtcEngineEventHandler is an abstract class providing default implementation.
* The SDK uses this class to report to the app on SDK runtime events.
*/
private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler() {
/**
* Error code description can be found at:
* en: https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onerror
* cn: https://docs.agora.io/cn/video-call-4.x/API%20Reference/java_ng/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onerror
*/
@Override
public void onError(int err) {
Log.e(TAG, String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err)));
showAlert(String.format("onError code %d message %s", err, RtcEngine.getErrorDescription(err)));
}
/**Occurs when the local user joins a specified channel.
* The channel name assignment is based on channelName specified in the joinChannel method.
* If the uid is not specified when joinChannel is called, the server automatically assigns a uid.
* @param channel Channel name
* @param uid User ID
* @param elapsed Time elapsed (ms) from the user calling joinChannel until this callback is triggered*/
@Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel, uid));
myUid = uid;
joined = true;
handler.post(new Runnable() {
@Override
public void run() {
mic.setEnabled(true);
pcm.setEnabled(true);
join.setEnabled(true);
join.setText(getString(R.string.leave));
if (audioPushingHelper != null) {
pushTimes = 0;
audioPushingHelper.start();
}
audioSeatManager.upLocalSeat(uid);
if (pcm.isChecked()) {
engine.enableCustomAudioLocalPlayback(0, true);
}
}
});
}
@Override
public void onUserJoined(int uid, int elapsed) {
super.onUserJoined(uid, elapsed);
runOnUIThread(() -> audioSeatManager.upRemoteSeat(uid));
}
@Override
public void onUserOffline(int uid, int reason) {
super.onUserOffline(uid, reason);
runOnUIThread(() -> audioSeatManager.downSeat(uid));
}
};
}
1
https://gitee.com/agoraio-community/API-Examples.git
git@gitee.com:agoraio-community/API-Examples.git
agoraio-community
API-Examples
API-Examples
main

搜索帮助