Сначала небольшая справочная информация для объяснения моей мотивации: у меня есть приложение Qt / C ++ / Objective-C ++, которое использует CoreAudio / AVFoundation для получения входящего звука с указанных аудиовходов на Mac, изменения звука, а затемвоспроизведите измененный звук обратно через некоторые указанные аудиовыходы.Все это работало нормально до Мохаве и Каталины, после чего новые ограничения конфиденциальности Apple в отношении микрофонов не позволили ему больше получать входящий звук (вместо этого он получал только нули / тишины из-за отсутствия явного разрешения пользователя на использование.микрофон).
Чтобы исправить это, я добавил код для перехода через новое разрешение «получить пользователя» hoops (то есть добавил тег NSMicrophoneUsageDescription
в Info.plist,добавлены вызовы к authorizationStatusForMediaType
и requestAccessForMediaType
, как предложено, и т. д.), и теперь мое приложение снова работает, как и ожидалось, при запуске из его значка (т. е. оно выдает «MyAudioProcessingApp хочет использовать запросчик микрофона», и как только пользовательв ответ на панель управления «Безопасность и конфиденциальность / Конфиденциальность / Микрофон» появляется флажок для моего приложения, который определяет, может ли мое приложение прослушивать входящее аудио).Это все работает нормально, насколько это возможно.
Моя проблема заключается в следующем - мое приложение также имеет функцию «фонового режима», где пользователь может попросить приложение установить себя как систему без графического интерфейса пользователя.-service (который запускается при загрузке через launchd / launchctl), так что он будет выполнять свою обработку звука в фоновом режиме, как только Mac будет загружен (то есть, не требуя, чтобы кто-либо входил в систему или запускал приложение вручную).Это очень полезно для людей, которые хотят запустить это приложение на «безголовом / встроенном» компьютере Mac в рамках установки стационарного аудио, где все, что нужно, это включить Mac, чтобы он начал обрабатывать аудио.
Однако я обнаружил, что, когда мое приложение работает в фоновом режиме, [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]
всегда возвращает AVAuthorizationStatusDenied
, даже если пользователь ранее дал разрешение моему приложению на доступ к микрофону.Это происходит, даже несмотря на то, что эффективный идентификатор пользователя процесса совпадает с идентификатором пользователя, давшего микрофонное разрешение, и выполняемый исполняемый файл является тем же файлом, что и тот, который ранее генерировал запрос на разрешение, с которым согласился пользователь.
У меня такой вопрос, нужен ли какой-то особенный прием, чтобы получить доступ к микрофону во время работы в фоновом режиме?Или Apple решила, что демоны launchctl-launchdl просто не могут получить доступ к микрофону ни при каких обстоятельствах, и поэтому мне не повезло?
ps файл MyAudioProcessingApp.app/Contents/Info.plist
моего приложения и файл /Library/LaunchDaemons/com.mycompany.myprogram.plist
(обаслегка анонимизированы) ниже, в случае их актуальности:
----- begin MyProcessingApp.app/Contents/Info.plist ------- snip ------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>MyAudioProcessingApp</string>
<key>CFBundleGetInfoString</key>
<string>Created by Qt/QMake</string>
<key>CFBundleIconFile</key>
<string>vcore.icns</string>
<key>CFBundleIdentifier</key>
<string>com.mycompany.MyAudioProcessingApp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSMinimumSystemVersion</key>
<string>10.10</string>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
<key>NSMicrophoneUsageDescription</key>
<string>To allow MyAudioProcessingApp to process incoming audio data.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
----- end MyProcessingApp.app/Contents/Info.plist ------- snip ------
---- begin /Library/LaunchDaemons/com.mycompany.myprogram.plist ------ snip ------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "
http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:</string>
</dict>
<key>Label</key>
<string>com.mycompany.MyAudioProcessingApp</string>
<key>Program</key>
<string>/Library/MyCompany/MyAudioProcessingApp/run_my_program_in_background.sh</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/myprogram.stdout</string>
<key>StandardErrorPath</key>
<string>/tmp/myprogram.stderr</string>
<key>UserName</key>
<string>jaf</string> // NOTE: this is set dynamically to the correct user as part of the install-as-service step
<key>ProcessType</key>
<string>Interactive</string>
<key>GroupName</key>
<string>admin</string>
<key>InitGroups</key>
<true/>
</dict>
</plist>
---- end /Library/LaunchDaemons/com.mycompany.myprogram.plist ------ snip ------
---- begin /Library/MyCompany/MyAudioProcessingApp/run_my_program_in_background.sh ------ snip ------
#!/bin/bash
PATH_TO_MYPROGRAM_EXE="/Library/MyCompany/MyAudioProcessingApp/MyAudioProcessingApp.app/Contents/MacOS/MyAudioProcessingApp"
"$PATH_TO_MYPROGRAM_EXE" run_without_gui
exit 0
---- end /Library/MyCompany/MyAudioProcessingApp/run_my_program_in_background.sh ------ snip ------