From f4672e965725147013c81fa41e663bf11724dd5a Mon Sep 17 00:00:00 2001 From: xucz Date: Thu, 7 Dec 2023 10:42:16 +0800 Subject: [PATCH] [Android]fix custom audio render api call problem(DEVEX-65). --- .../customaudio/CustomAudioRender.java | 120 ++++++------------ .../customaudio/CustomAudioRender.java | 58 ++------- 2 files changed, 54 insertions(+), 124 deletions(-) diff --git a/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java b/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java index f88ea322d..479854f9a 100755 --- a/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java +++ b/Android/APIExample-Audio/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java @@ -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; @@ -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; @@ -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 @@ -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 @@ -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 How to get the App ID */ 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. */ @@ -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(); @@ -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; @@ -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(); } @@ -197,7 +175,6 @@ public void onDestroy() { } - @Override public void onClick(View v) { if (v.getId() == R.id.btn_join) { @@ -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. @@ -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; @@ -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: @@ -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(); } @@ -358,7 +321,13 @@ public void onUserOffline(int uid, int reason) { } }; + /** + * The type Pulling task. + */ class PullingTask implements Runnable { + /** + * The Number. + */ long number = 0; @Override @@ -366,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); } } } diff --git a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java index 7c6cce762..479854f9a 100755 --- a/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java +++ b/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/CustomAudioRender.java @@ -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; @@ -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; @@ -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 @@ -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, @@ -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 @@ -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: @@ -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); } } }