@@ -753,6 +753,30 @@ 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 = stm. core_stream_data . has_input ( )
757
+ && !stm
758
+ . core_stream_data
759
+ . input_stream_params
760
+ . prefs ( )
761
+ . contains ( StreamPrefs :: DISABLE_DEVICE_SWITCHING )
762
+ && stm
763
+ . core_stream_data
764
+ . input_device
765
+ . flags
766
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT ) ;
767
+
768
+ let should_output_device_change = stm. core_stream_data . has_output ( )
769
+ && !stm
770
+ . core_stream_data
771
+ . output_stream_params
772
+ . prefs ( )
773
+ . contains ( StreamPrefs :: DISABLE_DEVICE_SWITCHING )
774
+ && stm
775
+ . core_stream_data
776
+ . output_device
777
+ . flags
778
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT ) ;
779
+
756
780
cubeb_log ! (
757
781
"({:p}) Audio device changed, {} events." ,
758
782
stm as * const AudioUnitStream ,
@@ -766,32 +790,129 @@ extern "C" fn audiounit_property_listener_callback(
766
790
i,
767
791
id
768
792
) ;
793
+ if !should_output_device_change {
794
+ cubeb_log ! ( "Output device should not change, ignore the event" ) ;
795
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
796
+ return NO_ERR ;
797
+ }
798
+ // When a device needs to be updated it always goes to the default one. Thus the
799
+ // output device is set to null. The re-init process will replace it with the current
800
+ // default device later.
801
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
802
+ // In case of a duplex call the output device info is filled. That will signal to the
803
+ // re-init process that an explicit chosen device is selected. However, if the
804
+ // user had selected the implicit default device (null deviceId) for the input device
805
+ // has to be cleaned in order to signal to reinit process that the implicit default
806
+ // device is being used (DEV_SELECTED_DEFAULT is the implicit defaut device).
807
+ if stm
808
+ . core_stream_data
809
+ . input_device
810
+ . flags
811
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT )
812
+ {
813
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
814
+ }
769
815
}
770
816
sys:: kAudioHardwarePropertyDefaultInputDevice => {
771
817
cubeb_log ! (
772
818
"Event[{}] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id={}" ,
773
819
i,
774
820
id
775
821
) ;
822
+ // See the comments above for default output case. The code is symmetrical.
823
+ if !should_input_device_change {
824
+ cubeb_log ! ( "Input device should not change, ignore the event" ) ;
825
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
826
+ return NO_ERR ;
827
+ }
828
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
829
+ if stm
830
+ . core_stream_data
831
+ . output_device
832
+ . flags
833
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT )
834
+ {
835
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
836
+ }
776
837
}
777
838
sys:: kAudioDevicePropertyDeviceIsAlive => {
778
839
cubeb_log ! (
779
840
"Event[{}] - mSelector == kAudioDevicePropertyDeviceIsAlive for id={}" ,
780
841
i,
781
842
id
782
843
) ;
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 )
844
+
845
+ // The same (removed) device is used for input and output, for example a headset.
846
+ if stm. core_stream_data . input_device . id == id
847
+ && stm. core_stream_data . output_device . id == id
848
+ && ( !should_input_device_change || !should_output_device_change)
790
849
{
791
- cubeb_log ! ( "It's the default input device , ignore the event" ) ;
792
- stm. switching_device . store ( false , Ordering :: SeqCst ) ;
850
+ cubeb_log ! ( "Duplex device should not change , ignore the event" ) ;
851
+ stm. report_error_async ( ) ;
793
852
return NO_ERR ;
794
853
}
854
+
855
+ if stm. core_stream_data . input_device . id == id {
856
+ // Keep that first because it is required to return an error callback if the
857
+ // device is removed but we don't have the option to change it.
858
+ if !should_input_device_change {
859
+ cubeb_log ! ( "Input device should not change, ignore the event" ) ;
860
+ stm. report_error_async ( ) ;
861
+ return NO_ERR ;
862
+ }
863
+
864
+ // Since the device will change let default event do so, if that's the case.
865
+ if stm
866
+ . core_stream_data
867
+ . input_device
868
+ . flags
869
+ . contains ( device_flags:: DEV_SYSTEM_DEFAULT )
870
+ {
871
+ cubeb_log ! ( "It's the default input device, ignore the event" ) ;
872
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
873
+ return NO_ERR ;
874
+ }
875
+
876
+ // The device is not the default, update it.
877
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
878
+ if stm
879
+ . core_stream_data
880
+ . output_device
881
+ . flags
882
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT )
883
+ {
884
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
885
+ }
886
+ }
887
+
888
+ if stm. core_stream_data . output_device . id == id {
889
+ if !should_output_device_change {
890
+ cubeb_log ! ( "Output device should not change, ignore the event" ) ;
891
+ stm. report_error_async ( ) ;
892
+ return NO_ERR ;
893
+ }
894
+
895
+ if stm
896
+ . core_stream_data
897
+ . input_device
898
+ . flags
899
+ . contains ( device_flags:: DEV_SYSTEM_DEFAULT )
900
+ {
901
+ cubeb_log ! ( "It's the default input device, ignore the event" ) ;
902
+ stm. switching_device . store ( false , Ordering :: SeqCst ) ;
903
+ return NO_ERR ;
904
+ }
905
+
906
+ stm. core_stream_data . output_device . id = kAudioObjectUnknown;
907
+ if stm
908
+ . core_stream_data
909
+ . input_device
910
+ . flags
911
+ . contains ( device_flags:: DEV_SELECTED_DEFAULT )
912
+ {
913
+ stm. core_stream_data . input_device . id = kAudioObjectUnknown;
914
+ }
915
+ }
795
916
}
796
917
sys:: kAudioDevicePropertyDataSource => {
797
918
cubeb_log ! (
@@ -984,7 +1105,7 @@ fn create_audiounit(device: &device_info) -> Result<AudioUnit> {
984
1105
. flags
985
1106
. contains( device_flags:: DEV_INPUT | device_flags:: DEV_OUTPUT ) ) ;
986
1107
987
- let unit = create_default_audiounit ( device . flags ) ?;
1108
+ let unit = create_default_audiounit ( ) ?;
988
1109
if device
989
1110
. flags
990
1111
. contains ( device_flags:: DEV_SYSTEM_DEFAULT | device_flags:: DEV_OUTPUT )
@@ -1080,26 +1201,16 @@ fn set_device_to_audiounit(
1080
1201
}
1081
1202
}
1082
1203
1083
- fn create_default_audiounit ( flags : device_flags ) -> Result < AudioUnit > {
1084
- let desc = get_audiounit_description ( flags ) ;
1204
+ fn create_default_audiounit ( ) -> Result < AudioUnit > {
1205
+ let desc = get_audiounit_description ( ) ;
1085
1206
create_audiounit_by_description ( desc)
1086
1207
}
1087
1208
1088
- fn get_audiounit_description ( flags : device_flags ) -> AudioComponentDescription {
1209
+ fn get_audiounit_description ( ) -> AudioComponentDescription {
1089
1210
AudioComponentDescription {
1090
1211
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
1212
#[ 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
- } ,
1213
+ componentSubType : kAudioUnitSubType_HALOutput,
1103
1214
#[ cfg( target_os = "ios" ) ]
1104
1215
componentSubType : kAudioUnitSubType_RemoteIO,
1105
1216
componentManufacturer : kAudioUnitManufacturer_Apple,
@@ -2321,6 +2432,7 @@ struct CoreStreamData<'ctx> {
2321
2432
default_input_listener : Option < device_property_listener > ,
2322
2433
default_output_listener : Option < device_property_listener > ,
2323
2434
input_alive_listener : Option < device_property_listener > ,
2435
+ output_alive_listener : Option < device_property_listener > ,
2324
2436
input_source_listener : Option < device_property_listener > ,
2325
2437
output_source_listener : Option < device_property_listener > ,
2326
2438
}
@@ -2359,6 +2471,7 @@ impl<'ctx> Default for CoreStreamData<'ctx> {
2359
2471
default_input_listener : None ,
2360
2472
default_output_listener : None ,
2361
2473
input_alive_listener : None ,
2474
+ output_alive_listener : None ,
2362
2475
input_source_listener : None ,
2363
2476
output_source_listener : None ,
2364
2477
}
@@ -2404,6 +2517,7 @@ impl<'ctx> CoreStreamData<'ctx> {
2404
2517
default_input_listener : None ,
2405
2518
default_output_listener : None ,
2406
2519
input_alive_listener : None ,
2520
+ output_alive_listener : None ,
2407
2521
input_source_listener : None ,
2408
2522
output_source_listener : None ,
2409
2523
}
@@ -2952,6 +3066,22 @@ impl<'ctx> CoreStreamData<'ctx> {
2952
3066
cubeb_log ! ( "AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource rv={}, device id={}" , rv, self . output_device. id) ;
2953
3067
return Err ( Error :: error ( ) ) ;
2954
3068
}
3069
+
3070
+ // Event to notify when the input is going away.
3071
+ self . output_alive_listener = Some ( device_property_listener:: new (
3072
+ self . output_device . id ,
3073
+ get_property_address (
3074
+ Property :: DeviceIsAlive ,
3075
+ DeviceType :: INPUT | DeviceType :: OUTPUT ,
3076
+ ) ,
3077
+ audiounit_property_listener_callback,
3078
+ ) ) ;
3079
+ let rv = stm. add_device_listener ( self . output_alive_listener . as_ref ( ) . unwrap ( ) ) ;
3080
+ if rv != NO_ERR {
3081
+ self . output_alive_listener = None ;
3082
+ cubeb_log ! ( "AudioObjectAddPropertyListener/output/kAudioDevicePropertyDeviceIsAlive rv={}, device id ={}" , rv, self . input_device. id) ;
3083
+ return Err ( Error :: error ( ) ) ;
3084
+ }
2955
3085
}
2956
3086
2957
3087
if !self . input_unit . is_null ( ) {
@@ -2971,20 +3101,24 @@ impl<'ctx> CoreStreamData<'ctx> {
2971
3101
return Err ( Error :: error ( ) ) ;
2972
3102
}
2973
3103
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 ( ) ) ;
3104
+ // If the event is registered for the output device, it cannot be re-registered to the
3105
+ // same device (it will return an 'nope' error).
3106
+ if self . input_device . id != self . output_device . id {
3107
+ // Event to notify when the input is going away.
3108
+ self . input_alive_listener = Some ( device_property_listener:: new (
3109
+ self . input_device . id ,
3110
+ get_property_address (
3111
+ Property :: DeviceIsAlive ,
3112
+ DeviceType :: INPUT | DeviceType :: OUTPUT ,
3113
+ ) ,
3114
+ audiounit_property_listener_callback,
3115
+ ) ) ;
3116
+ let rv = stm. add_device_listener ( self . input_alive_listener . as_ref ( ) . unwrap ( ) ) ;
3117
+ if rv != NO_ERR {
3118
+ self . input_alive_listener = None ;
3119
+ cubeb_log ! ( "AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv={}, device id ={}" , rv, self . input_device. id) ;
3120
+ return Err ( Error :: error ( ) ) ;
3121
+ }
2988
3122
}
2989
3123
}
2990
3124
@@ -3043,6 +3177,7 @@ impl<'ctx> CoreStreamData<'ctx> {
3043
3177
self . output_source_listener. is_none( )
3044
3178
&& self . input_source_listener. is_none( )
3045
3179
&& self . input_alive_listener. is_none( )
3180
+ && self . output_alive_listener. is_none( )
3046
3181
) ;
3047
3182
return Ok ( ( ) ) ;
3048
3183
}
@@ -3061,6 +3196,15 @@ impl<'ctx> CoreStreamData<'ctx> {
3061
3196
self . output_source_listener = None ;
3062
3197
}
3063
3198
3199
+ if self . output_alive_listener . is_some ( ) {
3200
+ let rv = stm. remove_device_listener ( self . output_alive_listener . as_ref ( ) . unwrap ( ) ) ;
3201
+ if rv != NO_ERR {
3202
+ cubeb_log ! ( "AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDeviceIsAlive rv={}, device id={}" , rv, self . input_device. id) ;
3203
+ r = Err ( Error :: error ( ) ) ;
3204
+ }
3205
+ self . output_alive_listener = None ;
3206
+ }
3207
+
3064
3208
if self . input_source_listener . is_some ( ) {
3065
3209
let rv = stm. remove_device_listener ( self . input_source_listener . as_ref ( ) . unwrap ( ) ) ;
3066
3210
if rv != NO_ERR {
@@ -3249,6 +3393,13 @@ impl<'ctx> AudioUnitStream<'ctx> {
3249
3393
kAudioObjectUnknown
3250
3394
} ;
3251
3395
3396
+ let has_output = !self . core_stream_data . output_unit . is_null ( ) ;
3397
+ let output_device = if has_output {
3398
+ self . core_stream_data . output_device . id
3399
+ } else {
3400
+ kAudioObjectUnknown
3401
+ } ;
3402
+
3252
3403
self . core_stream_data . close ( ) ;
3253
3404
3254
3405
// Reinit occurs in one of the following case:
@@ -3270,13 +3421,15 @@ impl<'ctx> AudioUnitStream<'ctx> {
3270
3421
// Always use the default output on reinit. This is not correct in every
3271
3422
// case but it is sufficient for Firefox and prevent reinit from reporting
3272
3423
// 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
- } ) ?;
3424
+ if has_output {
3425
+ self . core_stream_data . output_device = create_device_info ( output_device, DeviceType :: OUTPUT ) . map_err ( |e| {
3426
+ cubeb_log ! (
3427
+ "({:p}) Create output device info failed. This can happen when last media device is unplugged" ,
3428
+ self . core_stream_data. stm_ptr
3429
+ ) ;
3430
+ e
3431
+ } ) ?;
3432
+ }
3280
3433
3281
3434
if self . core_stream_data . setup ( ) . is_err ( ) {
3282
3435
cubeb_log ! (
@@ -3321,6 +3474,36 @@ impl<'ctx> AudioUnitStream<'ctx> {
3321
3474
Ok ( ( ) )
3322
3475
}
3323
3476
3477
+ // Stop (and destroy) the stream and fire an error state changed callback in a new thread.
3478
+ fn report_error_async ( & mut self ) {
3479
+ let queue = self . queue . clone ( ) ;
3480
+ let mutexed_stm = Arc :: new ( Mutex :: new ( self ) ) ;
3481
+ let also_mutexed_stm = Arc :: clone ( & mutexed_stm) ;
3482
+ queue. run_async ( move || {
3483
+ let mut stm_guard = also_mutexed_stm. lock ( ) . unwrap ( ) ;
3484
+ let stm_ptr = * stm_guard as * const AudioUnitStream ;
3485
+ if stm_guard. destroy_pending . load ( Ordering :: SeqCst ) {
3486
+ cubeb_log ! (
3487
+ "({:p}) stream pending destroy, cancelling error report" ,
3488
+ stm_ptr
3489
+ ) ;
3490
+ return ;
3491
+ }
3492
+
3493
+ if !stm_guard. shutdown . load ( Ordering :: SeqCst ) {
3494
+ stm_guard. core_stream_data . stop_audiounits ( ) ;
3495
+ }
3496
+ debug_assert ! (
3497
+ !stm_guard. core_stream_data. input_unit. is_null( )
3498
+ || !stm_guard. core_stream_data. output_unit. is_null( )
3499
+ ) ;
3500
+ stm_guard. core_stream_data . close ( ) ;
3501
+ stm_guard. notify_state_changed ( State :: Error ) ;
3502
+
3503
+ stm_guard. switching_device . store ( false , Ordering :: SeqCst ) ;
3504
+ } ) ;
3505
+ }
3506
+
3324
3507
fn reinit_async ( & mut self ) {
3325
3508
if self . reinit_pending . swap ( true , Ordering :: SeqCst ) {
3326
3509
// A reinit task is already pending, nothing more to do.
0 commit comments