Skip to content

Commit 0dc8b89

Browse files
committed
feat(audio): apply audio processing options with a typed result
LocalAudioTrack.setAudioProcessingOptions returns an AudioProcessingApplyResult (code + message + isSuccess) for operational outcomes — applied/stored, and the device-capability rejections platform-unavailable / apply-failed (inspect isSuccess). It throws AudioProcessingException only for malformed requests (invalid mode combination, or a non-local track). The native plugin handlers return {result, code, message}; the Dart side maps the code into the result or the exception accordingly.
1 parent 03e42b2 commit 0dc8b89

3 files changed

Lines changed: 85 additions & 22 deletions

File tree

lib/src/support/native.dart

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,28 +54,24 @@ class Native {
5454
///
5555
/// Resolved natively against the underlying WebRTC audio track owned by
5656
/// flutter_webrtc; [options] is the serialized [AudioProcessingOptions] map.
57-
/// Returns whether the native layer applied/stored the request.
57+
/// Returns the native result map (`result`/`code`/`message`) so the caller
58+
/// can surface typed rejections. Channel errors propagate to the caller.
5859
@internal
59-
static Future<bool> setAudioProcessingOptions(
60+
static Future<Map<String, dynamic>> setAudioProcessingOptions(
6061
String trackId,
6162
Map<String, dynamic> options,
6263
) async {
63-
try {
64-
final response = await channel.invokeMethod<dynamic>(
65-
'setAudioProcessingOptions',
66-
<String, dynamic>{
67-
'trackId': trackId,
68-
...options,
69-
},
70-
);
71-
if (response is Map) {
72-
return response['result'] == true;
73-
}
74-
return response == true;
75-
} catch (error) {
76-
logger.warning('setAudioProcessingOptions did throw $error');
77-
return false;
64+
final response = await channel.invokeMethod<dynamic>(
65+
'setAudioProcessingOptions',
66+
<String, dynamic>{
67+
'trackId': trackId,
68+
...options,
69+
},
70+
);
71+
if (response is Map) {
72+
return response.map((key, value) => MapEntry(key.toString(), value));
7873
}
74+
return <String, dynamic>{};
7975
}
8076

8177
@internal

lib/src/track/local/audio.dart

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,36 @@ class LocalAudioTrack extends LocalTrack with AudioTrack, LocalAudioManagementMi
4747
}
4848
}
4949

50-
Future<bool> setAudioProcessingOptions(track_options.AudioProcessingOptions options) async {
50+
Future<track_options.AudioProcessingApplyResult> setAudioProcessingOptions(
51+
track_options.AudioProcessingOptions options) async {
5152
final nextOptions = currentOptions.copyWith(processing: options);
52-
final success = await Native.setAudioProcessingOptions(
53+
final response = await Native.setAudioProcessingOptions(
5354
mediaStreamTrack.id!,
5455
options.toMap(),
5556
);
5657

57-
if (success) {
58+
final code = track_options.AudioProcessingOptionsResultCode.fromValue(response['code'] as String?);
59+
final message = (response['message'] as String?) ?? '';
60+
61+
// Malformed requests (incompatible modes, or a non-local track) are caller
62+
// bugs — surface them loudly rather than as a silently-unsuccessful result.
63+
if (code == track_options.AudioProcessingOptionsResultCode.rejectedInvalidCombination ||
64+
code == track_options.AudioProcessingOptionsResultCode.rejectedRemoteTrack) {
65+
throw track_options.AudioProcessingException(
66+
code,
67+
message.isNotEmpty ? message : 'Unable to apply audio processing options',
68+
);
69+
}
70+
71+
final result = track_options.AudioProcessingApplyResult(code, message);
72+
if (result.isSuccess) {
5873
currentOptions = nextOptions;
5974
events.emit(LocalTrackOptionsUpdatedEvent(
6075
track: this,
6176
options: currentOptions,
6277
));
6378
}
64-
65-
return success;
79+
return result;
6680
}
6781

6882
num? _currentBitrate;

lib/src/track/options.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,3 +516,56 @@ class AudioOutputOptions {
516516
);
517517
}
518518
}
519+
520+
/// Result code from applying [AudioProcessingOptions], mirroring the native
521+
/// `AudioProcessingOptionsResult`. `applied`/`stored` are success; the rest are
522+
/// rejections.
523+
enum AudioProcessingOptionsResultCode {
524+
applied('applied'),
525+
stored('stored'),
526+
rejectedRemoteTrack('rejectedRemoteTrack'),
527+
rejectedInvalidCombination('rejectedInvalidCombination'),
528+
rejectedPlatformUnavailable('rejectedPlatformUnavailable'),
529+
applyFailed('applyFailed');
530+
531+
const AudioProcessingOptionsResultCode(this.value);
532+
533+
final String value;
534+
535+
static AudioProcessingOptionsResultCode fromValue(String? value) =>
536+
AudioProcessingOptionsResultCode.values.firstWhere(
537+
(e) => e.value == value,
538+
orElse: () => AudioProcessingOptionsResultCode.applyFailed,
539+
);
540+
541+
bool get isSuccess =>
542+
this == AudioProcessingOptionsResultCode.applied || this == AudioProcessingOptionsResultCode.stored;
543+
}
544+
545+
/// Thrown when the native layer rejects requested [AudioProcessingOptions]
546+
/// (e.g. an invalid platform/software combination, or platform processing that
547+
/// is unavailable on the device).
548+
class AudioProcessingException implements Exception {
549+
AudioProcessingException(this.code, this.message);
550+
551+
final AudioProcessingOptionsResultCode code;
552+
final String message;
553+
554+
@override
555+
String toString() => 'AudioProcessingException(${code.value}): $message';
556+
}
557+
558+
/// Outcome of applying [AudioProcessingOptions].
559+
///
560+
/// Returned for operational outcomes — `applied`/`stored` (success), and the
561+
/// device-capability rejections `rejectedPlatformUnavailable`/`applyFailed`
562+
/// (inspect [isSuccess]). A malformed request (incompatible modes, or a
563+
/// non-local track) throws [AudioProcessingException] instead.
564+
class AudioProcessingApplyResult {
565+
AudioProcessingApplyResult(this.code, this.message);
566+
567+
final AudioProcessingOptionsResultCode code;
568+
final String message;
569+
570+
bool get isSuccess => code.isSuccess;
571+
}

0 commit comments

Comments
 (0)