在iPhone程序中使用后台音乐播放和VoIP

原文: http://blog.toshsoft.de/index.php?/archives/4-Adding-Background-Audio-and-VoIP-to-your-iPhone-App.html

 

Oct 5. 2010

今天,我会贴出关于iOS 后台任务中的Audio和VoIP的问题,也许你也会遇到同我一样的问题。

本教程使用C库处理sockets,而没有使用苹果的高级API。如果你向用苹果的API,请阅读苹果文档这里

首先,你需要在Info.plist文件中添加新的键UIBackgroundModes,表示你的后台任务类型,包括这两个值:

audio
voip

此外你需要保证,当你从后台恢复时所有的UDP sockets必须被重新open,因为它们不再有效,你会收到SIGPIPE错误。处理SIGPIPE的方法不止一个,其中一个方法就是设置socket选项,让它忽略SIGPIPE。

  1.  
  2.         // Ignore SIGPIPE because you will get it sometimes after going into zhe
  3.         int set = 1;
  4.         setsockopt(m_bsdSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(set));
  5.  

 

setsockopt应该在socket实例创建之后就调用。

你也可以用异常处理的办法简单地忽略SIGPIPE(Matt Gallagher 有一个很好的例子 这里)。在StackOverflow上也能找到大量的源代码。无论如何你都要进行处理。如果你尝试读取一个断开的socket,你会得到EPIPE、ECONNRESET或ENOTCONN错误。

现在进入正题。在进行tcp socket连接之后,我们将一个读取流关联上去,然后将它(流)设置为kCFStreamNetworkServiceTypeVoIP,以便iOS认为它应该在socket上监听。现在,socket会报告收到数据,哪怕App进入后台,电话仍然是被监听的。示例代码如下:

  1.  
  2. #if defined(__IPHONE_4_0) && !(TARGET_IPHONE_SIMULATOR)
  3.         // A read stream handle
  4.         CFReadStreamRef hReadStream;
  5.  
  6.         // Only do this if it is a SipSocket we are watching
  7.         if (IsSipSocket(bsdSocket))
  8.         {
  9.                 // Set it to non-blocking
  10.                 int set = 1;
  11.                 ioctl(bsdSocket, FIONBIO, reinterpret_cast<int>(&set));
  12.                
  13.                 CFStreamCreatePairWithSocket (kCFAllocatorDefault, bsdSocket,
  14.                                                         &hReadStream, NULL);
  15.                 if (CFReadStreamSetProperty(hReadStream,
  16.                                                         kCFStreamNetworkServiceType,
  17.                                                         kCFStreamNetworkServiceTypeVoIP) != TRUE)
  18.                 {
  19.                         // An error occured, delete the stream
  20.                         if(hReadStream != NULL)
  21.                         {
  22.                                 CFReadStreamClose(hReadStream);
  23.                                 CFRelease(hReadStream);
  24.                                 hReadStream = NULL;
  25.                         }
  26.                        
  27.                         return -1;
  28.                 }
  29.                
  30.                 if (CFReadStreamOpen(hReadStream) != TRUE)
  31.                 {
  32.                         // An error occured, delete the stream
  33.                         if(hReadStream != NULL)
  34.                         {
  35.                                 CFReadStreamClose(hReadStream);
  36.                                 CFRelease(hReadStream);
  37.                                 hReadStream = NULL;
  38.                         }
  39.  
  40.                         return -1;
  41.                 }
  42.         }
  43. #endif
  44.  

重启或关闭socket时,对应的读取流也应被关闭。

  1.  
  2. #if defined(__IPHONE_4_0) && !(TARGET_IPHONE_SIMULATOR)
  3.         if (hReadStream)
  4.         {
  5.                 CFReadStreamClose(hReadStream);
  6.                 CFRelease(hReadStream);
  7.                 hReadStream = NULL;
  8.         }
  9. #endif
  10.  

这确实很简单,但我花了许多时间才抓住问题的关键。如往常一样,你可以随意使用和修改这些代码,但如果你觉得它们对你有用,或者发现错误,请留下你的意见。

 


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一个简单的录音和播放程序。请按照以下步骤操作: 1. 创建一个新的 Xcode 项目,选择 Single View App 模板,语言选择 Swift。 2. 在 Main.storyboard 添加两个按钮(录音和播放)和一个标签,用于显示录音和播放状态。 3. 在 ViewController.swift 文件添加以下代码: ```swift import UIKit import AVFoundation class ViewController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate { @IBOutlet weak var statusLabel: UILabel! var audioRecorder: AVAudioRecorder? var audioPlayer: AVAudioPlayer? override func viewDidLoad() { super.viewDidLoad() setupRecorder() } func setupRecorder() { let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(AVAudioSession.Category.playAndRecord) try audioRecorder = AVAudioRecorder(url: getFileURL(), settings: getAudioSettings()) audioRecorder?.delegate = self audioRecorder?.prepareToRecord() } catch { print("Error setting up audio recorder: \(error.localizedDescription)") } } func getFileURL() -> URL { let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let audioURL = documentsDirectory.appendingPathComponent("recording.caf") return audioURL } func getAudioSettings() -> [String: Any] { let settings = [ AVFormatIDKey: NSNumber(value: kAudioFormatAppleLossless), AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue, AVEncoderBitRateKey: 320000, AVNumberOfChannelsKey: 2, AVSampleRateKey: 44100.0 ] return settings } @IBAction func startRecording(_ sender: UIButton) { if audioRecorder?.isRecording == false { let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setActive(true) audioRecorder?.record() statusLabel.text = "Recording..." } catch { print("Error starting recording: \(error.localizedDescription)") } } } @IBAction func stopRecording(_ sender: UIButton) { if audioRecorder?.isRecording == true { audioRecorder?.stop() let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setActive(false) statusLabel.text = "Recording stopped" } catch { print("Error stopping recording: \(error.localizedDescription)") } } } @IBAction func playRecording(_ sender: UIButton) { if audioPlayer?.isPlaying == false { do { try audioPlayer = AVAudioPlayer(contentsOf: getFileURL()) audioPlayer?.delegate = self audioPlayer?.play() statusLabel.text = "Playing..." } catch { print("Error playing audio: \(error.localizedDescription)") } } } func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if flag { statusLabel.text = "Recording saved" } else { statusLabel.text = "Recording failed" } } func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { if flag { statusLabel.text = "Playback finished" } else { statusLabel.text = "Playback failed" } } } ``` 这段代码包括了以下内容: - 创建 AVAudioRecorder 和 AVAudioPlayer 实例来处理录音和播放 - 设置录音的 URL 和参数 - 设置录音和播放的按钮动作 - 显示录音和播放状态的标签 - 实现 AVAudioRecorderDelegate 和 AVAudioPlayerDelegate 来处理录音和播放完成后的事件 4. 运行应用程序并在iPhone测试录音和播放功能。 请注意,这只是一个简单的示例,您可以根据您的需求进行修改和扩展。同时,您需要确保在应用程序的 Info.plist 文件添加了麦克风和音频播放权限,否则应用程序将无法访问麦克风或播放音频文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值