読者です 読者をやめる 読者になる 読者になる

ami_GS's diary

情報系大学院生の備忘録。ネットワークの勉強にハマっています。

UDPで音声通信(その2)挫折編

Python UDP

UDOで音声通信その2です。
タイトルの通り、挫折編というわけで、結論から言うと成果物がうまく動きませんでした!!

許してください!何でもしますから!(ん?)

解決しました!!

以下の通りにですね、クライアント側で録音、サーバ側で再生をするプログラムを書いたわけですが、とある問題があったわけです。

このコードを動かしてPC(主にスピーカー)に障害が出ても自己責任でオナシャス・・・
(強制終了でしか止められません)

Client側

import pyaudio
import socket
from threading import Thread

frames = []

def udpStream():
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    

    while True:
        if len(frames) > 0:
            udp.sendto(frames.pop(0), ("127.0.0.1", 12345))#ポップしてデータ送信

    udp.close()

def record(stream, CHUNK):    
    while True:
        frames.append(stream.read(CHUNK))#録音したデータをキューに詰め込む

if __name__ == "__main__":
    CHUNK = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 2
    RATE = 44100

    p = pyaudio.PyAudio()

    stream = p.open(format = FORMAT,
                    channels = CHANNELS,
                    rate = RATE,
                    input = True,
                    frames_per_buffer = CHUNK,
                    )

    Tr = Thread(target = record, args = (stream, CHUNK,))
    Ts = Thread(target = udpStream)
    Tr.setDaemon(True)
    Ts.setDaemon(True)#メインスレッドが終わってもTr及びTsスレッドが動き続けるように
    Tr.start()
    Ts.start()
    Tr.join()
    Ts.join()

Server側

import pyaudio
import socket
from threading import Thread

frames = []

def udpStream(CHUNK):

    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp.bind(("127.0.0.1", 12345))

    while True:
        soundData, addr = udp.recvfrom(CHUNK)
        frames.append(soundData)

    udp.close()

def play(stream, CHUNK):
    BUFFER = 10
    while True:
            if len(frames) == BUFFER:
                while True:
                    stream.write(frames.pop(0), CHUNK)

if __name__ == "__main__":
    FORMAT = pyaudio.paInt16
    CHUNK = 1024
    CHANNELS = 2
    RATE = 44100

    p = pyaudio.PyAudio()

    stream = p.open(format=FORMAT,
                    channels = CHANNELS,
                    rate = RATE,
                    output = True,
                    frames_per_buffer = CHUNK,
                    )

    Ts = Thread(target = udpStream, args=(CHUNK,))
    Tp = Thread(target = play, args=(stream, CHUNK,))
    Ts.setDaemon(True)
    Tp.setDaemon(True)
    Ts.start()
    Tp.start()
    Ts.join()
    Tp.join()


糞コード申し訳!
Client、Serverともに2つのスレッドを動かし、1つのスレッドでudp通信、もう一方で録音/再生を行っています。
録音したデータをキューに詰め、それをUDPで送っています。受信側もキューを用いています。



今回は失敗した事について述べるので、
UDPに関する説明は次回書くつもりの「UDPで画像通信」で書きますね。




とある問題、というのは所謂パケットロスがあったわけですね。
もう一方から出てくる音のノイズがハンパ無いんですね、ツライ。
ブチブチブチブチブチブチブチ
って感じです(げっそり)




UDPがパケットロスを起こす事については知っていました。
ですが、実は以前一方のPCのカメラで撮った画像を他方のPCに映すコードを書いた事がありまして、そこでは画面のノイズ、途切れは全く気になら無い物であり、「パケットロスってほとんど気づかないレベルなのかぁ・・・」と勝手に思い込んでました。



まぁ、これは画像だからなんですね。
たとえ1ドットのデータが落ちて、そこが白くなっても、動画中の1枚が落ちても「動画」という物ではそこまで不快な物ではないんですよねぇ。

なんでパケットロスに気づいたかというと、
python - UDP sound transfer : played sound have big noise - Stack Overflow
に質問を投稿させていただき、約5分で「そりゃパケットロスだぜHAHAHA」という回答をいただいたからです。



色々調べた末の結論ですが
音声の通信をするには

  • パケットロスした所の音を、前後の音から補完する。
  • UDPではなくVoIPSIPH.323を使う。

という事があげられます。
一つ目については卒論の合間にやる趣味コーディングの範囲を超えるガチ実装になるので、暇なときにやりたい。
二つ目は、Pythonでこれらのプロトコルをサポートできるものがない(あるにはある)。


twistedのようなメジャーなフレームワークにサポートされてれば是非使いたかったのですが、twistedには無い模様です。



というわけで、音声通信についてはもっと勉強が必要であると再認識しました。
画像の方は簡単なので、次回説明していこうと思います。