@@ -753,6 +753,24 @@ extern "C" fn audiounit_property_listener_callback(
753
753
}
754
754
stm. switching_device . store ( true , Ordering :: SeqCst ) ;
755
755
756
+ let should_input_device_change =
757
+ stm. core_stream_data . has_input ( ) &&
758
+ !stm. core_stream_data
759
+ . input_stream_params . prefs ( )
760
+ . contains ( StreamPrefs :: DISABLE_DEVICE_SWITCHING ) &&
761
+ stm. core_stream_data
762
+ . input_device . flags
763
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT ) ;
764
+
765
+ let should_output_device_change =
766
+ stm. core_stream_data . has_output ( ) &&
767
+ !stm. core_stream_data
768
+ . output_stream_params . prefs ( )
769
+ . contains ( StreamPrefs :: DISABLE_DEVICE_SWITCHING ) &&
770
+ stm. core_stream_data
771
+ . output_device . flags
772
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT ) ;
773
+
756
774
cubeb_log ! (
757
775
"({:p}) Audio device changed, {} events." ,
758
776
stm as * const AudioUnitStream ,
@@ -766,32 +784,108 @@ extern "C" fn audiounit_property_listener_callback(
766
784
i,
767
785
id
768
786
) ;
787
+ if !should_output_device_change {
788
+ cubeb_log ! ( "Output device should not change, ignore the event" ) ;
789
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
790
+ return NO_ERR ;
791
+ }
792
+ // When a device needs to be updated it always goes to the default one. Thus the
793
+ // output device is set to null. The re-init process will replace it with the current
794
+ // default device later.
795
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
796
+ // In case of a duplex call the output device info is filled. That will signal to the
797
+ // re-init process that an explicit chosen device is selected. However, if the
798
+ // user had selected the implicit default device (null deviceId) for the input device
799
+ // has to be cleaned in order to signal to reinit process that the implicit default
800
+ // device is being used (DEV_SELECTED_DEFAULT is the implicit defaut device).
801
+ if stm. core_stream_data . input_device . flags . contains ( device_flags:: DEV_SELECTED_DEFAULT ) {
802
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
803
+ }
769
804
}
770
805
sys:: kAudioHardwarePropertyDefaultInputDevice => {
771
806
cubeb_log ! (
772
807
"Event[{}] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id={}" ,
773
808
i,
774
809
id
775
810
) ;
811
+ // See the comments above for default output case. The code is symmetrical.
812
+ if !should_input_device_change {
813
+ cubeb_log ! ( "Input device should not change, ignore the event" ) ;
814
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
815
+ return NO_ERR ;
816
+ }
817
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
818
+ if stm. core_stream_data . output_device . flags . contains ( device_flags:: DEV_SELECTED_DEFAULT ) {
819
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
820
+ }
776
821
}
777
822
sys:: kAudioDevicePropertyDeviceIsAlive => {
778
823
cubeb_log ! (
779
824
"Event[{}] - mSelector == kAudioDevicePropertyDeviceIsAlive for id={}" ,
780
825
i,
781
826
id
782
827
) ;
783
- // If this is the default input device ignore the event,
784
- // kAudioHardwarePropertyDefaultInputDevice will take care of the switch
785
- if stm
786
- . core_stream_data
787
- . input_device
788
- . flags
789
- . contains ( device_flags:: DEV_SYSTEM_DEFAULT )
790
- {
791
- cubeb_log ! ( "It's the default input device, ignore the event" ) ;
792
- stm. switching_device . store ( false , Ordering :: SeqCst ) ;
828
+
829
+ // The same (removed) device is used for input and output, for example a headset.
830
+ if stm. core_stream_data . input_device . id == id &&
831
+ stm. core_stream_data . output_device . id == id &&
832
+ ( !should_input_device_change || !should_output_device_change) {
833
+ cubeb_log ! ( "Duplex device should not change, ignore the event" ) ;
834
+ stm. report_error_async ( ) ;
793
835
return NO_ERR ;
794
836
}
837
+
838
+ if stm. core_stream_data . input_device . id == id {
839
+ // Keep that first because it is required to return an error callback if the
840
+ // device is removed but we don't have the option to change it.
841
+ if !should_input_device_change {
842
+ cubeb_log ! ( "Input device should not change, ignore the event" ) ;
843
+ stm. report_error_async ( ) ;
844
+ return NO_ERR ;
845
+ }
846
+
847
+ // Since the device will change let default event do so, if that's the case.
848
+ if stm
849
+ . core_stream_data
850
+ . input_device
851
+ . flags
852
+ . contains ( device_flags:: DEV_SYSTEM_DEFAULT )
853
+ {
854
+ cubeb_log ! ( "It's the default input device, ignore the event" ) ;
855
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
856
+ return NO_ERR ;
857
+ }
858
+
859
+ // The device is not the default, update it.
860
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
861
+ if stm. core_stream_data . output_device . flags . contains ( device_flags:: DEV_SELECTED_DEFAULT ) {
862
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
863
+ }
864
+ }
865
+
866
+ if stm. core_stream_data . output_device . id == id {
867
+ if !should_output_device_change {
868
+ cubeb_log ! ( "Output device should not change, ignore the event" ) ;
869
+ stm. report_error_async ( ) ;
870
+ return NO_ERR ;
871
+ }
872
+
873
+ if stm
874
+ . core_stream_data
875
+ . input_device
876
+ . flags
877
+ . contains ( device_flags:: DEV_SYSTEM_DEFAULT )
878
+ {
879
+ cubeb_log ! ( "It's the default input device, ignore the event" ) ;
880
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
881
+ return NO_ERR ;
882
+ }
883
+
884
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
885
+ if stm. core_stream_data . input_device . flags . contains ( device_flags:: DEV_SELECTED_DEFAULT ) {
886
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
887
+ }
888
+ }
795
889
}
796
890
sys:: kAudioDevicePropertyDataSource => {
797
891
cubeb_log ! (
@@ -984,7 +1078,7 @@ fn create_audiounit(device: &device_info) -> Result<AudioUnit> {
984
1078
. flags
985
1079
. contains( device_flags:: DEV_INPUT | device_flags:: DEV_OUTPUT ) ) ;
986
1080
987
- let unit = create_default_audiounit ( device . flags ) ?;
1081
+ let unit = create_default_audiounit ( ) ?;
988
1082
if device
989
1083
. flags
990
1084
. contains ( device_flags:: DEV_SYSTEM_DEFAULT | device_flags:: DEV_OUTPUT )
@@ -1080,26 +1174,16 @@ fn set_device_to_audiounit(
1080
1174
}
1081
1175
}
1082
1176
1083
- fn create_default_audiounit ( flags : device_flags ) -> Result < AudioUnit > {
1084
- let desc = get_audiounit_description ( flags ) ;
1177
+ fn create_default_audiounit ( ) -> Result < AudioUnit > {
1178
+ let desc = get_audiounit_description ( ) ;
1085
1179
create_audiounit_by_description ( desc)
1086
1180
}
1087
1181
1088
- fn get_audiounit_description ( flags : device_flags ) -> AudioComponentDescription {
1182
+ fn get_audiounit_description ( ) -> AudioComponentDescription {
1089
1183
AudioComponentDescription {
1090
1184
componentType : kAudioUnitType_Output,
1091
- // Use the DefaultOutputUnit for output when no device is specified
1092
- // so we retain automatic output device switching when the default
1093
- // changes. Once we have complete support for device notifications
1094
- // and switching, we can use the AUHAL for everything.
1095
1185
#[ cfg( not( target_os = "ios" ) ) ]
1096
- componentSubType : if flags
1097
- . contains ( device_flags:: DEV_SYSTEM_DEFAULT | device_flags:: DEV_OUTPUT )
1098
- {
1099
- kAudioUnitSubType_DefaultOutput
1100
- } else {
1101
- kAudioUnitSubType_HALOutput
1102
- } ,
1186
+ componentSubType : kAudioUnitSubType_HALOutput,
1103
1187
#[ cfg( target_os = "ios" ) ]
1104
1188
componentSubType : kAudioUnitSubType_RemoteIO,
1105
1189
componentManufacturer : kAudioUnitManufacturer_Apple,
@@ -2321,6 +2405,7 @@ struct CoreStreamData<'ctx> {
2321
2405
default_input_listener : Option < device_property_listener > ,
2322
2406
default_output_listener : Option < device_property_listener > ,
2323
2407
input_alive_listener : Option < device_property_listener > ,
2408
+ output_alive_listener : Option < device_property_listener > ,
2324
2409
input_source_listener : Option < device_property_listener > ,
2325
2410
output_source_listener : Option < device_property_listener > ,
2326
2411
}
@@ -2359,6 +2444,7 @@ impl<'ctx> Default for CoreStreamData<'ctx> {
2359
2444
default_input_listener : None ,
2360
2445
default_output_listener : None ,
2361
2446
input_alive_listener : None ,
2447
+ output_alive_listener : None ,
2362
2448
input_source_listener : None ,
2363
2449
output_source_listener : None ,
2364
2450
}
@@ -2404,6 +2490,7 @@ impl<'ctx> CoreStreamData<'ctx> {
2404
2490
default_input_listener : None ,
2405
2491
default_output_listener : None ,
2406
2492
input_alive_listener : None ,
2493
+ output_alive_listener : None ,
2407
2494
input_source_listener : None ,
2408
2495
output_source_listener : None ,
2409
2496
}
@@ -2952,6 +3039,22 @@ impl<'ctx> CoreStreamData<'ctx> {
2952
3039
cubeb_log ! ( "AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource rv={}, device id={}" , rv, self . output_device. id) ;
2953
3040
return Err ( Error :: error ( ) ) ;
2954
3041
}
3042
+
3043
+ // Event to notify when the input is going away.
3044
+ self . output_alive_listener = Some ( device_property_listener:: new (
3045
+ self . output_device . id ,
3046
+ get_property_address (
3047
+ Property :: DeviceIsAlive ,
3048
+ DeviceType :: INPUT | DeviceType :: OUTPUT ,
3049
+ ) ,
3050
+ audiounit_property_listener_callback,
3051
+ ) ) ;
3052
+ let rv = stm. add_device_listener ( self . output_alive_listener . as_ref ( ) . unwrap ( ) ) ;
3053
+ if rv != NO_ERR {
3054
+ self . output_alive_listener = None ;
3055
+ cubeb_log ! ( "AudioObjectAddPropertyListener/output/kAudioDevicePropertyDeviceIsAlive rv={}, device id ={}" , rv, self . input_device. id) ;
3056
+ return Err ( Error :: error ( ) ) ;
3057
+ }
2955
3058
}
2956
3059
2957
3060
if !self . input_unit . is_null ( ) {
@@ -2971,20 +3074,24 @@ impl<'ctx> CoreStreamData<'ctx> {
2971
3074
return Err ( Error :: error ( ) ) ;
2972
3075
}
2973
3076
2974
- // Event to notify when the input is going away.
2975
- self . input_alive_listener = Some ( device_property_listener:: new (
2976
- self . input_device . id ,
2977
- get_property_address (
2978
- Property :: DeviceIsAlive ,
2979
- DeviceType :: INPUT | DeviceType :: OUTPUT ,
2980
- ) ,
2981
- audiounit_property_listener_callback,
2982
- ) ) ;
2983
- let rv = stm. add_device_listener ( self . input_alive_listener . as_ref ( ) . unwrap ( ) ) ;
2984
- if rv != NO_ERR {
2985
- self . input_alive_listener = None ;
2986
- cubeb_log ! ( "AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv={}, device id ={}" , rv, self . input_device. id) ;
2987
- return Err ( Error :: error ( ) ) ;
3077
+ // If the event is registered for the output device, it cannot be re-registered to the
3078
+ // same device (it will return an 'nope' error).
3079
+ if self . input_device . id != self . output_device . id {
3080
+ // Event to notify when the input is going away.
3081
+ self . input_alive_listener = Some ( device_property_listener:: new (
3082
+ self . input_device . id ,
3083
+ get_property_address (
3084
+ Property :: DeviceIsAlive ,
3085
+ DeviceType :: INPUT | DeviceType :: OUTPUT ,
3086
+ ) ,
3087
+ audiounit_property_listener_callback,
3088
+ ) ) ;
3089
+ let rv = stm. add_device_listener ( self . input_alive_listener . as_ref ( ) . unwrap ( ) ) ;
3090
+ if rv != NO_ERR {
3091
+ self . input_alive_listener = None ;
3092
+ cubeb_log ! ( "AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv={}, device id ={}" , rv, self . input_device. id) ;
3093
+ return Err ( Error :: error ( ) ) ;
3094
+ }
2988
3095
}
2989
3096
}
2990
3097
@@ -3043,6 +3150,7 @@ impl<'ctx> CoreStreamData<'ctx> {
3043
3150
self . output_source_listener. is_none( )
3044
3151
&& self . input_source_listener. is_none( )
3045
3152
&& self . input_alive_listener. is_none( )
3153
+ && self . output_alive_listener. is_none( )
3046
3154
) ;
3047
3155
return Ok ( ( ) ) ;
3048
3156
}
@@ -3061,6 +3169,15 @@ impl<'ctx> CoreStreamData<'ctx> {
3061
3169
self . output_source_listener = None ;
3062
3170
}
3063
3171
3172
+ if self . output_alive_listener . is_some ( ) {
3173
+ let rv = stm. remove_device_listener ( self . output_alive_listener . as_ref ( ) . unwrap ( ) ) ;
3174
+ if rv != NO_ERR {
3175
+ cubeb_log ! ( "AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDeviceIsAlive rv={}, device id={}" , rv, self . input_device. id) ;
3176
+ r = Err ( Error :: error ( ) ) ;
3177
+ }
3178
+ self . output_alive_listener = None ;
3179
+ }
3180
+
3064
3181
if self . input_source_listener . is_some ( ) {
3065
3182
let rv = stm. remove_device_listener ( self . input_source_listener . as_ref ( ) . unwrap ( ) ) ;
3066
3183
if rv != NO_ERR {
@@ -3249,6 +3366,13 @@ impl<'ctx> AudioUnitStream<'ctx> {
3249
3366
kAudioObjectUnknown
3250
3367
} ;
3251
3368
3369
+ let has_output = !self . core_stream_data . output_unit . is_null ( ) ;
3370
+ let output_device = if has_output {
3371
+ self . core_stream_data . output_device . id
3372
+ } else {
3373
+ kAudioObjectUnknown
3374
+ } ;
3375
+
3252
3376
self . core_stream_data . close ( ) ;
3253
3377
3254
3378
// Reinit occurs in one of the following case:
@@ -3270,13 +3394,15 @@ impl<'ctx> AudioUnitStream<'ctx> {
3270
3394
// Always use the default output on reinit. This is not correct in every
3271
3395
// case but it is sufficient for Firefox and prevent reinit from reporting
3272
3396
// failures. It will change soon when reinit mechanism will be updated.
3273
- self . core_stream_data . output_device = create_device_info ( kAudioObjectUnknown, DeviceType :: OUTPUT ) . map_err ( |e| {
3274
- cubeb_log ! (
3275
- "({:p}) Create output device info failed. This can happen when last media device is unplugged" ,
3276
- self . core_stream_data. stm_ptr
3277
- ) ;
3278
- e
3279
- } ) ?;
3397
+ if has_output {
3398
+ self . core_stream_data . output_device = create_device_info ( output_device, DeviceType :: OUTPUT ) . map_err ( |e| {
3399
+ cubeb_log ! (
3400
+ "({:p}) Create output device info failed. This can happen when last media device is unplugged" ,
3401
+ self . core_stream_data. stm_ptr
3402
+ ) ;
3403
+ e
3404
+ } ) ?;
3405
+ }
3280
3406
3281
3407
if self . core_stream_data . setup ( ) . is_err ( ) {
3282
3408
cubeb_log ! (
@@ -3321,6 +3447,36 @@ impl<'ctx> AudioUnitStream<'ctx> {
3321
3447
Ok ( ( ) )
3322
3448
}
3323
3449
3450
+ // Stop (and destroy) the stream and fire an error state changed callback in a new thread.
3451
+ fn report_error_async ( & mut self ) {
3452
+ let queue = self . queue . clone ( ) ;
3453
+ let mutexed_stm = Arc :: new ( Mutex :: new ( self ) ) ;
3454
+ let also_mutexed_stm = Arc :: clone ( & mutexed_stm) ;
3455
+ queue. run_async ( move || {
3456
+ let mut stm_guard = also_mutexed_stm. lock ( ) . unwrap ( ) ;
3457
+ let stm_ptr = * stm_guard as * const AudioUnitStream ;
3458
+ if stm_guard. destroy_pending . load ( Ordering :: SeqCst ) {
3459
+ cubeb_log ! (
3460
+ "({:p}) stream pending destroy, cancelling error report" ,
3461
+ stm_ptr
3462
+ ) ;
3463
+ return ;
3464
+ }
3465
+
3466
+ if !stm_guard. shutdown . load ( Ordering :: SeqCst ) {
3467
+ stm_guard. core_stream_data . stop_audiounits ( ) ;
3468
+ }
3469
+ debug_assert ! (
3470
+ !stm_guard. core_stream_data. input_unit. is_null( )
3471
+ || !stm_guard. core_stream_data. output_unit. is_null( )
3472
+ ) ;
3473
+ stm_guard. core_stream_data . close ( ) ;
3474
+ stm_guard. notify_state_changed ( State :: Error ) ;
3475
+
3476
+ stm_guard. switching_device . store ( false , Ordering :: SeqCst ) ;
3477
+ } ) ;
3478
+ }
3479
+
3324
3480
fn reinit_async ( & mut self ) {
3325
3481
if self . reinit_pending . swap ( true , Ordering :: SeqCst ) {
3326
3482
// A reinit task is already pending, nothing more to do.
0 commit comments