Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,100 +3,100 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import io.agora.api.example.MainActivity;
import io.agora.rtc.Constants.AudioExternalSourcePos;
import io.agora.rtc.RtcEngine;

public class AudioRecordService extends Service
{
public class AudioRecordService extends Service {
private static final String TAG = AudioRecordService.class.getSimpleName();

private RecordThread thread;
private volatile boolean stopped;

@Nullable
private Thread recordThread;
private RecordBinder recordBinder;

/**
* Since v3.5.1
* 根据实际需求,你可以将外部音频帧推送到音频采集后、编码前或本地播放前的位置。
* 你可以多次调用该方法,将一个音频帧推送到多个位置或者将多个音频帧推送到不同位置。
* 例如,在 KTV 场景中,你可以将歌声推送到音频采集后的位置,让歌声经过 SDK 音频模块的处理,
* 从而获取优质的音频体验;将伴奏推送到音频编码前的位置,让伴奏不受 SDK 音频模块的影响。
*
* {@link io.agora.rtc.Constants.AudioExternalSourcePos}
* AUDIO_EXTERNAL_PLAYOUT_SOURCE(0)
* AUDIO_EXTERNAL_RECORD_SOURCE_PRE_PROCESS(1),
* AUDIO_EXTERNAL_RECORD_SOURCE_POST_PROCESS(2);
*
*/
public volatile int currentSourcePos = AudioExternalSourcePos.getValue(AudioExternalSourcePos.AUDIO_EXTERNAL_PLAYOUT_SOURCE);

public RtcEngine engine = null;
@Override
public IBinder onBind(Intent intent)
{
return null;
public void onCreate() {
super.onCreate();
recordThread = new RecordThread();
recordBinder = new RecordBinder();
startForeground();
}

@Nullable
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
startForeground();
startRecording();
return Service.START_STICKY;
public IBinder onBind(Intent intent) {
return recordBinder;
}

private void startForeground()
{
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);

private void startForeground() {
createNotificationChannel();
Notification notification = new NotificationCompat.Builder(this, TAG)
.setContentTitle(TAG)
.setContentIntent(pendingIntent)
.build();

startForeground(1, notification);
}

private void createNotificationChannel()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
TAG, TAG, NotificationManager.IMPORTANCE_DEFAULT
TAG, TAG, NotificationManager.IMPORTANCE_HIGH
);

NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}

private void startRecording()
{
thread = new RecordThread();
thread.start();
}

private void stopRecording()
{
stopped = true;
public void startRecording() {
recordThread.start();
}

@Override
public void onDestroy()
{
stopRecording();
public void onDestroy() {
super.onDestroy();
recordThread.interrupt();
}

public class RecordThread extends Thread
{
private AudioRecord audioRecord;
public static final int DEFAULT_SAMPLE_RATE = 16000;
/**1 corresponds to AudioFormat.CHANNEL_IN_MONO;
* 2 corresponds to AudioFormat.CHANNEL_IN_STEREO*/
public class RecordThread extends Thread {
private final AudioRecord audioRecord;
public static final int DEFAULT_SAMPLE_RATE = 44100;
/**
* 1 corresponds to AudioFormat.CHANNEL_IN_MONO;
* 2 corresponds to AudioFormat.CHANNEL_IN_STEREO
*/
public static final int DEFAULT_CHANNEL_COUNT = 1, DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
private byte[] buffer;

RecordThread()
{
RecordThread() {
int bufferSize = AudioRecord.getMinBufferSize(DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_CONFIG,
AudioFormat.ENCODING_PCM_16BIT);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_COUNT,
Expand All @@ -105,46 +105,24 @@ public class RecordThread extends Thread
}

@Override
public void run()
{
try
{
audioRecord.startRecording();
while (!stopped)
{
int result = audioRecord.read(buffer, 0, buffer.length);
if (result >= 0)
{
/**Pushes the external audio frame to the Agora SDK for encoding.
* @param data External audio data to be pushed.
* @param timeStamp Timestamp of the external audio frame. It is mandatory.
* You can use this parameter for the following purposes:
* 1:Restore the order of the captured audio frame.
* 2:Synchronize audio and video frames in video-related
* scenarios, including scenarios where external video sources are used.
* @return
* 0: Success.
* < 0: Failure.*/
CustomAudioSource.engine.pushExternalAudioFrame(
buffer, System.currentTimeMillis());
}
else
{
logRecordError(result);
}
Log.d(TAG, "byte size is :" + result);
public void run() {
audioRecord.startRecording();
while (!isInterrupted()) {
int result = audioRecord.read(buffer, 0, buffer.length);
if (result > 0 && engine != null) {
engine.pushExternalAudioFrame(buffer, System.currentTimeMillis(), DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_COUNT
, AudioFormat.ENCODING_PCM_16BIT, AudioRecordService.this.currentSourcePos);
} else {
logRecordError(result);
}
release();
Log.d(TAG, "byte size is :" + result);
}
catch (Exception e)
{e.printStackTrace();}
release();
}

private void logRecordError(int error)
{
private void logRecordError(int error) {
String message = "";
switch (error)
{
switch (error) {
case AudioRecord.ERROR:
message = "generic operation failure";
break;
Expand All @@ -161,13 +139,19 @@ private void logRecordError(int error)
Log.e(TAG, message);
}

private void release()
{
if (audioRecord != null)
{
private void release() {
if (audioRecord != null) {
audioRecord.stop();
buffer = null;
audioRecord.release();
}
buffer = null;
}
}

public class RecordBinder extends Binder {

public AudioRecordService getService(){
return AudioRecordService.this;
}
}
}
Loading