我想尝试并学习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中的样本被传输到服务器,听起来一秒钟不错,但是随后它加快了速度,一秒钟长的歌曲在2秒内结束了
即使我在客户机上写time.sleep(任意数量)以更慢地发送那些样本,它也会变得越来越快。这是什么行为?
答案 0 :(得分:0)
正在发生的情况是,您的网络将音频数据传输到服务器的速度大大快于服务器的音频卡可以播放音频数据的速度。
因此(结合UDP没有任何流控制的概念),服务器的传入UDP数据包缓冲区会迅速填满(而服务器被阻塞在stream.write(data)
内部,等待一小段音频要播放),此时服务器的网络堆栈会静默丢弃任何其他传入的UDP数据包(这是UDP的预期行为)。
如果要接收大多数或所有UDP数据包,则需要对服务器进行编码,使其始终及时调用recvfrom()
,以便它可以提取传入的UDP数据包在套接字的接收缓冲区溢出之前将其移出。 (请注意,即使那样也不能保证您会收到所有数据,因为UDP数据包也可能由于其他原因而被丢弃。)
做到这一点的一种方法是在服务器上的单独线程中处理网络和音频播放,以使音频播放不会延迟网络数据接收。另外,您也许可以使用select()
或类似的方法来复用两个流(取决于PyAudio的实现方式;我从未使用过该API,因此无法确定)。
或者,您可以减慢客户端的速度,使其以与服务器消耗音频相同的速率发送音频(即每秒22050 * 4字节,或无论结果如何),尽管即使这样做只会帮助一小会儿音频文件,因为最终由于时钟漂移等原因,服务器端的音频将欠载或超载。