33import android .app .Notification ;
44import android .app .NotificationChannel ;
55import android .app .NotificationManager ;
6- import android .app .PendingIntent ;
76import android .app .Service ;
87import android .content .Intent ;
98import android .media .AudioFormat ;
109import android .media .AudioRecord ;
1110import android .media .MediaRecorder ;
11+ import android .os .Binder ;
1212import android .os .Build ;
1313import android .os .IBinder ;
1414import android .util .Log ;
1515
1616import androidx .annotation .Nullable ;
1717import androidx .core .app .NotificationCompat ;
1818
19- import io .agora .api .example .MainActivity ;
19+ import io .agora .rtc .Constants .AudioExternalSourcePos ;
20+ import io .agora .rtc .RtcEngine ;
2021
21- public class AudioRecordService extends Service
22- {
22+ public class AudioRecordService extends Service {
2323 private static final String TAG = AudioRecordService .class .getSimpleName ();
2424
25- private RecordThread thread ;
26- private volatile boolean stopped ;
27-
28- @ Nullable
25+ private Thread recordThread ;
26+ private RecordBinder recordBinder ;
27+
28+ /**
29+ * Since v3.5.1
30+ * 根据实际需求,你可以将外部音频帧推送到音频采集后、编码前或本地播放前的位置。
31+ * 你可以多次调用该方法,将一个音频帧推送到多个位置或者将多个音频帧推送到不同位置。
32+ * 例如,在 KTV 场景中,你可以将歌声推送到音频采集后的位置,让歌声经过 SDK 音频模块的处理,
33+ * 从而获取优质的音频体验;将伴奏推送到音频编码前的位置,让伴奏不受 SDK 音频模块的影响。
34+ *
35+ * {@link io.agora.rtc.Constants.AudioExternalSourcePos}
36+ * AUDIO_EXTERNAL_PLAYOUT_SOURCE(0)
37+ * AUDIO_EXTERNAL_RECORD_SOURCE_PRE_PROCESS(1),
38+ * AUDIO_EXTERNAL_RECORD_SOURCE_POST_PROCESS(2);
39+ *
40+ */
41+ public volatile int currentSourcePos = AudioExternalSourcePos .getValue (AudioExternalSourcePos .AUDIO_EXTERNAL_PLAYOUT_SOURCE );
42+
43+ public RtcEngine engine = null ;
2944 @ Override
30- public IBinder onBind (Intent intent )
31- {
32- return null ;
45+ public void onCreate () {
46+ super .onCreate ();
47+ recordThread = new RecordThread ();
48+ recordBinder = new RecordBinder ();
49+ startForeground ();
3350 }
3451
52+ @ Nullable
3553 @ Override
36- public int onStartCommand (Intent intent , int flags , int startId )
37- {
38- startForeground ();
39- startRecording ();
40- return Service .START_STICKY ;
54+ public IBinder onBind (Intent intent ) {
55+ return recordBinder ;
4156 }
4257
43- private void startForeground ()
44- {
45- createNotificationChannel ();
46- Intent notificationIntent = new Intent (this , MainActivity .class );
47- PendingIntent pendingIntent = PendingIntent .getActivity (this ,
48- 0 , notificationIntent , 0 );
4958
59+ private void startForeground () {
60+ createNotificationChannel ();
5061 Notification notification = new NotificationCompat .Builder (this , TAG )
5162 .setContentTitle (TAG )
52- .setContentIntent (pendingIntent )
5363 .build ();
5464
5565 startForeground (1 , notification );
5666 }
5767
58- private void createNotificationChannel ()
59- {
60- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O )
61- {
68+ private void createNotificationChannel () {
69+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
6270 NotificationChannel serviceChannel = new NotificationChannel (
63- TAG , TAG , NotificationManager .IMPORTANCE_DEFAULT
71+ TAG , TAG , NotificationManager .IMPORTANCE_HIGH
6472 );
6573
6674 NotificationManager manager = getSystemService (NotificationManager .class );
6775 manager .createNotificationChannel (serviceChannel );
6876 }
6977 }
7078
71- private void startRecording ()
72- {
73- thread = new RecordThread ();
74- thread .start ();
75- }
76-
77- private void stopRecording ()
78- {
79- stopped = true ;
79+ public void startRecording () {
80+ recordThread .start ();
8081 }
8182
8283 @ Override
83- public void onDestroy ()
84- {
85- stopRecording ();
84+ public void onDestroy () {
8685 super .onDestroy ();
86+ recordThread .interrupt ();
8787 }
8888
89- public class RecordThread extends Thread
90- {
91- private AudioRecord audioRecord ;
92- public static final int DEFAULT_SAMPLE_RATE = 16000 ;
93- /**1 corresponds to AudioFormat.CHANNEL_IN_MONO;
94- * 2 corresponds to AudioFormat.CHANNEL_IN_STEREO*/
89+ public class RecordThread extends Thread {
90+ private final AudioRecord audioRecord ;
91+ public static final int DEFAULT_SAMPLE_RATE = 44100 ;
92+ /**
93+ * 1 corresponds to AudioFormat.CHANNEL_IN_MONO;
94+ * 2 corresponds to AudioFormat.CHANNEL_IN_STEREO
95+ */
9596 public static final int DEFAULT_CHANNEL_COUNT = 1 , DEFAULT_CHANNEL_CONFIG = AudioFormat .CHANNEL_IN_MONO ;
9697 private byte [] buffer ;
9798
98- RecordThread ()
99- {
99+ RecordThread () {
100100 int bufferSize = AudioRecord .getMinBufferSize (DEFAULT_SAMPLE_RATE , DEFAULT_CHANNEL_CONFIG ,
101101 AudioFormat .ENCODING_PCM_16BIT );
102102 audioRecord = new AudioRecord (MediaRecorder .AudioSource .MIC , DEFAULT_SAMPLE_RATE , DEFAULT_CHANNEL_COUNT ,
@@ -105,46 +105,24 @@ public class RecordThread extends Thread
105105 }
106106
107107 @ Override
108- public void run ()
109- {
110- try
111- {
112- audioRecord .startRecording ();
113- while (!stopped )
114- {
115- int result = audioRecord .read (buffer , 0 , buffer .length );
116- if (result >= 0 )
117- {
118- /**Pushes the external audio frame to the Agora SDK for encoding.
119- * @param data External audio data to be pushed.
120- * @param timeStamp Timestamp of the external audio frame. It is mandatory.
121- * You can use this parameter for the following purposes:
122- * 1:Restore the order of the captured audio frame.
123- * 2:Synchronize audio and video frames in video-related
124- * scenarios, including scenarios where external video sources are used.
125- * @return
126- * 0: Success.
127- * < 0: Failure.*/
128- CustomAudioSource .engine .pushExternalAudioFrame (
129- buffer , System .currentTimeMillis ());
130- }
131- else
132- {
133- logRecordError (result );
134- }
135- Log .d (TAG , "byte size is :" + result );
108+ public void run () {
109+ audioRecord .startRecording ();
110+ while (!isInterrupted ()) {
111+ int result = audioRecord .read (buffer , 0 , buffer .length );
112+ if (result > 0 && engine != null ) {
113+ engine .pushExternalAudioFrame (buffer , System .currentTimeMillis (), DEFAULT_SAMPLE_RATE , DEFAULT_CHANNEL_COUNT
114+ , AudioFormat .ENCODING_PCM_16BIT , AudioRecordService .this .currentSourcePos );
115+ } else {
116+ logRecordError (result );
136117 }
137- release ( );
118+ Log . d ( TAG , "byte size is :" + result );
138119 }
139- catch (Exception e )
140- {e .printStackTrace ();}
120+ release ();
141121 }
142122
143- private void logRecordError (int error )
144- {
123+ private void logRecordError (int error ) {
145124 String message = "" ;
146- switch (error )
147- {
125+ switch (error ) {
148126 case AudioRecord .ERROR :
149127 message = "generic operation failure" ;
150128 break ;
@@ -161,13 +139,19 @@ private void logRecordError(int error)
161139 Log .e (TAG , message );
162140 }
163141
164- private void release ()
165- {
166- if (audioRecord != null )
167- {
142+ private void release () {
143+ if (audioRecord != null ) {
168144 audioRecord .stop ();
169- buffer = null ;
145+ audioRecord . release () ;
170146 }
147+ buffer = null ;
148+ }
149+ }
150+
151+ public class RecordBinder extends Binder {
152+
153+ public AudioRecordService getService (){
154+ return AudioRecordService .this ;
171155 }
172156 }
173157}
0 commit comments