Проблемы с потоковой передачей звука с микрофона между устройствами с использованием Multipeer Connectivity - PullRequest
0 голосов
/ 15 марта 2020

Я успешно настроил основы Multipeer Connectivity, и мои два устройства распознают и соединяются друг с другом как одноранговые узлы. Однако у меня возникают проблемы с потоковой передачей и воспроизведением звука с использованием AVAudioPlayerNode. У меня есть функция StartRecording(), вызываемая при переключении кнопки, и она должна открывать и закрывать OutputStream.

В общем, я понимаю, что вам нужно создать AVAudioEngine, присоединить AVAudioPlayerNode, подключить AVAudioPlayerNode для основного микшера или выходного узла (?). Затем с отправляющего устройства запустите outputStream и installTap() на inputNode Audio Engine, который будет непрерывно отправлять байты, преобразованные из AVAudioPCMBuffer.

В функциях Multipeer, один раз поток данные получены, я установил ViewController inputStream, который, я думаю, обрабатывается с использованием функций StreamDelegate. Входящие байты должны быть обработаны (если есть место). Код кажется функциональным, но .hasBytesAvailable редко вызывается. У меня также включен доступ к микрофону на обоих устройствах.

Поскольку весь код в этом посте трудно уместить, я создал publi c gist с соответствующими файлами. Вот соответствующие части из 3 основных классов: ViewController.swift, MultipeerHandler.swift, StreamHelper.swift:

В ViewController.swift:

override func viewDidLoad() {
    super.viewDidLoad()
    configureAudioSession()
    audioEngine = AVAudioEngine()
    inputNode = audioEngine.inputNode
    audioFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: false)
    multipeerSetup()
    viewSetup()
}

private func configureAudioSession() {
    do {
        try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker)
        try audioSession.setMode(.voiceChat)
        try audioSession.setActive(true)

        audioSession.requestRecordPermission() { [unowned self] (allowed: Bool) -> Void in
            DispatchQueue.main.async {
                if allowed {
                    print("allowed")
                }
            }
        }
    } catch { }
}

@objc func startRecording() throws {
    // start streaming
    if !(self.isRecording) {
        audioEngine.stop()
        let inputFormat = inputNode.inputFormat(forBus: 0)

        audioEngine.attach(audioPlayer)
        audioEngine.connect(audioPlayer, to: mainMixer, format: audioFormat)
        // audioEngine.connect(audioPlayer, to: audioPlayer.outputNode, format: inputFormat)

        do {
            if multipeerHandler.session.connectedPeers.count > 0 {
                if outputStream != nil {
                    outputStream = nil
                }

                outputStream = try multipeerHandler.session.startStream(withName: "voice", toPeer: multipeerHandler.session.connectedPeers[0])
                outputStream.schedule(in: RunLoop.main, forMode: .default)
                outputStream.delegate = self
                outputStream.open()

                inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(inputFormat.sampleRate/10), format: inputFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in

                    let convertedFrameCount = AVAudioFrameCount((Double(buffer.frameLength) / inputFormat.sampleRate) * inputFormat.sampleRate)
                    guard let pcmBuffer = AVAudioPCMBuffer(pcmFormat: inputFormat, frameCapacity: convertedFrameCount) else {
                        print("cannot make pcm buffer")
                        return
                    }

                    print("\(#line)")

                    let bytes = StreamHelper.copyAudioBufferBytes(pcmBuffer)
                    if(self.outputStream.hasSpaceAvailable){
                        self.outputStream.write(bytes, maxLength: bytes.count)
                        print("\(#line)")
                    }
                }
                audioEngine.prepare()
                try audioEngine.start()

            } else {
                print("no peers to connect to")
            }
        } catch let error {
            print(error.localizedDescription)

        }
        self.isRecording = true
    } else {
        // stop streaming
        inputNode.removeTap(onBus: 0)
        self.isRecording = false
    }
}

В MultipeerHelper.swift:

func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {
    print("didReceiveStream")
    if streamName == "voice" {
        print(#line)
        viewController.inputStream = stream
        viewController.inputStream.delegate = viewController
        viewController.inputStream.schedule(in: RunLoop.main, forMode: .default)
        viewController.inputStream.open()
    }
}

Также важно отметить, что я нашел много похожих постов, но они несколько старые. Вот некоторые источники, которые я использовал, чтобы понять, что происходит:

Еще раз, я опубликовал publi c gist для этого запроса. Любая помощь будет принята с благодарностью.

...