Skip to content

Commit f234357

Browse files
committed
[FEAT] NMS-811 is done.
1 parent a859834 commit f234357

5 files changed

Lines changed: 324 additions & 209 deletions

File tree

Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/customaudio/AudioRecordService.java

Lines changed: 72 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -3,100 +3,100 @@
33
import android.app.Notification;
44
import android.app.NotificationChannel;
55
import android.app.NotificationManager;
6-
import android.app.PendingIntent;
76
import android.app.Service;
87
import android.content.Intent;
98
import android.media.AudioFormat;
109
import android.media.AudioRecord;
1110
import android.media.MediaRecorder;
11+
import android.os.Binder;
1212
import android.os.Build;
1313
import android.os.IBinder;
1414
import android.util.Log;
1515

1616
import androidx.annotation.Nullable;
1717
import 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

Comments
 (0)