从WKWebView AVAudioSession运行的WebRTC开发障碍



在过去的几年里,我使用SIP协议稳步开发了一个完整的基于WebRTC的浏览器Phone。主要的SIP工具箱是SIPJS(https://sipjs.com/),它提供了向您自己的基于SIP的PBX拨打和接听电话所需的所有工具。

浏览器手机项目:https://github.com/InnovateAsterisk/Browser-Phone/为SIPJS提供了完整的功能和UI。你只需在浏览器中导航到手机并开始使用它。一切都会完美运行。

移动

苹果公司终于允许在WKWebView上使用WebRTC(getUserMedia()(,所以没过多久,人们就开始问它在移动设备上如何工作。虽然UI非常适合手机和平板电脑,但现在仅仅是UI还不足以成为一个完整的解决方案。

主要考虑的是,移动应用程序的使用寿命通常很短,因为你不能或不会像在电脑上使用浏览器那样让它在后台运行。这给真正使浏览器手机对移动友好带来了一些挑战。iOS会想关闭应用程序,因为它不是最前端的应用程序-这是正确的;推送通知。这允许应用程序被唤醒,以便它可以接受呼叫并通知用户。

请记住,这个应用程序是通过打开UIViewController、添加WKWebView并导航到电话页面来创建的。应用程序和html&Javascript,因此事件可以来回传递。

WKWebView&AVAudioSession问题:

在阅读了大量未解决的论坛帖子后,很明显AVAudioSession.sharedInstance()根本没有连接到WKWebView,或者存在一些未记录的连接。

结果是,如果呼叫从应用程序启动,并发送到后台,麦克风将被禁用。很明显,如果你在打电话,这不是一个选择。现在,我可以通过在应用程序发送到后台时暂停调用来稍微管理一下这个限制,尽管这会让用户感到困惑,用户体验也会很差。

然而,真正的问题是,如果应用程序被Callkit唤醒,因为应用程序从未进入前台(因为Callkit是(,麦克风一开始就不会激活,即使你切换了应用程序,它也不会激活。这只是一种无法接受的用户体验

我发现有趣的是,如果你只需在iOS(15.x(上打开Safari浏览器,然后导航到手机页面:https://www.innovateasterisk.com/phone/(不需要在xCode中制作应用程序并将其加载到WKWebView中(,当应用程序发送到后台时,麦克风将继续工作。那么Safari是如何做到这一点的呢?当然,这并不能也不能解决CallKit问题,但看到Safari可以在后台使用麦克风仍然很有趣,因为Safari是基于WKWebView构建的。

(我读到了关于权利的文章,这可能需要特别批准……我不确定这是怎么回事?(

AVAudioSession的下一个问题是,由于无法访问WkWebView的会话,因此无法更改<audio>元素的输出,因此无法将其从扬声器更改为耳机,也无法使其使用蓝牙设备。

使用过时的WebRTC SDK(谷歌不再维护WebRTC iOS SDK(重新开发整个应用程序,然后像SIPJS一样构建我自己的Swift SIP堆栈,并获得两组代码来维护,这是不可行的。。。所以我的主要问题是:

  1. 如何访问WKWebView的AVAudioSession以便设置输出路径/设备
  2. 当应用程序发送到后台时,如何使麦克风保持活动状态
  3. 当Callkit激活应用程序时(当应用程序处于后台时(,我如何激活麦克风

对于1(也许有人也在遵循这种方法,并可以添加一些见解/纠正错误的假设:WebRTC站点中的音频表示为Mediastream。也许可以在没有WKWebView的情况下从中获取该流,并以某种方式在应用程序中播放?这个代码应该传递一些缓冲区,但当它们以swift:到达时,它们是空的

//javascript
...
someRecorder = new MediaRecorder(audioStream);
someRecorder.ondataavailable = async (e) =>
{
window.webkit.messageHandlers.callBackMethod.postMessage(await e.data.arrayBuffer());
}
mediaRecorder.start(1000);

然后在swift中像一样接收

//swift
import UIKit
import WebKit
class ViewController: UIViewController, WKScriptMessageHandler {
...
let config = WKWebViewConfiguration()
config.userContentController = WKUserContentController()
config.userContentController.add(self, name: "callBackMethod")
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 10, height: 10), configuration: config)
...
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
addToPlayingAudioBuffer(message.body)
//print(message.body) gives the output "{}" every 1000ms.
}

最新更新