Flattern: ❗️ Issue: MIC aktiviert nicht nach der Wiederaufnahme der Aufnahme nach iOS -Telefon-/VoIP -AnrufunterbrechungIOS

Programmierung für iOS
Anonymous
 Flattern: ❗️ Issue: MIC aktiviert nicht nach der Wiederaufnahme der Aufnahme nach iOS -Telefon-/VoIP -Anrufunterbrechung

Post by Anonymous »

Platform: iOS
Flutter version: 3.29.3
Packages used:
  • record: ^6.0.0
  • audio_session: ^0.2.1
✅ Expected Behaviour
  • MIKROFON -Verfügbarkeitsprüfung < /li>
    Vor dem Starten oder Wiederaufnehmen von Aufnahmen, ob das Mikrofon bereits von einer anderen App verwendet wird. Um auch dann fortzufahren, wenn die App an den Hintergrund gesendet wird, um eine anhaltende Audio -Erfassung zu gewährleisten. /> Pause Aufzeichnung Wenn sich das System in einem Fokusmodus befindet (wie nicht stört) und das Mikrofon nicht verfügbar ist. Aufzeichnung < /li>
    Nehmen Sie die Aufnahme automatisch wieder auf, sobald die Unterbrechung (Anruf- oder Fokusmodus) endet und die Sitzung nahtlos beibehält. Die Aufnahme wird fortgesetzt und der Mikrofon -Orange -Punkt bleibt aktiv. PlatformException (Datensatz, der Vorgang konnte nicht abgeschlossen werden. (com.apple.coreaudio.avfaudio error -10868.), null, null)
🔁 Steps to Reproduce the Issue

[*]Start recording in the app (orange mic indicator appears)
[*]Receive an incoming Teams call (recording Fortsetzung)
Lehnen Sie die Teams ab (die Aufzeichnung der Aufnahme automatisch durch Unterbrechungsbehandlung). /> < /ul>
🔍 verwandte Code -Snippets < /p>

Setzen/*
_initialiseRecorderController is used to initialize recorder controller and audio session
*/
void _initialiseRecorderController() async {
_audioRecorder = AudioRecorder();
_session = await AudioSession.instance;
await _configureAudioSession();
}
< /code>

Konfigurieren Sie die 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>

Die Funktion zur Umschüttungsaufzeichnung wird aus der Schaltfläche Rekorder innerhalb der Build -Methode ausgelöst. < /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>

Überprüfen Sie die Verfügbarkeit von Mikrofon sowohl am Start als auch bei der Wiederaufnahme der Aufzeichnung. < /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>

Starten Sie die Aufzeichnung < /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>

Anfrage Audio Focus < /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>

Audio -Unterbrechung verhalten < /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>

Lebenslauf und pausieren Sie die Aufzeichnung < /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(); // 🔁 reconfigure session before resuming
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>

Stoppen Sie die Aufzeichnung < /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>

Deaktivieren Sie die Audiositzung < /li>
< /ol>
Future _deactivateAudioSession() async {
try {
await _session.setActive(
false,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation,
);
} catch (e) {
debugPrint("Error deactivating audio session: ${e.toString()}");
}
}
< /code>
🔄 Workaround Probit < /p>

Manuell deaktivieren und reaktivieren Sie die Sitzung. setActive (true). < /li>
< /ul>
❓ Fragen < /p>

Gibt es eine iOS-spezifische Problemumgehung, um das Mikrofon wieder aufzunehmen. /> < /ul>

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post