Skip to content

Commit

Permalink
[Android]fix custom audio render api call problem(DEVEX-65).
Browse files Browse the repository at this point in the history
  • Loading branch information
xgfd3 committed Dec 7, 2023
1 parent cb693eb commit f4672e9
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import android.media.AudioFormat;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.util.Log;
import android.view.LayoutInflater;
Expand Down Expand Up @@ -41,20 +40,16 @@
/**
* This demo demonstrates how to make a one-to-one voice call
*/
@Example(
index = 6,
group = ADVANCED,
name = R.string.item_customaudiorender,
actionId = R.id.action_mainFragment_to_CustomAudioRender,
tipsId = R.string.customaudiorender
)
@Example(index = 6, group = ADVANCED, name = R.string.item_customaudiorender, actionId = R.id.action_mainFragment_to_CustomAudioRender, tipsId = R.string.customaudiorender)
public class CustomAudioRender extends BaseFragment implements View.OnClickListener {
private static final String TAG = CustomAudioRender.class.getSimpleName();
private EditText et_channel;
private Button join;
private boolean joined = false;
/**
* The constant engine.
*/
public static RtcEngineEx engine;
private ChannelMediaOptions option = new ChannelMediaOptions();

private static final Integer SAMPLE_RATE = 44100;
private static final Integer SAMPLE_NUM_OF_CHANNEL = 2;
Expand All @@ -69,22 +64,6 @@ public class CustomAudioRender extends BaseFragment implements View.OnClickListe

private AudioSeatManager audioSeatManager;

@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
Expand All @@ -109,8 +88,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
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)
);
view.findViewById(R.id.audio_place_09));
}

@Override
Expand All @@ -123,30 +101,30 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
}
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.
/* 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();
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.
*/
Expand All @@ -165,9 +143,9 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
engine.setLocalAccessPoint(localAccessPointConfiguration);
}

engine.setExternalAudioSource(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL);

audioPlayer = new AudioPlayer(AudioManager.STREAM_MUSIC, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL,
audioPlayer = new AudioPlayer(AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
SAMPLE_NUM_OF_CHANNEL,
AudioFormat.ENCODING_PCM_16BIT);
} catch (Exception e) {
e.printStackTrace();
Expand All @@ -179,7 +157,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
public void onDestroy() {
super.onDestroy();
pulling = false;
if(pullingTask != null){
if (pullingTask != null) {
try {
pullingTask.join();
pullingTask = null;
Expand All @@ -188,7 +166,7 @@ public void onDestroy() {
}
}
audioPlayer.stopPlayer();
/**leaveChannel and Destroy the RtcEngine instance*/
/*leaveChannel and Destroy the RtcEngine instance*/
if (engine != null) {
engine.leaveChannel();
}
Expand All @@ -197,7 +175,6 @@ public void onDestroy() {
}



@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_join) {
Expand All @@ -211,17 +188,13 @@ public void onClick(View v) {
return;
}
// Request permission
AndPermission.with(this).runtime().permission(
Permission.Group.STORAGE,
Permission.Group.MICROPHONE
).onGranted(permissions ->
{
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
/*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.
Expand All @@ -242,7 +215,7 @@ public void onClick(View v) {
pulling = false;
join.setText(getString(R.string.join));
audioSeatManager.downAllSeats();
if(pullingTask != null){
if (pullingTask != null) {
try {
pullingTask.join();
pullingTask = null;
Expand All @@ -259,35 +232,25 @@ public void onClick(View v) {
* 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.*/
// engine.setExternalAudioSource(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL, 2, false, true);



/**Please configure accessToken in the string_config file.

engine.setExternalAudioSink(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL);

/*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.
ChannelMediaOptions option = new ChannelMediaOptions();
option.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;


/* 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:
Expand Down Expand Up @@ -337,7 +300,7 @@ public void run() {
join.setText(getString(R.string.leave));
pulling = true;
audioPlayer.startPlayer();
if(pullingTask == null){
if (pullingTask == null) {
pullingTask = new Thread(new PullingTask());
pullingTask.start();
}
Expand All @@ -358,31 +321,32 @@ public void onUserOffline(int uid, int reason) {
}
};

/**
* The type Pulling task.
*/
class PullingTask implements Runnable {
/**
* The Number.
*/
long number = 0;

@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
while (pulling) {
Log.i(TAG, "pushExternalAudioFrame times:" + number++);
long before = System.currentTimeMillis();

ByteBuffer frame = ByteBuffer.allocateDirect(BUFFER_SIZE);
engine.pullPlaybackAudioFrame(frame, BUFFER_SIZE);
byte[] data = new byte[frame.remaining()];
frame.get(data, 0, data.length);
audioPlayer.play(data, 0, BUFFER_SIZE);

long now = System.currentTimeMillis();
long consuming = now - before;
if(consuming < PULL_INTERVAL){
try {
Thread.sleep(PULL_INTERVAL - consuming);
} catch (InterruptedException e) {
Log.e(TAG, "PushingTask Interrupted");
}
// simple audio filter
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] + 5);
}

audioPlayer.play(data, 0, BUFFER_SIZE);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import android.media.AudioFormat;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.util.Log;
import android.view.LayoutInflater;
Expand Down Expand Up @@ -51,7 +50,6 @@ public class CustomAudioRender extends BaseFragment implements View.OnClickListe
* The constant engine.
*/
public static RtcEngineEx engine;
private ChannelMediaOptions option = new ChannelMediaOptions();

private static final Integer SAMPLE_RATE = 44100;
private static final Integer SAMPLE_NUM_OF_CHANNEL = 2;
Expand All @@ -66,22 +64,6 @@ public class CustomAudioRender extends BaseFragment implements View.OnClickListe

private AudioSeatManager audioSeatManager;

@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
Expand Down Expand Up @@ -161,8 +143,6 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
engine.setLocalAccessPoint(localAccessPointConfiguration);
}

engine.setExternalAudioSource(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL);

audioPlayer = new AudioPlayer(AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
SAMPLE_NUM_OF_CHANNEL,
Expand Down Expand Up @@ -252,23 +232,8 @@ public void onClick(View v) {
* 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.*/
// engine.setExternalAudioSource(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL, 2, false, true);

engine.setExternalAudioSink(true, SAMPLE_RATE, SAMPLE_NUM_OF_CHANNEL);

/*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
Expand All @@ -277,9 +242,15 @@ private void joinChannel(String channelId) {
* https://docs.agora.io/en/cloud-recording/token_server_java?platform=Java*/
TokenUtils.gen(requireContext(), channelId, 0, ret -> {

ChannelMediaOptions option = new ChannelMediaOptions();
option.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;


/* 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:
Expand Down Expand Up @@ -364,23 +335,18 @@ public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
while (pulling) {
Log.i(TAG, "pushExternalAudioFrame times:" + number++);
long before = System.currentTimeMillis();

ByteBuffer frame = ByteBuffer.allocateDirect(BUFFER_SIZE);
engine.pullPlaybackAudioFrame(frame, BUFFER_SIZE);
byte[] data = new byte[frame.remaining()];
frame.get(data, 0, data.length);
audioPlayer.play(data, 0, BUFFER_SIZE);

long now = System.currentTimeMillis();
long consuming = now - before;
if (consuming < PULL_INTERVAL) {
try {
Thread.sleep(PULL_INTERVAL - consuming);
} catch (InterruptedException e) {
Log.e(TAG, "PushingTask Interrupted");
}
// simple audio filter
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] + 5);
}

audioPlayer.play(data, 0, BUFFER_SIZE);
}
}
}
Expand Down

0 comments on commit f4672e9

Please sign in to comment.