Версия Flutter: 3.29.3 < /p>
Используемые пакеты: < /p>
[*] Запись: ^6.0.0 < /li>
Audio_Session: ^0.2.1 < /li>
< /ul>
Проверка доступности микрофона < /li>
Перед началом или возобновлением записи проверьте, если микрофон уже используется другим приложением. Чтобы продолжить, даже когда приложение отправляется на фон, обеспечивает постоянный захват аудио. /> Пауза записи, если система находится в режиме фокусировки (например, не беспокоить), а микрофон недоступен. < /li>
Запись об резюме вручную должна возобновить только если микрофон. Запись < /li>
Автоматическое резюме записи после того, как прерывание (режим вызова или фокуса) заканчивается, поддержание сеанса плавно. Принято), запись продолжается, и микрофонный оранжевый точка остается активной. PlatformException (запись, операция не может быть завершена. (com.apple.coreaudio.avfaudio error -10868.), null, null) < /li>
< /ul>
Продолжается)
Снижение сортировки команд вызывает (запись автоматически пауза из -за перерыва) /> < /ul>
Настройка регистратора и аудиосессии во время инициализации в методе initstate. < /li>
< /ol>
/*
_initialiseRecorderController is used to initialize recorder controller and audio session
*/
void _initialiseRecorderController() async {
_audioRecorder = AudioRecorder();
_session = await AudioSession.instance;
await _configureAudioSession();
}
< /code>
Настройте Audio_Session < /li>
< /ol>
Future _configureAudioSession() async {
try {
await _session.configure(
AudioSessionConfiguration(
// iOS configuration for recording continuously when the another app starts recording
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation,
avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.mixWithOthers |
AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.allowBluetoothA2dp |
AVAudioSessionCategoryOptions.allowAirPlay,
avAudioSessionMode: AVAudioSessionMode.defaultMode,
// androidAudioAttributes: const AndroidAudioAttributes(
// contentType: AndroidAudioContentType.speech,
// flags: AndroidAudioFlags.audibilityEnforced,
// usage: AndroidAudioUsage.voiceCommunication,
// ),
androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransient,
androidWillPauseWhenDucked: false,
),
);
} catch (e) {
debugPrint("_configureAudioSession error : ${e.toString()}");
}
}
< /code>
Функция записи переключения запускается из кнопки регистратора в методе сборки. < /li>
< /ol>
/*
_toggleRecording is function that starts the recording if the
microphone request is granted and request permission if not.
*/
Future _toggleRecording() async {
try {
bool isMicrophoneAvailable = await _checkMicrophoneAvailability();
if (!isMicrophoneAvailable) {
await _showMicrophoneUnavailableDialog();
return;
}
if (await Permission.microphone.isGranted) {
if (_isRecordingStarted) {
await _resumePauseRecording();
} else {
await WakelockPlus.enable();
await _startRecording();
}
} else {
final bool isPermissionGranted = await ServiceUtils.requestPermission(
Permission.microphone,
);
if (isPermissionGranted) {
await _toggleRecording();
}
}
} catch (e) {
debugPrint("_toggleRecording error : ${e.toString()}");
}
}
< /code>
Проверьте доступность микрофона как в начале, так и при возобновлении записи. < /li>
< /ol>
Future _checkMicrophoneAvailability() async {
try {
if (_isMicrophoneInUse && Platform.isAndroid) {
debugPrint("Microphone is currently in use by another app.");
return false;
}
final bool isActive = await _requestAudioFocus();
if (!isActive) {
await _session.setActive(false);
debugPrint("Microphone is unavailable. Another app is using it.");
return false;
}
return true;
} catch (e) {
await _session.setActive(false);
debugPrint("Error checking microphone availability: ${e.toString()}");
return false;
}
}
< /code>
Начните запись < /li>
< /ol>
/*
_startRecording function calls the _startBackupRecording with the starting
of the foreground service and connecting to the socket
*/
Future _startRecording() async {
try {
_meetingStartTime = DateTime.now().millisecondsSinceEpoch;
_startForegroundTask();
await _socketConnection();
_recordDuration = 0;
_startTimer();
await _startBackupRecording();
setState(() {
_isRecording = true;
_isRecordingStarted = true;
_isRecordingCompleted = false;
});
} catch (e) {
debugPrint("_startRecording error : ${e.toString()}");
}
}
/*
_startBackupRecording starts the recording using startStream and
_recordStream is listened to add audio data to the _recordedData continuously
and a _backupTimer is started for creating 30 secs chunk file
*/
Future _startBackupRecording() async {
try {
await _configureAudioSession();
bool active = await _requestAudioFocus();
if (!active) return; // Prevent starting if focus not acquired
await _handleAudioInterruptions();
_recordStream = await _audioRecorder.startStream(
const RecordConfig(
encoder: AudioEncoder.pcm16bits,
sampleRate: 22000,
numChannels: 1,
// bluetoothSco: true,
autoGain: true,
),
);
_recordStream?.listen((event) {
_recordedData.add(event);
});
_startBackupTimer();
} catch (e) {
debugPrint("Error startBackupRecording: ${e.toString()}");
}
}
< /code>
Запросить аудио фокус < /li>
< /ol>
/*
_requestAudioFocus is used to request audio focus when app starts recording
*/
Future _requestAudioFocus() async {
try {
return await _session.setActive(
true,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation,
);
} catch (e) {
return false;
}
}
< /code>
Обработка аудио прерывания < /li>
< /ol>
/*
_handleAudioInterruptions is used to detect and handle interruptions when another app uses microphone
*/
Future _handleAudioInterruptions() async {
_recordInterruptionSubscription = _session.interruptionEventStream.listen((event) async {
if (event.begin) {
setState(() {
_isMicrophoneInUse = true;
});
} else {
setState(() {
_isMicrophoneInUse = false;
});
}
});
}
< /code>
резюме и паузу < /li>
< /ol>
/*
_resumePauseRecording is used to resume and pause the recording.
When paused the _recordDuration _timer and _backupTimer is stopped.
When resumed the _recordDuration _timer and _backupTimer is started again.
*/
Future _resumePauseRecording() async {
if (_isRecording) {
// pause recording
} else {
// resume recording
}
}
/*
_resumeRecording is used to resume the recording.
When resumed the _recordDuration _timer and _backupTimer is started again.
*/
Future _resumeRecording() async {
try {
await _configureAudioSession(); //
bool active = await _requestAudioFocus();
if (!active) {
return;
}
await Future.delayed(Durations.medium1);
await _audioRecorder.resume();
_startTimer();
_startBackupTimer();
setState(() {
_isRecording = true;
_isPause = false;
_isManuallyPause = false;
});
} catch (e) {
}
}
/*
_pauseRecording is used to pause the recording and upload that part of chunk file.
When paused the _recordDuration _timer and _backupTimer is stopped.
*/
Future _pauseRecording({
bool isManuallyPause = false,
}) async {
try {
_timer?.cancel();
_backupTimer?.cancel();
await _audioRecorder.pause();
setState(() {
_isRecording = false;
_isPause = true;
_isManuallyPause = isManuallyPause;
});
} catch (e) {
debugPrint("Error _pauseRecording: ${e.toString()}");
}
}
< /code>
Остановить запись < /li>
< /ol>
/*
_stopRecording stops the recording and stops the foreground service
*/
Future _stopRecording() async {
try {
await _audioRecorder.stop();
_timer?.cancel();
_stopForegroundTask();
// Add a short delay before deactivating the session
await Future.delayed(const Duration(milliseconds: 200));
await _deactivateAudioSession();
setState(() {
_isRecording = false;
_isRecordingStarted = false;
_isPause = false;
_isManuallyPause = false;
_isRecordingCompleted = true;
_isMicrophoneInUse = false;
});
} catch (e) {
debugPrint("Error stopping the recording: ${e.toString()}");
}
}
< /code>
Deactivate Audio Session < /li>
< /ol>
Future _deactivateAudioSession() async {
try {
await _session.setActive(
false,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation,
);
} catch (e) {
debugPrint("Error deactivating audio session: ${e.toString()}");
}
}
< /code>
вручную деактивирование и реактивацию сеанса. < /li>
Унициализация звукового региона. setActive (true). < /li>
< /ul>
Есть ли какой-нибудь iOS-специфический обходной путь для возобновления микрофона после того, как
отказался от вызова Voip? /> < /ul>
Подробнее здесь: https://stackoverflow.com/questions/796 ... -after-ios