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());
118 mHandler.removeMessages(MSG_START_RECORD);
119 mHandler.obtainMessage(MSG_START_RECORD).sendToTarget();
122 private void onStartRecord() {
123 audioManager.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
125 if (isRecording.get()) {
126 NimLog.audio(TAG +
" startRecord false, as current state is isRecording");
127 callBackRecordState(RECORD_FAILED);
131 if (!NimStorageUtil.hasEnoughSpaceForWrite(NimStorageType.TYPE_AUDIO)) {
132 NimLog.audio(TAG +
" startRecord false, as has no enough space to write");
133 callBackRecordState(RECORD_FAILED);
139 String outputFilePath = NimStorageUtil.getWritePath(SDKCache.getContext(), StringUtil.get36UUID() + outputFormat,
140 NimStorageType.TYPE_AUDIO);
141 if (TextUtils.isEmpty(outputFilePath)) {
142 NimLog.audio(TAG +
" startRecord false, as outputFilePath is empty");
143 callBackRecordState(RECORD_FAILED);
146 outputFilePath = outputFilePath + recordType.
getFileSuffix();
147 audioFile =
new File(outputFilePath);
150 cancelRecord.set(
false);
153 mAudioRecorder =
new AudioRecord(context, outputFilePath, maxDuration * 1000);
154 mAudioRecorder.setAudioCodec(outputFormat);
155 networkClass = NetworkUtil.getNetworkClass(context);
156 if (networkClass == NetworkUtil.NETWORK_CLASS_3_G)
157 mAudioRecorder.setMaxSampleRateInHz(22050);
158 else if (networkClass == NetworkUtil.NETWORK_CLASS_2_G)
159 mAudioRecorder.setMaxSampleRateInHz(16000);
160 mAudioRecorder.setOnInfoListener(infoListener);
162 if (!cancelRecord.get()) {
163 callBackRecordState(RECORD_READY);
164 if (mAudioRecorder.startRecording()) {
165 isRecording.set(
true);
166 callBackRecordState(RECORD_START);
169 }
catch (Exception e) {
171 onCompleteRecord(
false);
173 if (!isRecording.get()) {
174 callBackRecordState(RECORD_FAILED);
185 Message message = mHandler.obtainMessage(MSG_STOP_RECORD);
186 message.obj = cancel;
187 message.sendToTarget();
190 private void onCompleteRecord(
boolean cancel) {
191 if (!isRecording.get()) {
194 cancelRecord.set(cancel);
195 audioManager.abandonAudioFocus(null);
197 if (mAudioRecorder != null) {
198 mAudioRecorder.stopRecording();
201 mAudioRecorder = null;
203 }
catch (Exception e) {
212 if (mHandler != null) {
213 mHandler.removeCallbacksAndMessages(null);
215 if (handlerThread != null && handlerThread.isAlive()) {
216 Looper looper = handlerThread.getLooper();
225 return isRecording.get();
229 Message message = mHandler.obtainMessage(MSG_END_RECORD);
230 message.obj = isSuccess;
231 message.arg1 = duration;
232 message.sendToTarget();
235 private void onHandleEndRecord(
boolean isSuccess,
final int duration) {
236 if (cancelRecord.get()) {
238 AttachmentStore.deleteOnExit(audioFile.getAbsolutePath());
240 callBackRecordState(RECORD_CANCELED);
241 }
else if (!isSuccess) {
243 AttachmentStore.deleteOnExit(audioFile.getAbsolutePath());
244 callBackRecordState(RECORD_FAILED);
247 if (audioFile == null || !audioFile.exists() || audioFile.length() <= 0) {
248 callBackRecordState(RECORD_FAILED);
251 mEventHandler.post(
new Runnable() {
259 isRecording.set(
false);
262 private void callBackRecordState(
final int recordState) {
263 mEventHandler.post(
new Runnable() {
266 switch (recordState) {
276 case RECORD_CANCELED:
291 if (mAudioRecorder != null) {
292 return mAudioRecorder.getMaxAmplitude();
298 private void handleReachedMaxRecordTime(
int duration) {
303 private final OnInfoListener infoListener =
new OnInfoListener() {
305 public void onInfo(
int session,
int what,
int extra) {
307 case OnInfoListener.MEDIA_RECORDER_INFO_ERROR:
310 case OnInfoListener.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
311 mHandler.post(() -> isRecording.set(
false));
312 handleReachedMaxRecordTime(extra);
314 case OnInfoListener.MEDIA_RECORDER_INFO_RECORD_COMPLETE: