Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,14 @@ static public String getAudioGroupId(AudioDeviceInfo device) {
if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC) {
groupId = "microphone";
}
if (device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
if (device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET
|| device.getType() == AudioDeviceInfo.TYPE_USB_HEADSET
|| device.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
groupId = "wired-headset";
}
if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
|| device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
|| device.getType() == AudioDeviceInfo.TYPE_BLE_SPEAKER) {
groupId = "bluetooth";
}
return groupId;
Expand Down
2 changes: 2 additions & 0 deletions common/darwin/Classes/FlutterRTCMediaStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@
- (void)selectAudioInput:(nonnull NSString*)deviceId result:(nullable FlutterResult)result;

- (void)selectAudioOutput:(nonnull NSString*)deviceId result:(nullable FlutterResult)result;

- (void)triggeriOSAudioRouteSelectionUI:(nonnull FlutterResult)result;
@end
55 changes: 55 additions & 0 deletions common/darwin/Classes/FlutterRTCMediaStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import "VideoProcessingAdapter.h"
#import "LocalVideoTrack.h"
#import "LocalAudioTrack.h"
#import "AVKit/AVKit.h"

@implementation RTCMediaStreamTrack (Flutter)

Expand Down Expand Up @@ -874,6 +875,60 @@ - (void)selectAudioOutput:(NSString*)deviceId result:(FlutterResult)result {
details:nil]);
}

- (void)triggeriOSAudioRouteSelectionUI:(FlutterResult)result {
#if TARGET_OS_IPHONE
if (@available(iOS 11.0, *)) {
AVRoutePickerView *routePicker = [[AVRoutePickerView alloc] init];
routePicker.frame = CGRectMake(0, 0, 44, 44);

// Add the route picker to a temporary window to ensure it's in the view hierarchy
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
if (!window) {
// Fallback for iOS 13+ where keyWindow is deprecated
for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) {
if (windowScene.activationState == UISceneActivationStateForegroundActive) {
window = windowScene.windows.firstObject;
break;
}
}
}

if (window) {
[window addSubview:routePicker];

// Trigger the route picker programmatically
for (UIView *view in routePicker.subviews) {
if ([view isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)view;
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
break; // Only trigger the first button found
}
}

// Remove the route picker after a short delay
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[routePicker removeFromSuperview];
});

result(nil);
} else {
result([FlutterError errorWithCode:@"NoWindowError"
message:@"Could not find a window to present the route picker"
details:nil]);
}
} else {
result([FlutterError errorWithCode:@"UnsupportedVersionError"
message:@"AVRoutePickerView is only available on iOS 11.0 or later"
details:nil]);
}
#else
// macOS doesn't support iOS audio route selection UI
result([FlutterError errorWithCode:@"UnsupportedPlatformError"
message:@"triggeriOSAudioRouteSelectionUI is only supported on iOS"
details:nil]);
#endif
}

- (void)mediaStreamTrackRelease:(RTCMediaStream*)mediaStream track:(RTCMediaStreamTrack*)track {
// what's different to mediaStreamTrackStop? only call mediaStream explicitly?
if (mediaStream && track) {
Expand Down
3 changes: 3 additions & 0 deletions common/darwin/Classes/FlutterWebRTCPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import <AVFoundation/AVFoundation.h>
#import <WebRTC/RTCFieldTrials.h>
#import <WebRTC/WebRTC.h>
#import <AVKit/AVKit.h>

#import "LocalTrack.h"
#import "LocalAudioTrack.h"
Expand Down Expand Up @@ -357,6 +358,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
NSDictionary* argsMap = call.arguments;
NSString* deviceId = argsMap[@"deviceId"];
[self selectAudioOutput:deviceId result:result];
} else if ([@"triggeriOSAudioRouteSelectionUI" isEqualToString:call.method]) {
[self triggeriOSAudioRouteSelectionUI:result];
} else if ([@"mediaStreamGetTracks" isEqualToString:call.method]) {
NSDictionary* argsMap = call.arguments;
NSString* streamId = argsMap[@"streamId"];
Expand Down
11 changes: 11 additions & 0 deletions lib/src/helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,15 @@ class Helper {
'enableIOSMultitaskingCameraAccess is only supported for iOS');
}
}

static Future<void> triggeriOSAudioRouteSelectionUI() async {
if (WebRTC.platformIsIOS) {
return await WebRTC.invokeMethod(
'triggeriOSAudioRouteSelectionUI',
);
} else {
throw Exception(
'triggeriOSAudioRouteSelectionUI is only supported for iOS');
}
}
}