1 package com.netease.nimlib.sdk.media.record;
3 import android.content.Context;
4 import android.media.AudioManager;
5 import android.os.Handler;
6 import android.os.HandlerThread;
7 import android.os.Looper;
8 import android.os.Message;
9 import android.text.TextUtils;
22 import java.util.concurrent.atomic.AtomicBoolean;
31 private static final int MSG_START_RECORD = 1;
32 private static final int MSG_STOP_RECORD = 2;
33 private static final int MSG_END_RECORD = 3;
35 private static final int RECORD_FAILED = 1;
36 private static final int RECORD_READY = 2;
37 private static final int RECORD_START = 3;
38 private static final int RECORD_SUCCESS = 4;
39 private static final int RECORD_CANCELED = 5;
40 private static final String TAG =
"AudioRecordManager";
42 private AudioRecord mAudioRecorder;
43 private AudioManager audioManager;
45 private Context context;
46 private int networkClass = NetworkUtil.NETWORK_CLASS_UNKNOWN;
48 private File audioFile;
50 private int maxDuration;
51 private AtomicBoolean
isRecording =
new AtomicBoolean(
false);
52 private AtomicBoolean cancelRecord =
new AtomicBoolean(
false);
55 private RecordHandler mHandler;
56 private Handler mEventHandler =
new Handler(Looper.getMainLooper());
57 private HandlerThread handlerThread;
59 private class RecordHandler
extends Handler {
61 public RecordHandler(Looper looper) {
66 public void handleMessage(Message msg) {
68 case MSG_START_RECORD:
72 boolean cancel = (Boolean) msg.obj;
73 onCompleteRecord(cancel);
76 boolean success = (Boolean) msg.obj;
77 int duration = msg.arg1;
78 onHandleEndRecord(success, duration);
98 this.context = context.getApplicationContext();
99 this.recordType = recordType;
100 if (maxDuration <= 0) {
103 this.maxDuration = maxDuration;
107 audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
108 handlerThread =
new HandlerThread(
"audio_recorder");
109 handlerThread.start();
110 mHandler =
new RecordHandler(handlerThread.getLooper());
120 mHandler.removeMessages(MSG_START_RECORD);
121 mHandler.obtainMessage(MSG_START_RECORD).sendToTarget();
124 private void onStartRecord() {
125 audioManager.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
127 if (isRecording.get()) {
128 NimLog.audio(TAG +
" startRecord false, as current state is isRecording");
129 callBackRecordState(RECORD_FAILED);
133 if (!NimStorageUtil.hasEnoughSpaceForWrite(NimStorageType.TYPE_AUDIO)) {
134 NimLog.audio(TAG +
" startRecord false, as has no enough space to write");
135 callBackRecordState(RECORD_FAILED);
141 String outputFilePath = NimStorageUtil.getWritePath(SDKCache.getContext(), StringUtil.get36UUID() + outputFormat,
142 NimStorageType.TYPE_AUDIO);
143 if (TextUtils.isEmpty(outputFilePath)) {
144 NimLog.audio(TAG +
" startRecord false, as outputFilePath is empty");
145 callBackRecordState(RECORD_FAILED);
148 outputFilePath = outputFilePath + recordType.
getFileSuffix();
149 audioFile =
new File(outputFilePath);
152 cancelRecord.set(
false);
155 mAudioRecorder =
new AudioRecord(context, outputFilePath, maxDuration * 1000);
156 mAudioRecorder.setAudioCodec(outputFormat);
157 networkClass = NetworkUtil.getNetworkClass(context);
158 if (networkClass == NetworkUtil.NETWORK_CLASS_3_G)
159 mAudioRecorder.setMaxSampleRateInHz(22050);
160 else if (networkClass == NetworkUtil.NETWORK_CLASS_2_G)
161 mAudioRecorder.setMaxSampleRateInHz(16000);
162 mAudioRecorder.setOnInfoListener(infoListener);
164 if (!cancelRecord.get()) {
165 callBackRecordState(RECORD_READY);
166 if (mAudioRecorder.startRecording()) {
167 isRecording.set(
true);
168 callBackRecordState(RECORD_START);
171 }
catch (Exception e) {
173 onCompleteRecord(
false);
175 if (!isRecording.get()) {
176 callBackRecordState(RECORD_FAILED);
187 Message message = mHandler.obtainMessage(MSG_STOP_RECORD);
188 message.obj = cancel;
189 message.sendToTarget();
192 private void onCompleteRecord(
boolean cancel) {
193 if (!isRecording.get()) {
196 cancelRecord.set(cancel);
197 audioManager.abandonAudioFocus(null);
199 if (mAudioRecorder != null) {
200 mAudioRecorder.stopRecording();
203 mAudioRecorder = null;
205 }
catch (Exception e) {
214 if (mHandler != null) {
215 mHandler.removeCallbacksAndMessages(null);
217 if (handlerThread != null && handlerThread.isAlive()) {
218 Looper looper = handlerThread.getLooper();
227 return isRecording.get();
231 Message message = mHandler.obtainMessage(MSG_END_RECORD);
232 message.obj = isSuccess;
233 message.arg1 = duration;
234 message.sendToTarget();
237 private void onHandleEndRecord(
boolean isSuccess,
final int duration) {
238 if (cancelRecord.get()) {
240 AttachmentStore.deleteOnExit(audioFile.getAbsolutePath());
242 callBackRecordState(RECORD_CANCELED);
243 }
else if (!isSuccess) {
245 AttachmentStore.deleteOnExit(audioFile.getAbsolutePath());
246 callBackRecordState(RECORD_FAILED);
249 if (audioFile == null || !audioFile.exists() || audioFile.length() <= 0) {
250 callBackRecordState(RECORD_FAILED);
253 mEventHandler.post(
new Runnable() {
261 isRecording.set(
false);
264 private void callBackRecordState(
final int recordState) {
265 mEventHandler.post(
new Runnable() {
268 switch (recordState) {
278 case RECORD_CANCELED:
293 if (mAudioRecorder != null) {
294 return mAudioRecorder.getMaxAmplitude();
300 private void handleReachedMaxRecordTime(
int duration) {
305 private final OnInfoListener infoListener =
new OnInfoListener() {
307 public void onInfo(
int session,
int what,
int extra) {
309 case OnInfoListener.MEDIA_RECORDER_INFO_ERROR:
312 case OnInfoListener.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
313 mHandler.post(() -> isRecording.set(
false));
314 handleReachedMaxRecordTime(extra);
316 case OnInfoListener.MEDIA_RECORDER_INFO_RECORD_COMPLETE: