在 Swift 中使用 AVAudioRecorder 在后台恢复录制的问题

如何解决在 Swift 中使用 AVAudioRecorder 在后台恢复录制的问题

在上下文中,我正在开发一个应用程序,该应用程序在后台或应用程序中记录大量时间。我在使用 AVAudioRecorder 在我的应用中录制时遇到两个问题:

  • 例如,当我在 WhatsApp 中播放音频或在 Instagram 中播放视频时,我的录音恢复正常。但是当我在 Apple Music 中播放歌曲或播放语音笔记时,它不会再次恢复录音。

  • 如果我正在接听电话并开始录音,我的应用会崩溃并输出以下内容:

    (OSStatus 561017449 错误。)

  • 我认为还需要处理输入/输出设备发生变化时的情况,以免我的应用程序崩溃。

我为我正在录制的课程(以及我使用录音机播放声音,以便我可以尝试看看它是否有效)实现的代码是这样的:

class RecordViewController: UIViewController,AVAudioPlayerDelegate,AVAudioRecorderDelegate {


@IBOutlet weak var principalLabel: UILabel!
@IBOutlet weak var loginLabel: UILabel!
@IBOutlet weak var recordBTN: UIButton!
@IBOutlet weak var playBTN: UIButton!

var audioRecorder : AVAudioRecorder!
var audioPlayer : AVAudioPlayer!

var isAudioRecordingGranted: Bool!
var isRecording = false
var isPlaying = false

override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .white
    check_record_permission()
    checkActivateMicrophone()
    
    NotificationCenter.default.addObserver(self,selector: #selector(handleInterruption(notification:)),name: AVAudioSession.interruptionNotification,object: audioRecorder)
    
    let tapRegister: UITapGestureRecognizer = UITapGestureRecognizer(target: self,action: #selector(self.goToLogin))
    loginLabel.addGestureRecognizer(tapRegister)
    loginLabel.isUserInteractionEnabled = true
}

@objc func goToLogin() {
    self.performSegue(withIdentifier: "goToLogin",sender: self)
}

@objc func handleInterruption(notification: Notification) {
    guard let userInfo = notification.userInfo,let typeInt = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,let type = AVAudioSession.InterruptionType(rawValue: typeInt) else {
            return
    }

    switch type {
    
    case .began:
        
        if isRecording {
            print("OOOOOOO")
            audioRecorder.pause()
        }
        
        break
    case .ended:
        guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
        let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
        if options.contains(.shouldResume) {
            audioRecorder.record()
            print("AAAAAAA")
        } else {
            print("III")
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.audioRecorder.record()
            }
        }

    default: ()
    }
}

func check_record_permission()
{
    switch AVAudioSession.sharedInstance().recordPermission {
    case AVAudioSessionRecordPermission.granted:
        isAudioRecordingGranted = true
        break
    case AVAudioSessionRecordPermission.denied:
        isAudioRecordingGranted = false
        break
    case AVAudioSessionRecordPermission.undetermined:
        AVAudioSession.sharedInstance().requestRecordPermission({ (allowed) in
                if allowed {
                    self.isAudioRecordingGranted = true
                } else {
                    self.isAudioRecordingGranted = false
                }
        })
        break
    default:
        break
    }
}

func checkActivateMicrophone() {
    if !isAudioRecordingGranted {
        playBTN.isEnabled = false
        playBTN.alpha = 0.5
        recordBTN.isEnabled = false
        recordBTN.alpha = 0.5
        principalLabel.text = "Activa el micrófono desde ajustes"
    } else {
        playBTN.isEnabled = true
        playBTN.alpha = 1
        recordBTN.isEnabled = true
        recordBTN.alpha = 1
        principalLabel.text = "¡Prueba las grabaciones!"
    }
}

func getDocumentsDirectory() -> URL
{
    let paths = FileManager.default.urls(for: .documentDirectory,in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

func getFileUrl() -> URL
{
    let filename = "myRecording.m4a"
    let filePath = getDocumentsDirectory().appendingPathComponent(filename)
    return filePath
}

func setup_recorder()
{
    if isAudioRecordingGranted
    {
        let session = AVAudioSession.sharedInstance()
        do
        {
            try session.setCategory(AVAudioSession.Category.playAndRecord,options: .defaultToSpeaker)
            try session.setActive(true)
            let settings = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),AVSampleRateKey: 44100,AVNumberOfChannelsKey: 2,AVEncoderAudioQualityKey:AVAudioQuality.high.rawValue
            ]
            audioRecorder = try AVAudioRecorder(url: getFileUrl(),settings: settings)
            audioRecorder.delegate = self
            audioRecorder.isMeteringEnabled = true
            audioRecorder.prepareToRecord()
        }
        catch let error {
            print(error.localizedDescription)
        }
    }
    else
    {
        print("AAAAA")
    }
}


@IBAction func recordAct(_ sender: Any) {
            
    if(isRecording) {
        finishAudioRecording(success: true)
        recordBTN.setTitle("Record",for: .normal)
        playBTN.isEnabled = true
        isRecording = false
    }
    else
    {
        setup_recorder()
        audioRecorder.record()//aaaaaa
        recordBTN.setTitle("Stop",for: .normal)
        playBTN.isEnabled = false
        isRecording = true
    }
}

func finishAudioRecording(success: Bool)
{
    if success
    {
        audioRecorder.stop()
        audioRecorder = nil
        print("recorded successfully.")
    }
    else
    {
        print("Recording failed.")
    }
}


func prepare_play()
{
    do
    {
        audioPlayer = try AVAudioPlayer(contentsOf: getFileUrl())
        audioPlayer.delegate = self
        audioPlayer.prepareToPlay()
    }
    catch{
        print("Error")
    }
}

@IBAction func playAct(_ sender: Any) {
    
    if(isPlaying)
        {
            audioPlayer.stop()
            recordBTN.isEnabled = true
            playBTN.setTitle("Play",for: .normal)
            isPlaying = false
        }
        else
        {
            if FileManager.default.fileExists(atPath: getFileUrl().path)
            {
                recordBTN.isEnabled = false
                playBTN.setTitle("pause",for: .normal)
                prepare_play()
                audioPlayer.play()
                isPlaying = true
            }
            else
            {
                print("Audio file is missing.")
            }
        }
}

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder,successfully flag: Bool)
{
    if !flag
    {
        print("UUU")
        finishAudioRecording(success: false)
    }
    playBTN.isEnabled = true
}

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer,successfully flag: Bool)
{
    recordBTN.isEnabled = true
}
}

我实现了音频、AirPlay 和画中画的“背景模式”

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-