通过 UDP 协议发送 WAV 文件

SEND A WAV FILE OVER UDP PROTOCOL

我想试验和学习 UDP 协议。所以我开始编写服务器代码,客户端将一个 .wav 文件从客户端发送到服务器,并在我收到样本时将其输出到我的耳机上。我使用以下代码遇到了一些非常奇怪的行为

客户端

import socket
import wave
import time as ti
sock=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

server_address2 = ('192.168.0.196',40000)


BUFFER_SIZE = 1024

wf = wave.open(r'path\song.wav','rb')   

data = wf.readframes(BUFFER_SIZE)
print(len(data))
sent = sock.sendto(data,server_address2)


    #response,addr = sock.recvfrom(1024)

while data!=b'':

        data = wf.readframes(BUFFER_SIZE)
        sent = sock.sendto(data,server_address2)    
        ti.sleep(0.04)

服务器

from scipy.io import wavfile
import socket 
import pyaudio
import time
import struct 
import wave 
import numpy as np

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('192.168.0.196',40000)
print('starting up on %s port %s' %(server_address, str(40000)))

sock.bind(server_address)


# BUFFER_SIZE = 1

# #OUTPUT FILE OPTIONS 


# # Opening audio file as binary data
wf = wave.open(r'path\song.wav', 'rb')

# # Instantiate PyAudio
p = pyaudio.PyAudio()
file_sw = wf.getsampwidth()
# print(file_sw)
print("channels: " ,wf.getnchannels())
print("sampwidth: ",wf.getsampwidth())
print("framerate: " ,wf.getframerate())
print("p.get_format_from_width(file_sw): ", p.get_format_from_width(file_sw))

televizor = 7
casti = 5
stream = p.open(format=p.get_format_from_width(file_sw),
                channels=1,
                rate=22050,
                output_device_index=casti,
                output=True
                #stream_callback = callback
                )



#sock.settimeout(0.3)
while True:
    try:

        data, addr = sock.recvfrom(2048*2*2)     

        stream.write(data)
        print(data)





    except KeyboardInterrupt:
        break

wav 的样本传输到服务器,一秒钟听起来还不错,但随后速度加快,一首 1 分钟长的歌曲在 2 秒内结束

即使我在客户端上写 time.sleep(任意数字)以更慢地发送这些样本,它只会变得越来越快。这是什么行为?

发生的情况是您的网络将音频数据传输到服务器的速度比服务器的声卡播放音频数据的速度快得多。

正因为如此(再加上 UDP 没有任何流量控制的概念),您服务器的传入 UDP 数据包缓冲区很快就会填满(而您的服务器在 stream.write(data) 中被阻塞等待一段音频播放),此时任何额外的传入 UDP 数据包都会被服务器的网络堆栈静默丢弃(这是 UDP 的预期行为)。

如果您想要接收大部分或全部 UDP 数据包,您需要对您的服务器进行编码,使其始终及时调用 recvfrom(),以便它可以拉入传入UDP 数据包在缓冲区填满之前从套接字的接收缓冲区中发出。 (请注意,即使那样也不能保证您会收到所有数据,因为 UDP 数据包也可能由于其他原因而被丢弃)。

实现此目的的一种方法是在服务器上的单独线程中处理网络和音频播放,这样音频播放就不会延迟网络数据接收。或者,您可以使用 select() 或类似方法对两个流进行多路复用(取决于 PyAudio 的实现方式;我从未使用过 API 所以我不能肯定地说)。

或者,您可以减慢客户端的速度,使其发送音频的速率与服务器消耗它的速率相同(即每秒 22050*4 字节,或任何实际结果),尽管即使那样也只会有所帮助对于短音频文件,由于时钟漂移等原因,最终音频会在服务器端运行不足或过载