当音频记录长于30秒左右时,Flask socketio不会断开连接,而仅当Gunicorn在Azure App Services上提供服务时。本地开发人员似乎具有几乎无限的容量,并且较短的录音在Azure环境中可以正常工作。
除voice_disconnect_request()
触发外,该代码似乎实际上行为正确。尽管disconnect()
之后的代码可以运行(即@socketio.on('disconnect', namespace='/voice_enroll') -> voice_test_disconnect()
),所以我不确定emit('my_response',{'data': 'Voice Enroll Complete!', 'count': session['receive_count']})
为什么不起作用。出于某种原因,如果此操作无法触发,$('form#connect').submit(function(event)
将再次运行,这将自动创建新的连接并启动记录器。
站点上的按钮确实正确且立即更改为已禁用/启用,因此似乎“记录”按钮不应处于活动状态,断开连接功能应完全正确,并且不应再次调用$('form#connect').submit(function(event)
。在正确发送了断开连接请求的情况下,一切都会按预期进行。
我尝试在线程中运行voice_enroll()
(代码反映在下面),因为它确实需要几秒钟,并且我担心在那里会有一些延迟。
我为audio_context尝试了各种缓冲区大小。
我更改了各种异步模式和gunicorn设置,但实际上我对尝试其他方法一无所知。任何地方都没有错误消息。似乎只是放弃并重新开始。
我已经尝试过将它们设置为Gunicorn
gunicorn --worker-class eventlet -w 1 --bind '0.0.0.0:8000' app:server --timeout 300
gunicorn --worker-class gevent -w 1 --bind '0.0.0.0:8000' app:server --timeout 300
gunicorn -w 1 --threads 12 --bind '0.0.0.0:8000' app:server --timeout 300
python
@socketio.on('my_event', namespace='/voice_enroll')
def voice_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': message['data'], 'count': session['receive_count']})
@socketio.on('disconnect_request', namespace='/voice_enroll')
def voice_disconnect_request():
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': 'Voice Enroll Complete!', 'count': session['receive_count']})
disconnect()
@socketio.on('connect', namespace='/voice_enroll')
def voice_test_connect():
session['audio'] = []
emit('my_response', {'data': 'Connected', 'count': 0})
@socketio.on('sample_rate', namespace='/voice_enroll')
def voice_handle_my_sample_rate(sampleRate):
session['sample_rate'] = sampleRate
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response', {'data': "sampleRate : %s" % sampleRate, 'count': session['receive_count'] })
@socketio.on('audio', namespace='/voice_enroll')
def voice_handle_my_custom_event(audio):
"""
"""
values = OrderedDict(sorted(audio.items(), key=lambda t: int(t[0]))).values()
session['audio'] += values
@socketio.on('disconnect', namespace='/voice_enroll')
def voice_test_disconnect():
# https://stackoverflow.com/a/18644461/466693
sample_rate = session['sample_rate']
my_audio = np.array(session['audio'], np.float32)
scaled = np.round(32767*np.sin(my_audio))
scipy.io.wavfile.write('out.wav', sample_rate, scaled.astype(np.int16))
audio = AudioSegment.from_file('out.wav', format='wav').set_frame_rate(MS_VOICE_FRAMERATE)
audio.export('temp.wav', format='wav')
thread = Thread(target=enroll_voice, args=('temp.wav', current_user.username))
thread.start()
session['audio'] = []
和
JS
var namespace = '/voice_enroll';
var socket = null;
var mediaStream = null;
// prepare button state
$('#disconnect input')[0].disabled = true;
// audio functions
function initializeRecorder(stream){
// https://stackoverflow.com/a/42360902/466693
mediaStream = stream;
// get sample rate
audio_context = new AudioContext;
sampleRate = audio_context.sampleRate;
console.log('<sample_rate>', sampleRate);
socket.emit('sample_rate', sampleRate);
var audioInput = audio_context.createMediaStreamSource(stream);
console.log("Created media stream.");
var bufferSize = 8192;
// record only 1 channel
var recorder = audio_context.createScriptProcessor(bufferSize, 1, 1);
// specify the processing function
recorder.onaudioprocess = recorderProcess;
// connect stream to our recorder
audioInput.connect(recorder);
// connect our recorder to the previous destination
recorder.connect(audio_context.destination);
}
function recorderProcess(e) {
var left = e.inputBuffer.getChannelData(0);
socket.emit('audio', left);
}
$('form#connect').submit(function(event) {
if(socket == null){
socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
socket.on('connect', function() {
socket.emit('my_event', {data: 'I\'m connected!'});
navigator.getUserMedia({audio: true}, initializeRecorder, function(a, b, c){
console.log(a, b, c);
});
});
socket.on('my_response', function(msg) {
$('#log').append('<br>' + $('<div/>').text('Received #' + msg.count + ': ' + msg.data).html());
});
}
else {
socket.disconnect();
socket.connect();
}
$('#connect input')[0].disabled = true;
$('#disconnect input')[0].disabled = false;
return false;
});
$('form#disconnect').submit(function(event) {
mediaStream.getAudioTracks()[0].stop();
audio_context.close();
socket.emit('disconnect_request');
$('#connect input')[0].disabled = false;
$('#disconnect input')[0].disabled = true;
return false;
});
Flask socketio应该正常退出,发出断开连接消息,以警告最终用户该过程成功完成,就像本地开发人员和短音频消息一样。