@@ -41,6 +41,7 @@ class TasksCollectionViewController: UICollectionViewController, UICollectionVie
4141 var captureSession : AVCaptureSession ?
4242 var cameraImage : UIImage ?
4343
44+ private let audioSession = AVAudioSession . sharedInstance ( )
4445 var videoCaptureSession = AVCaptureSession ( )
4546 let movieOutput = AVCaptureMovieFileOutput ( )
4647 var activeInput : AVCaptureDeviceInput !
@@ -921,10 +922,64 @@ class TasksCollectionViewController: UICollectionViewController, UICollectionVie
921922 } )
922923 }
923924
924- //MARK:- Setup Camera
925+ //MARK: - Select Audio Source
926+ // Prefer BT HFP, else fall back to built-in mic
927+ private func configureAudioSessionForVideo( ) throws {
928+ // Let *you* control the audio session (not AVCaptureSession)
929+ videoCaptureSession. automaticallyConfiguresApplicationAudioSession = false
930+
931+ // Category/mode appropriate for video capture; allow BT HFP input
932+ try audioSession. setCategory ( . playAndRecord,
933+ mode: . videoRecording,
934+ options: [ . allowBluetooth, . defaultToSpeaker] )
935+
936+ // HFP is typically 8–16 kHz mono; asking for 16 kHz reduces resampling
937+ try ? audioSession. setPreferredSampleRate ( 16_000 )
938+
939+ // Pick HFP if available; otherwise nil = system default (built-in mic)
940+ if let hfp = audioSession. availableInputs? . first ( where: { $0. portType == . bluetoothHFP } ) {
941+ try audioSession. setPreferredInput ( hfp)
942+ } else {
943+ try audioSession. setPreferredInput ( nil )
944+ }
945+
946+ try audioSession. setActive ( true , options: [ ] )
947+
948+ // (Optional) log the actual route you got
949+ let routeDesc = audioSession. currentRoute. inputs. map { " \( $0. portType. rawValue) \( $0. portName) " } . joined ( separator: " , " )
950+ NSLog ( " Audio route inputs: \( routeDesc) " )
951+ }
952+
953+ // Keep preferring HFP when the route changes (AirPods in/out, etc.)
954+ @objc private func handleRouteChange( _ note: Notification ) {
955+ do {
956+ if let hfp = audioSession. availableInputs? . first ( where: { $0. portType == . bluetoothHFP } ) {
957+ try audioSession. setPreferredInput ( hfp)
958+ } else {
959+ try audioSession. setPreferredInput ( nil ) // fallback (built-in)
960+ }
961+ } catch {
962+ NSLog ( " Audio route change handling failed: \( error) " )
963+ }
964+ }
965+
966+ //MARK: - Setup Camera
925967 func setupSession( position: AVCaptureDevice . Position ) -> Bool {
926- videoCaptureSession. sessionPreset = AVCaptureSession . Preset ( rawValue: convertFromAVCaptureSessionPreset ( AVCaptureSession . Preset. high) )
927- // Setup Camera
968+ videoCaptureSession. sessionPreset = . high
969+
970+ // 1) Configure audio session first (prefer BT HFP, else builtin)
971+ do {
972+ try configureAudioSessionForVideo ( )
973+ NotificationCenter . default. addObserver ( self ,
974+ selector: #selector( handleRouteChange ( _: ) ) ,
975+ name: AVAudioSession . routeChangeNotification,
976+ object: audioSession)
977+ } catch {
978+ NSLog ( " Audio session config failed: \( error) " )
979+ // keep going; we'll still attempt builtin mic via default route
980+ }
981+
982+ // 2) Setup Camera
928983 if let camera = AVCaptureDevice . default ( . builtInWideAngleCamera, for: . video, position: position) {
929984 do {
930985 let input = try AVCaptureDeviceInput ( device: camera)
@@ -933,36 +988,37 @@ class TasksCollectionViewController: UICollectionViewController, UICollectionVie
933988 activeInput = input
934989 }
935990 } catch {
936- NSLog ( " TasksCollectionViewController: Error setting device video input: \( error) " )
991+ NSLog ( " Error setting device video input: \( error) " )
937992 return false
938993 }
994+ } else {
995+ NSLog ( " Front camera not available " )
939996 }
940- else {
941- NSLog ( " TasksCollectionViewController: Front camera not available " )
942- }
943-
944- // Setup Microphone
945- let microphone = AVCaptureDevice . default ( for : AVMediaType ( rawValue : convertFromAVMediaType ( AVMediaType . audio ) ) )
946-
947- do {
948- let micInput = try AVCaptureDeviceInput ( device : microphone! )
949- if videoCaptureSession . canAddInput ( micInput ) {
950- videoCaptureSession . addInput ( micInput )
997+
998+ // 3) Setup Microphone (device is generic; AVAudioSession decides route )
999+ if let microphone = AVCaptureDevice . default ( for : . audio ) {
1000+ do {
1001+ let micInput = try AVCaptureDeviceInput ( device : microphone )
1002+ if videoCaptureSession . canAddInput ( micInput ) {
1003+ videoCaptureSession . addInput ( micInput )
1004+ }
1005+ } catch {
1006+ NSLog ( " Error setting device audio input: \( error ) " )
1007+ return false
9511008 }
952- } catch {
953- NSLog ( " TasksCollectionViewController: Error setting device audio input: \( error) " )
954- return false
1009+ } else {
1010+ NSLog ( " No audio capture device available " )
9551011 }
956-
957- // Movie output
1012+
1013+ // 4) Movie output
9581014 if videoCaptureSession. canAddOutput ( movieOutput) {
9591015 videoCaptureSession. addOutput ( movieOutput)
9601016 }
961-
1017+
9621018 return true
9631019 }
9641020
965- //MARK:- Camera Session
1021+ //MARK: - Camera Session
9661022 func startSession( orientation: AVCaptureVideoOrientation ) {
9671023 NSLog ( " TasksCollectionViewController: startSession() " )
9681024 if !videoCaptureSession. isRunning {
0 commit comments