11package io .agora .api .example .examples .advanced .customaudio ;
22
33import android .content .Context ;
4- import android .content .Intent ;
5- import android .media .AudioFormat ;
6- import android .media .AudioManager ;
74import android .os .Bundle ;
8- import android .os .Environment ;
95import android .os .Handler ;
106import android .text .TextUtils ;
117import android .util .Log ;
2319import com .yanzhenjie .permission .AndPermission ;
2420import com .yanzhenjie .permission .runtime .Permission ;
2521
26- import java .io .File ;
27- import java .util . Random ;
22+ import java .io .IOException ;
23+ import java .io . InputStream ;
2824
2925import io .agora .api .example .R ;
3026import io .agora .api .example .annotation .Example ;
3329import io .agora .rtc2 .ChannelMediaOptions ;
3430import io .agora .rtc2 .Constants ;
3531import io .agora .rtc2 .IRtcEngineEventHandler ;
36- import io .agora .rtc2 .RtcConnection ;
3732import io .agora .rtc2 .RtcEngine ;
3833import io .agora .rtc2 .RtcEngineConfig ;
3934import io .agora .rtc2 .RtcEngineEx ;
4035
4136import static io .agora .api .example .common .model .Examples .ADVANCED ;
42- import static io .agora .api .example .examples .advanced .customaudio .MicrophoneRecordService .RecordThread .DEFAULT_CHANNEL_COUNT ;
43- import static io .agora .api .example .examples .advanced .customaudio .MicrophoneRecordService .RecordThread .DEFAULT_SAMPLE_RATE ;
4437
4538/**This demo demonstrates how to make a one-to-one voice call*/
4639@ Example (
@@ -60,6 +53,13 @@ public class CustomAudioSource extends BaseFragment implements View.OnClickListe
6053 public static RtcEngineEx engine ;
6154 private Switch mic , pcm ;
6255 private ChannelMediaOptions option = new ChannelMediaOptions ();
56+ private static final String AUDIO_FILE = "output.raw" ;
57+ private static final Integer SAMPLE_RATE = 44100 ;
58+ private static final Integer SAMPLE_NUM_OF_CHANNEL = 1 ;
59+ private static final Integer SAMPLES = 1024 ;
60+ private static final Integer BUFFER_SIZE = SAMPLES * SAMPLE_NUM_OF_CHANNEL * 2 ;
61+
62+ private InputStream inputStream ;
6363
6464 @ Override
6565 public void onCreate (@ Nullable Bundle savedInstanceState )
@@ -76,6 +76,36 @@ private void initMediaOption() {
7676 option .publishCustomAudioTrack = false ;
7777 }
7878
79+ private void openAudioFile (){
80+ try {
81+ inputStream = this .getResources ().getAssets ().open (AUDIO_FILE );
82+ } catch (IOException e ) {
83+ e .printStackTrace ();
84+ }
85+ }
86+
87+ private void closeAudioFile (){
88+ try {
89+ inputStream .close ();
90+ } catch (IOException e ) {
91+ e .printStackTrace ();
92+ }
93+ }
94+
95+ private byte [] readBuffer (){
96+ int byteSize = BUFFER_SIZE ;
97+ byte [] buffer = new byte [byteSize ];
98+ try {
99+ if (inputStream .read (buffer ) < 0 ){
100+ inputStream .reset ();
101+ return readBuffer ();
102+ }
103+ } catch (IOException e ) {
104+ e .printStackTrace ();
105+ }
106+ return buffer ;
107+ }
108+
79109 @ Nullable
80110 @ Override
81111 public View onCreateView (@ NonNull LayoutInflater inflater , @ Nullable ViewGroup container , @ Nullable Bundle savedInstanceState )
@@ -95,15 +125,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
95125 pcm = view .findViewById (R .id .localAudio );
96126 mic .setOnCheckedChangeListener (this );
97127 pcm .setOnCheckedChangeListener (this );
98- checkLocalAudio ();
99- }
100-
101- private void checkLocalAudio () {
102- File dir = getContext ().getExternalFilesDir (Environment .DIRECTORY_MUSIC );
103- File audioFile = new File (dir , PCMRecordService .FILE_NAME );
104- if (!audioFile .exists ()) {
105- showAlert (String .format (getString (R .string .alert_no_local_audio_message ), dir .getAbsolutePath ()));
106- }
107128 }
108129
109130 @ Override
@@ -141,6 +162,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState)
141162 config .mEventHandler = iRtcEngineEventHandler ;
142163 config .mAudioScenario = Constants .AudioScenario .getValue (Constants .AudioScenario .HIGH_DEFINITION );
143164 engine = (RtcEngineEx ) RtcEngine .create (config );
165+ openAudioFile ();
144166 }
145167 catch (Exception e )
146168 {
@@ -153,14 +175,14 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState)
153175 public void onDestroy ()
154176 {
155177 super .onDestroy ();
156- stopPCMRecord ();
157178 /**leaveChannel and Destroy the RtcEngine instance*/
158179 if (engine != null )
159180 {
160181 engine .leaveChannel ();
161182 }
162183 handler .post (RtcEngine ::destroy );
163184 engine = null ;
185+ closeAudioFile ();
164186 }
165187
166188
@@ -170,23 +192,21 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
170192 {
171193 if (b ){
172194 option .publishAudioTrack = true ;
173- engine .updateChannelMediaOptions (option );
174195 }
175196 else {
176197 option .publishAudioTrack = false ;
177- engine .updateChannelMediaOptions (option );
178198 }
199+ engine .updateChannelMediaOptions (option );
179200 }
180201 else if (compoundButton .getId () == R .id .localAudio )
181202 {
182203 if (b ){
183204 option .publishCustomAudioTrack = true ;
184- engine .updateChannelMediaOptions (option );
185205 }
186206 else {
187207 option .publishCustomAudioTrack = false ;
188- engine .updateChannelMediaOptions (option );
189208 }
209+ engine .updateChannelMediaOptions (option );
190210 }
191211 }
192212
@@ -220,7 +240,6 @@ public void onClick(View v)
220240 else
221241 {
222242 joined = false ;
223- stopPCMRecord ();
224243 /**After joining a channel, the user must call the leaveChannel method to end the
225244 * call before joining another channel. This method returns 0 if the user leaves the
226245 * channel and releases all resources related to the call. This method call is
@@ -266,7 +285,7 @@ private void joinChannel(String channelId)
266285 * 0: Success.
267286 * < 0: Failure.
268287 * PS: Ensure that you call this method before the joinChannel method.*/
269- engine .setExternalAudioSource (true , DEFAULT_SAMPLE_RATE , DEFAULT_CHANNEL_COUNT , 1 , true , true );
288+ engine .setExternalAudioSource (true , SAMPLE_RATE , SAMPLE_NUM_OF_CHANNEL , 2 , true , true );
270289 /**Please configure accessToken in the string_config file.
271290 * A temporary token generated in Console. A temporary token is valid for 24 hours. For details, see
272291 * https://docs.agora.io/en/Agora%20Platform/token?platform=All%20Platforms#get-a-temporary-token
@@ -294,20 +313,6 @@ private void joinChannel(String channelId)
294313 join .setEnabled (false );
295314 }
296315
297- private void startPCMRecord ()
298- {
299- Intent intent = new Intent (getContext (), PCMRecordService .class );
300- getActivity ().startService (intent );
301- }
302-
303- private void stopPCMRecord ()
304- {
305- Intent intent = new Intent (getContext (), PCMRecordService .class );
306- getActivity ().stopService (intent );
307- option .publishCustomAudioTrack = false ;
308- engine .updateChannelMediaOptions (option );
309- }
310-
311316 /**IRtcEngineEventHandler is an abstract class providing default implementation.
312317 * The SDK uses this class to report to the app on SDK runtime events.*/
313318 private final IRtcEngineEventHandler iRtcEngineEventHandler = new IRtcEngineEventHandler ()
@@ -342,7 +347,6 @@ public void onJoinChannelSuccess(String channel, int uid, int elapsed)
342347 showLongToast (String .format ("onJoinChannelSuccess channel %s uid %d" , channel , uid ));
343348 myUid = uid ;
344349 joined = true ;
345- startPCMRecord ();
346350 handler .post (new Runnable ()
347351 {
348352 @ Override
@@ -352,9 +356,20 @@ public void run()
352356 pcm .setEnabled (true );
353357 join .setEnabled (true );
354358 join .setText (getString (R .string .leave ));
359+ new Thread (new PushingTask ()).start ();
355360 }
356361 });
357362 }
358363
359364 };
365+
366+ class PushingTask implements Runnable {
367+
368+ @ Override
369+ public void run () {
370+ while (true ){
371+ engine .pushExternalAudioFrame (readBuffer (), BUFFER_SIZE );
372+ }
373+ }
374+ }
360375}
0 commit comments