MCSessionDelegate — получение байтов при получении потока

я новичок в Swift/iOS, только начал изучать это пару дней назад. Я использую Swift 3 и хочу разработать два приложения для iPhone, которые могут отправлять аудиопоток с микрофона на другие устройства iPhone с использованием многорангового подключения. Первое приложение будет приложением говорящего, а второе — приложением слушателя.

Ранее я научился рекламировать, просматривать и приглашать пиров из этого полезного руководства< /а>

и я научился получать аудиоданные с микрофона и преобразовывать их в байты из этого ответ и этот ответить. Большое спасибо Rhythmic Fistman.

Итак, мой код представляет собой комбинацию того, что содержится в этих статьях.

Это ViewController приложения-слушателя

import UIKit
import MultipeerConnectivity
import AVFoundation


class ColorSwitchViewController: UIViewController {

    @IBOutlet weak var connectionsLabel: UILabel!

    let colorService = ColorServiceManager()
    var engine = AVAudioEngine()
    let player = AVAudioPlayerNode()

    // Somewhere, schedule the stream in the mainRunLoop, set the delegate and open it. Choose the peer that you want to connect
    var inputStream = InputStream()
    var inputStreamIsSet: Bool!
    var outputStreamIsSet: Bool!
    public let peerID = MCPeerID(displayName: UIDevice.current.name)

    //MARK: Private Functions
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func copyAudioBufferBytes(_ audioBuffer: AVAudioPCMBuffer) -> [UInt8] {
        let srcLeft = audioBuffer.floatChannelData![0]
        let bytesPerFrame = audioBuffer.format.streamDescription.pointee.mBytesPerFrame
        let numBytes = Int(bytesPerFrame * audioBuffer.frameLength)

        // initialize bytes to 0 (how to avoid?)
        var audioByteArray = [UInt8] (repeating: 0, count: numBytes)

        // copy data from buffer
        srcLeft.withMemoryRebound(to: UInt8.self, capacity: numBytes) { srcByteData in
            audioByteArray.withUnsafeMutableBufferPointer {
                $0.baseAddress!.initialize(from: srcByteData, count: numBytes)
            }
        }

        return audioByteArray
    }

    func bytesToAudioBuffer(_ buf: [UInt8]) -> AVAudioPCMBuffer {
        let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
        let frameLength = UInt32(buf.count) / fmt.streamDescription.pointee.mBytesPerFrame

        let audioBuffer = AVAudioPCMBuffer(pcmFormat: fmt, frameCapacity: frameLength)
        audioBuffer.frameLength = frameLength

        let dstLeft = audioBuffer.floatChannelData![0]

        buf.withUnsafeBufferPointer {
            let src = UnsafeRawPointer($0.baseAddress!).bindMemory(to: Float.self, capacity: Int(frameLength))
            dstLeft.initialize(from: src, count: Int(frameLength))
        }

        return audioBuffer
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        colorService.delegate = self
    }

    @IBAction func redTapped() {
        self.change(color: .red)
        colorService.send(colorName: "red")

    }

    @IBAction func yellowTapped() {
        self.change(color: .yellow)
        colorService.send(colorName: "yellow")
    }

    func change(color : UIColor) {
        UIView.animate(withDuration: 0.2) {
            self.view.backgroundColor = color
        }
    }

}

extension ColorSwitchViewController : ColorServiceManagerDelegate {

    func connectedDevicesChanged(manager: ColorServiceManager, connectedDevices: [String]) {
        OperationQueue.main.addOperation {
            self.connectionsLabel.text = "Connections: \(connectedDevices)"
        }
    }

    func colorChanged(manager: ColorServiceManager, colorString: String) {
        OperationQueue.main.addOperation {
            switch colorString {
            case "red":
                self.change(color: .red)
            case "yellow":
                self.change(color: .yellow)
            default:
                NSLog("%@", "Unknown color value received: \(colorString)")
            }
        }
    }

    func streamReceived(manager: ColorServiceManager, stream: InputStream, streamName: String, fromPeer: MCPeerID) {
        NSLog("%@", "name " + fromPeer.displayName)
        if streamName == "stream" && fromPeer != peerID {

            NSLog("%@", "voice received")

            stream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
            stream.open()

            var bytes = [UInt8](repeating: 0, count: 17640)

            if (stream.hasBytesAvailable == true) {
                NSLog("%@", "has bytes available...")
            } else {
                NSLog("%@", "has NO byte ...")
            }

            let result = stream.read(&bytes, maxLength: bytes.count)
            if result == 0 {
                print("Stream at capacity")
            } else if result == -1 {
                print("Operation failed: \(String(describing: stream.streamError))")
            } else {
                print("The number of bytes read is \(result)")
            }

            let audioBuffer = self.bytesToAudioBuffer(bytes) //Here is where the app crashes

            engine.attach(player)
            let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true)
            engine.connect(player, to: engine.mainMixerNode, format: outputFormat)


            do {
                try engine.start()

                player.scheduleBuffer(audioBuffer, completionHandler: nil)
                player.play()
            } catch let error {
                print(error.localizedDescription)

            }
        }
    }

}

И ViewController приложения динамика похож, за исключением того, что он содержит код для отправки потока и не содержит кода для получения

// ....

    override func viewDidLoad() {
        super.viewDidLoad()
        colorService.delegate = self

    }


    func startStream() {
        let input = engine.inputNode!
        engine.attach(player)

        let bus = 0
        let inputFormat = input.inputFormat(forBus: bus)
        engine.connect(player, to: engine.mainMixerNode, format: inputFormat)

        NSLog("%@", "sendStream: to \(self.colorService.session.connectedPeers.count) peers")

        if self.colorService.session.connectedPeers.count > 0 {
            do {
                let outputStream = try self.colorService.session.startStream(withName: "stream", toPeer: self.colorService.session.connectedPeers.first!)
                outputStream.schedule(in: RunLoop.main, forMode:RunLoopMode.defaultRunLoopMode)
                outputStream.open()

                let bus = 0
                let inputFormat = input.inputFormat(forBus: bus)
                input.installTap(onBus: bus, bufferSize: 2048, format: inputFormat, block: {
                    (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
                    self.player.scheduleBuffer(buffer)
                    let audioBuffer = self.copyAudioBufferBytes(buffer)
//                    NSLog("%@", "speaking...")
                    let result = outputStream.write(audioBuffer, maxLength: audioBuffer.count)
                    if result == 0 {
                        print("Stream at capacity")
                    } else if result == -1 {
                        print("Operation failed: \(String(describing: outputStream.streamError))")
                    } else {
                        print("The number of bytes written is \(result)")
                    }
                })


                try! engine.start()
                player.play()


            }
            catch let error {
                NSLog("%@", "Error for sending: \(error)")
            }
        }

    }

    func stopStream() {
        engine.inputNode?.removeTap(onBus: 0)
        player.stop()
    }

    @IBAction func redTapped() {
        self.change(color: .red)
        colorService.send(colorName: "red")
        self.startStream()
    }

    @IBAction func yellowTapped() {
        self.change(color: .yellow)
        colorService.send(colorName: "yellow")
        self.stopStream()
    }

    // ...

К сожалению, на стороне слушателя я получаю приложение, получающее поток без доступных байтов. NSLog("%@", "has NO byte ...") был вызван. Интересно, действительно ли приложение-слушатель получает аудиопоток или нет.

Итак, в чем моя ошибка? Любая помощь будет оценена по достоинству. Заранее спасибо.


person M Rijalul Kahfi    schedule 06.06.2017    source источник
comment
Вы видите, что говорящий что-то посылает?   -  person John Tracid    schedule 12.06.2017
comment
Могу ли я предложить, прежде чем приступить к обмену звуком, просто проверить, правильно ли работает соединение MC между двумя устройствами, потому что я не вижу никаких MCSession, MCAdvertiserAssistant или MCBrowserViewController для установления MCSession в первую очередь. Можете ли вы поделиться кодом, который это делает?   -  person Jack G.    schedule 13.06.2017