我想尝试和学习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'pathsong.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'pathsong.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 字节,或任何结果(发送音频,尽管即使这样也只对短音频文件有帮助,因为最终音频会由于时钟漂移而在服务器端运行不足或溢出, 等。