toxygen/toxygen/calls.py

145 lines
4.6 KiB
Python

import pyaudio
import time
import threading
import settings
from toxav_enums import *
# TODO: play sound until outgoing call will be started or cancelled and add timeout
# TODO: add widget for call
CALL_TYPE = {
'NONE': 0,
'AUDIO': 1,
'VIDEO': 2
}
class AV:
def __init__(self, toxav):
self._toxav = toxav
self._running = True
self._calls = {} # dict: key - friend number, value - call type
self._audio = None
self._audio_stream = None
self._audio_thread = None
self._audio_running = False
self._out_stream = None
self._audio_rate = 8000
self._audio_channels = 1
self._audio_duration = 60
self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000
def __contains__(self, friend_number):
return friend_number in self._calls
def __call__(self, friend_number, audio, video):
"""Call friend with specified number"""
self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0)
self._calls[friend_number] = CALL_TYPE['AUDIO']
self.start_audio_thread()
def finish_call(self, friend_number, by_friend=False):
if not by_friend:
self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL'])
if friend_number in self._calls:
del self._calls[friend_number]
if not len(self._calls):
self.stop_audio_thread()
def stop(self):
self._running = False
self.stop_audio_thread()
def start_audio_thread(self):
"""
Start audio sending
"""
if self._audio_thread is not None:
return
self._audio_running = True
self._audio = pyaudio.PyAudio()
self._audio_stream = self._audio.open(format=pyaudio.paInt16,
rate=self._audio_rate,
channels=self._audio_channels,
input=True,
input_device_index=settings.Settings.get_instance().audio['input'],
frames_per_buffer=self._audio_sample_count * 10)
self._audio_thread = threading.Thread(target=self.send_audio)
self._audio_thread.start()
def stop_audio_thread(self):
if self._audio_thread is None:
return
self._audio_running = False
self._audio_thread.join()
self._audio_thread = None
self._audio_stream = None
self._audio = None
if self._out_stream is not None:
self._out_stream.stop_stream()
self._out_stream.close()
self._out_stream = None
def chunk(self, samples, channels_count, rate):
"""
Incoming chunk
"""
if self._out_stream is None:
self._out_stream = self._audio.open(format=pyaudio.paInt16,
channels=channels_count,
rate=rate,
output_device_index=settings.Settings.get_instance().audio['output'],
output=True)
self._out_stream.write(samples)
def send_audio(self):
"""
This method sends audio to friends
"""
while self._audio_running:
try:
pcm = self._audio_stream.read(self._audio_sample_count)
if pcm:
for friend in self._calls:
if self._calls[friend] & 1:
try:
self._toxav.audio_send_frame(friend, pcm, self._audio_sample_count,
self._audio_channels, self._audio_rate)
except:
pass
except:
pass
time.sleep(0.01)
def accept_call(self, friend_number, audio_enabled, video_enabled):
if self._running:
self._calls[friend_number] = int(video_enabled) * 2 + int(audio_enabled)
self._toxav.answer(friend_number, 32 if audio_enabled else 0, 5000 if video_enabled else 0)
self.start_audio_thread()
def toxav_call_state_cb(self, friend_number, state):
"""
New call state
"""
if self._running:
if state & TOXAV_FRIEND_CALL_STATE['ACCEPTING_A']:
self._calls[friend_number] |= 1