Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
3546dbd75b | |||
b081bff2ce | |||
e257d8f96f | |||
972a073cb9 | |||
e4998cd5b5 |
@ -7,11 +7,9 @@ before_install:
|
|||||||
- sudo apt-get install -y checkinstall build-essential
|
- sudo apt-get install -y checkinstall build-essential
|
||||||
- sudo apt-get install portaudio19-dev
|
- sudo apt-get install portaudio19-dev
|
||||||
- sudo apt-get install libconfig-dev libvpx-dev check -qq
|
- sudo apt-get install libconfig-dev libvpx-dev check -qq
|
||||||
|
- sudo apt-get install -y python3-pyqt5
|
||||||
install:
|
install:
|
||||||
- pip install sip
|
|
||||||
- pip install pyqt5
|
|
||||||
- pip install pyaudio
|
- pip install pyaudio
|
||||||
- pip install opencv-python
|
|
||||||
before_script:
|
before_script:
|
||||||
# OPUS
|
# OPUS
|
||||||
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
|
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
|
||||||
|
12
setup.py
12
setup.py
@ -8,20 +8,15 @@ import sys
|
|||||||
|
|
||||||
version = program_version + '.0'
|
version = program_version + '.0'
|
||||||
|
|
||||||
MODULES = ['numpy', 'PyQt5']
|
MODULES = []
|
||||||
|
|
||||||
if system() in ('Windows', 'Darwin'):
|
if system() in ('Windows', 'Darwin'):
|
||||||
MODULES.append('PyAudio')
|
MODULES = ['PyAudio', 'PyQt5']
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
import pyaudio
|
import pyaudio
|
||||||
except ImportError:
|
except ImportError:
|
||||||
MODULES.append('PyAudio')
|
MODULES = ['PyAudio']
|
||||||
|
|
||||||
DEP_LINKS = []
|
|
||||||
|
|
||||||
if system() == 'Windows':
|
|
||||||
DEP_LINKS = [] # TODO: add opencv.whl
|
|
||||||
|
|
||||||
|
|
||||||
class InstallScript(install):
|
class InstallScript(install):
|
||||||
@ -60,7 +55,6 @@ setup(name='Toxygen',
|
|||||||
license='GPL3',
|
license='GPL3',
|
||||||
packages=['toxygen', 'toxygen.plugins', 'toxygen.styles'],
|
packages=['toxygen', 'toxygen.plugins', 'toxygen.styles'],
|
||||||
install_requires=MODULES,
|
install_requires=MODULES,
|
||||||
dependency_links=DEP_LINKS,
|
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Programming Language :: Python :: 3 :: Only',
|
'Programming Language :: Python :: 3 :: Only',
|
||||||
|
@ -19,7 +19,6 @@ class IncomingCallWidget(widgets.CenteredWidget):
|
|||||||
self.avatar_label.setScaledContents(False)
|
self.avatar_label.setScaledContents(False)
|
||||||
self.name = widgets.DataLabel(self)
|
self.name = widgets.DataLabel(self)
|
||||||
self.name.setGeometry(QtCore.QRect(90, 20, 300, 25))
|
self.name.setGeometry(QtCore.QRect(90, 20, 300, 25))
|
||||||
self._friend_number = friend_number
|
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily(settings.Settings.get_instance()['font'])
|
font.setFamily(settings.Settings.get_instance()['font'])
|
||||||
font.setPointSize(16)
|
font.setPointSize(16)
|
||||||
@ -52,10 +51,10 @@ class IncomingCallWidget(widgets.CenteredWidget):
|
|||||||
self.setWindowTitle(text)
|
self.setWindowTitle(text)
|
||||||
self.name.setText(name)
|
self.name.setText(name)
|
||||||
self.call_type.setText(text)
|
self.call_type.setText(text)
|
||||||
self._processing = False
|
pr = profile.Profile.get_instance()
|
||||||
self.accept_audio.clicked.connect(self.accept_call_with_audio)
|
self.accept_audio.clicked.connect(lambda: pr.accept_call(friend_number, True, False) or self.stop())
|
||||||
self.accept_video.clicked.connect(self.accept_call_with_video)
|
# self.accept_video.clicked.connect(lambda: pr.start_call(friend_number, True, True))
|
||||||
self.decline.clicked.connect(self.decline_call)
|
self.decline.clicked.connect(lambda: pr.stop_call(friend_number, False) or self.stop())
|
||||||
|
|
||||||
class SoundPlay(QtCore.QThread):
|
class SoundPlay(QtCore.QThread):
|
||||||
|
|
||||||
@ -106,29 +105,31 @@ class IncomingCallWidget(widgets.CenteredWidget):
|
|||||||
self.thread.wait()
|
self.thread.wait()
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def accept_call_with_audio(self):
|
|
||||||
if self._processing:
|
|
||||||
return
|
|
||||||
self._processing = True
|
|
||||||
pr = profile.Profile.get_instance()
|
|
||||||
pr.accept_call(self._friend_number, True, False)
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def accept_call_with_video(self):
|
|
||||||
if self._processing:
|
|
||||||
return
|
|
||||||
self._processing = True
|
|
||||||
pr = profile.Profile.get_instance()
|
|
||||||
pr.accept_call(self._friend_number, True, True)
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def decline_call(self):
|
|
||||||
if self._processing:
|
|
||||||
return
|
|
||||||
self._processing = True
|
|
||||||
pr = profile.Profile.get_instance()
|
|
||||||
pr.stop_call(self._friend_number, False)
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
def set_pixmap(self, pixmap):
|
def set_pixmap(self, pixmap):
|
||||||
self.avatar_label.setPixmap(pixmap)
|
self.avatar_label.setPixmap(pixmap)
|
||||||
|
|
||||||
|
|
||||||
|
class AudioMessageRecorder(widgets.CenteredWidget):
|
||||||
|
|
||||||
|
def __init__(self, friend_number, name):
|
||||||
|
super(AudioMessageRecorder, self).__init__()
|
||||||
|
self.label = QtWidgets.QLabel(self)
|
||||||
|
self.label.setGeometry(QtCore.QRect(10, 20, 250, 20))
|
||||||
|
text = QtWidgets.QApplication.translate("MenuWindow", "Send audio message to friend {}")
|
||||||
|
self.label.setText(text.format(name))
|
||||||
|
self.record = QtWidgets.QPushButton(self)
|
||||||
|
self.record.setGeometry(QtCore.QRect(20, 100, 150, 150))
|
||||||
|
|
||||||
|
self.record.setText(QtWidgets.QApplication.translate("MenuWindow", "Start recording"))
|
||||||
|
self.record.clicked.connect(self.start_or_stop_recording)
|
||||||
|
self.recording = False
|
||||||
|
self.friend_num = friend_number
|
||||||
|
|
||||||
|
def start_or_stop_recording(self):
|
||||||
|
if not self.recording:
|
||||||
|
self.recording = True
|
||||||
|
self.record.setText(QtWidgets.QApplication.translate("MenuWindow", "Stop recording"))
|
||||||
|
else:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,8 +9,7 @@ from plugin_support import PluginLoader
|
|||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
import util
|
import util
|
||||||
import cv2
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Threads
|
# Threads
|
||||||
@ -313,67 +312,11 @@ def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, aud
|
|||||||
"""
|
"""
|
||||||
New audio chunk
|
New audio chunk
|
||||||
"""
|
"""
|
||||||
Profile.get_instance().call.audio_chunk(
|
Profile.get_instance().call.chunk(
|
||||||
bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
||||||
audio_channels_count,
|
audio_channels_count,
|
||||||
rate)
|
rate)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Callbacks - video
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data):
|
|
||||||
"""
|
|
||||||
Creates yuv frame from y, u, v and shows it using OpenCV
|
|
||||||
For yuv => bgr we need this YUV420 frame:
|
|
||||||
|
|
||||||
width
|
|
||||||
-------------------------
|
|
||||||
| |
|
|
||||||
| Y | height
|
|
||||||
| |
|
|
||||||
-------------------------
|
|
||||||
| | |
|
|
||||||
| U even | U odd | height // 4
|
|
||||||
| | |
|
|
||||||
-------------------------
|
|
||||||
| | |
|
|
||||||
| V even | V odd | height // 4
|
|
||||||
| | |
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
width // 2 width // 2
|
|
||||||
|
|
||||||
It can be created from initial y, u, v using slices
|
|
||||||
For more info see callback_video_receive_frame docs
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
y_size = abs(max(width, abs(ystride)))
|
|
||||||
u_size = abs(max(width // 2, abs(ustride)))
|
|
||||||
v_size = abs(max(width // 2, abs(vstride)))
|
|
||||||
|
|
||||||
y = np.asarray(y[:y_size * height], dtype=np.uint8).reshape(height, y_size)
|
|
||||||
u = np.asarray(u[:u_size * height // 2], dtype=np.uint8).reshape(height // 2, u_size)
|
|
||||||
v = np.asarray(v[:v_size * height // 2], dtype=np.uint8).reshape(height // 2, v_size)
|
|
||||||
|
|
||||||
width -= width % 4
|
|
||||||
height -= height % 4
|
|
||||||
|
|
||||||
frame = np.zeros((int(height * 1.5), width), dtype=np.uint8)
|
|
||||||
|
|
||||||
frame[:height, :] = y[:height, :width]
|
|
||||||
frame[height:height * 5 // 4, :width // 2] = u[:height // 2:2, :width // 2]
|
|
||||||
frame[height:height * 5 // 4, width // 2:] = u[1:height // 2:2, :width // 2]
|
|
||||||
|
|
||||||
frame[height * 5 // 4:, :width // 2] = v[:height // 2:2, :width // 2]
|
|
||||||
frame[height * 5 // 4:, width // 2:] = v[1:height // 2:2, :width // 2]
|
|
||||||
|
|
||||||
frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420)
|
|
||||||
|
|
||||||
invoke_in_main_thread(cv2.imshow, str(friend_number), frame)
|
|
||||||
except Exception as ex:
|
|
||||||
print(ex)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - initialization
|
# Callbacks - initialization
|
||||||
@ -407,7 +350,7 @@ def init_callbacks(tox, window, tray):
|
|||||||
toxav.callback_call_state(call_state, 0)
|
toxav.callback_call_state(call_state, 0)
|
||||||
toxav.callback_call(call, 0)
|
toxav.callback_call(call, 0)
|
||||||
toxav.callback_audio_receive_frame(callback_audio, 0)
|
toxav.callback_audio_receive_frame(callback_audio, 0)
|
||||||
toxav.callback_video_receive_frame(video_receive_frame, 0)
|
|
||||||
|
|
||||||
tox.callback_friend_lossless_packet(lossless_packet, 0)
|
tox.callback_friend_lossless_packet(lossless_packet, 0)
|
||||||
tox.callback_friend_lossy_packet(lossy_packet, 0)
|
tox.callback_friend_lossy_packet(lossy_packet, 0)
|
||||||
|
|
||||||
|
248
toxygen/calls.py
248
toxygen/calls.py
@ -3,68 +3,14 @@ import time
|
|||||||
import threading
|
import threading
|
||||||
import settings
|
import settings
|
||||||
from toxav_enums import *
|
from toxav_enums import *
|
||||||
import cv2
|
# TODO: play sound until outgoing call will be started or cancelled and add timeout
|
||||||
import itertools
|
# TODO: add widget for call
|
||||||
import numpy as np
|
|
||||||
# TODO: play sound until outgoing call will be started or cancelled
|
|
||||||
|
|
||||||
|
CALL_TYPE = {
|
||||||
class Call:
|
'NONE': 0,
|
||||||
|
'AUDIO': 1,
|
||||||
def __init__(self, out_audio, out_video, in_audio=False, in_video=False):
|
'VIDEO': 2
|
||||||
self._in_audio = in_audio
|
}
|
||||||
self._in_video = in_video
|
|
||||||
self._out_audio = out_audio
|
|
||||||
self._out_video = out_video
|
|
||||||
self._is_active = False
|
|
||||||
|
|
||||||
def get_is_active(self):
|
|
||||||
return self._is_active
|
|
||||||
|
|
||||||
def set_is_active(self, value):
|
|
||||||
self._is_active = value
|
|
||||||
|
|
||||||
is_active = property(get_is_active, set_is_active)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Audio
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_in_audio(self):
|
|
||||||
return self._in_audio
|
|
||||||
|
|
||||||
def set_in_audio(self, value):
|
|
||||||
self._in_audio = value
|
|
||||||
|
|
||||||
in_audio = property(get_in_audio, set_in_audio)
|
|
||||||
|
|
||||||
def get_out_audio(self):
|
|
||||||
return self._out_audio
|
|
||||||
|
|
||||||
def set_out_audio(self, value):
|
|
||||||
self._out_audio = value
|
|
||||||
|
|
||||||
out_audio = property(get_out_audio, set_out_audio)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Video
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_in_video(self):
|
|
||||||
return self._in_video
|
|
||||||
|
|
||||||
def set_in_video(self, value):
|
|
||||||
self._in_video = value
|
|
||||||
|
|
||||||
in_video = property(get_in_video, set_in_video)
|
|
||||||
|
|
||||||
def get_out_video(self):
|
|
||||||
return self._out_video
|
|
||||||
|
|
||||||
def set_out_video(self, value):
|
|
||||||
self._in_video = value
|
|
||||||
|
|
||||||
out_video = property(get_out_video, set_out_video)
|
|
||||||
|
|
||||||
|
|
||||||
class AV:
|
class AV:
|
||||||
@ -73,7 +19,7 @@ class AV:
|
|||||||
self._toxav = toxav
|
self._toxav = toxav
|
||||||
self._running = True
|
self._running = True
|
||||||
|
|
||||||
self._calls = {} # dict: key - friend number, value - Call instance
|
self._calls = {} # dict: key - friend number, value - call type
|
||||||
|
|
||||||
self._audio = None
|
self._audio = None
|
||||||
self._audio_stream = None
|
self._audio_stream = None
|
||||||
@ -86,75 +32,27 @@ class AV:
|
|||||||
self._audio_duration = 60
|
self._audio_duration = 60
|
||||||
self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000
|
self._audio_sample_count = self._audio_rate * self._audio_channels * self._audio_duration // 1000
|
||||||
|
|
||||||
self._video = None
|
|
||||||
self._video_thread = None
|
|
||||||
self._video_running = False
|
|
||||||
|
|
||||||
self._video_width = 640
|
|
||||||
self._video_height = 480
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._running = False
|
|
||||||
self.stop_audio_thread()
|
|
||||||
self.stop_video_thread()
|
|
||||||
|
|
||||||
def __contains__(self, friend_number):
|
def __contains__(self, friend_number):
|
||||||
return friend_number in self._calls
|
return friend_number in self._calls
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Calls
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def __call__(self, friend_number, audio, video):
|
def __call__(self, friend_number, audio, video):
|
||||||
"""Call friend with specified number"""
|
"""Call friend with specified number"""
|
||||||
self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0)
|
self._toxav.call(friend_number, 32 if audio else 0, 5000 if video else 0)
|
||||||
self._calls[friend_number] = Call(audio, video)
|
self._calls[friend_number] = CALL_TYPE['AUDIO']
|
||||||
threading.Timer(30.0, lambda: self.finish_not_started_call(friend_number)).start()
|
|
||||||
|
|
||||||
def accept_call(self, friend_number, audio_enabled, video_enabled):
|
|
||||||
if self._running:
|
|
||||||
self._calls[friend_number] = Call(audio_enabled, video_enabled)
|
|
||||||
self._toxav.answer(friend_number, 32 if audio_enabled else 0, 5000 if video_enabled else 0)
|
|
||||||
if audio_enabled:
|
|
||||||
self.start_audio_thread()
|
self.start_audio_thread()
|
||||||
if video_enabled:
|
|
||||||
self.start_video_thread()
|
|
||||||
|
|
||||||
def finish_call(self, friend_number, by_friend=False):
|
def finish_call(self, friend_number, by_friend=False):
|
||||||
|
|
||||||
if not by_friend:
|
if not by_friend:
|
||||||
self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL'])
|
self._toxav.call_control(friend_number, TOXAV_CALL_CONTROL['CANCEL'])
|
||||||
if friend_number in self._calls:
|
if friend_number in self._calls:
|
||||||
del self._calls[friend_number]
|
del self._calls[friend_number]
|
||||||
if not len(list(filter(lambda c: c.out_audio, self._calls))):
|
if not len(self._calls):
|
||||||
self.stop_audio_thread()
|
self.stop_audio_thread()
|
||||||
if not len(list(filter(lambda c: c.out_video, self._calls))):
|
|
||||||
self.stop_video_thread()
|
|
||||||
|
|
||||||
def finish_not_started_call(self, friend_number):
|
def stop(self):
|
||||||
if friend_number in self:
|
self._running = False
|
||||||
call = self._calls[friend_number]
|
self.stop_audio_thread()
|
||||||
if not call.is_active:
|
|
||||||
self.finish_call(friend_number)
|
|
||||||
|
|
||||||
def toxav_call_state_cb(self, friend_number, state):
|
|
||||||
"""
|
|
||||||
New call state
|
|
||||||
"""
|
|
||||||
call = self._calls[friend_number]
|
|
||||||
call.is_active = True
|
|
||||||
|
|
||||||
call.in_audio = state | TOXAV_FRIEND_CALL_STATE['SENDING_A']
|
|
||||||
call.in_video = state | TOXAV_FRIEND_CALL_STATE['SENDING_V']
|
|
||||||
|
|
||||||
if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_A'] and call.out_audio:
|
|
||||||
self.start_audio_thread()
|
|
||||||
|
|
||||||
if state | TOXAV_FRIEND_CALL_STATE['ACCEPTING_V'] and call.out_video:
|
|
||||||
self.start_video_thread()
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Threads
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def start_audio_thread(self):
|
def start_audio_thread(self):
|
||||||
"""
|
"""
|
||||||
@ -194,37 +92,7 @@ class AV:
|
|||||||
self._out_stream.close()
|
self._out_stream.close()
|
||||||
self._out_stream = None
|
self._out_stream = None
|
||||||
|
|
||||||
def start_video_thread(self):
|
def chunk(self, samples, channels_count, rate):
|
||||||
if self._video_thread is not None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._video_running = True
|
|
||||||
s = settings.Settings.get_instance()
|
|
||||||
self._video_width = s.video['width']
|
|
||||||
self._video_height = s.video['height']
|
|
||||||
|
|
||||||
self._video = cv2.VideoCapture(s.video['device'])
|
|
||||||
self._video.set(cv2.CAP_PROP_FPS, 25)
|
|
||||||
self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width)
|
|
||||||
self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height)
|
|
||||||
|
|
||||||
self._video_thread = threading.Thread(target=self.send_video)
|
|
||||||
self._video_thread.start()
|
|
||||||
|
|
||||||
def stop_video_thread(self):
|
|
||||||
if self._video_thread is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._video_running = False
|
|
||||||
self._video_thread.join()
|
|
||||||
self._video_thread = None
|
|
||||||
self._video = None
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Incoming chunks
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def audio_chunk(self, samples, channels_count, rate):
|
|
||||||
"""
|
"""
|
||||||
Incoming chunk
|
Incoming chunk
|
||||||
"""
|
"""
|
||||||
@ -237,10 +105,6 @@ class AV:
|
|||||||
output=True)
|
output=True)
|
||||||
self._out_stream.write(samples)
|
self._out_stream.write(samples)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# AV sending
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def send_audio(self):
|
def send_audio(self):
|
||||||
"""
|
"""
|
||||||
This method sends audio to friends
|
This method sends audio to friends
|
||||||
@ -250,10 +114,10 @@ class AV:
|
|||||||
try:
|
try:
|
||||||
pcm = self._audio_stream.read(self._audio_sample_count)
|
pcm = self._audio_stream.read(self._audio_sample_count)
|
||||||
if pcm:
|
if pcm:
|
||||||
for friend_num in self._calls:
|
for friend in self._calls:
|
||||||
if self._calls[friend_num].out_audio:
|
if self._calls[friend] & 1:
|
||||||
try:
|
try:
|
||||||
self._toxav.audio_send_frame(friend_num, pcm, self._audio_sample_count,
|
self._toxav.audio_send_frame(friend, pcm, self._audio_sample_count,
|
||||||
self._audio_channels, self._audio_rate)
|
self._audio_channels, self._audio_rate)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@ -262,71 +126,19 @@ class AV:
|
|||||||
|
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
def send_video(self):
|
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):
|
||||||
"""
|
"""
|
||||||
This method sends video to friends
|
New call state
|
||||||
"""
|
"""
|
||||||
while self._video_running:
|
if self._running:
|
||||||
try:
|
|
||||||
result, frame = self._video.read()
|
|
||||||
if result:
|
|
||||||
height, width, channels = frame.shape
|
|
||||||
for friend_num in self._calls:
|
|
||||||
if self._calls[friend_num].out_video:
|
|
||||||
try:
|
|
||||||
y, u, v = self.convert_bgr_to_yuv(frame)
|
|
||||||
self._toxav.video_send_frame(friend_num, width, height, y, u, v)
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
time.sleep(0.01)
|
if state & TOXAV_FRIEND_CALL_STATE['ACCEPTING_A']:
|
||||||
|
self._calls[friend_number] |= 1
|
||||||
|
|
||||||
def convert_bgr_to_yuv(self, frame):
|
|
||||||
"""
|
|
||||||
:param frame: input bgr frame
|
|
||||||
:return y, u, v: y, u, v values of frame
|
|
||||||
|
|
||||||
How this function works:
|
|
||||||
OpenCV creates YUV420 frame from BGR
|
|
||||||
This frame has following structure and size:
|
|
||||||
width, height - dim of input frame
|
|
||||||
width, height * 1.5 - dim of output frame
|
|
||||||
|
|
||||||
width
|
|
||||||
-------------------------
|
|
||||||
| |
|
|
||||||
| Y | height
|
|
||||||
| |
|
|
||||||
-------------------------
|
|
||||||
| | |
|
|
||||||
| U even | U odd | height // 4
|
|
||||||
| | |
|
|
||||||
-------------------------
|
|
||||||
| | |
|
|
||||||
| V even | V odd | height // 4
|
|
||||||
| | |
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
width // 2 width // 2
|
|
||||||
|
|
||||||
Y, U, V can be extracted using slices and joined in one list using itertools.chain.from_iterable()
|
|
||||||
Function returns bytes(y), bytes(u), bytes(v), because it is required for ctypes
|
|
||||||
"""
|
|
||||||
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420)
|
|
||||||
|
|
||||||
y = frame[:self._video_height, :]
|
|
||||||
y = list(itertools.chain.from_iterable(y))
|
|
||||||
|
|
||||||
u = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int)
|
|
||||||
u[::2, :] = frame[self._video_height:self._video_height * 5 // 4, :self._video_width // 2]
|
|
||||||
u[1::2, :] = frame[self._video_height:self._video_height * 5 // 4, self._video_width // 2:]
|
|
||||||
u = list(itertools.chain.from_iterable(u))
|
|
||||||
|
|
||||||
v = np.zeros((self._video_height // 2, self._video_width // 2), dtype=np.int)
|
|
||||||
v[::2, :] = frame[self._video_height * 5 // 4:, :self._video_width // 2]
|
|
||||||
v[1::2, :] = frame[self._video_height * 5 // 4:, self._video_width // 2:]
|
|
||||||
v = list(itertools.chain.from_iterable(v))
|
|
||||||
|
|
||||||
return bytes(y), bytes(u), bytes(v)
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
@ -541,6 +541,8 @@ class InlineImageItem(QtWidgets.QScrollArea):
|
|||||||
fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png')
|
fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png')
|
||||||
self._pixmap.save(fl, 'PNG')
|
self._pixmap.save(fl, 'PNG')
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def mark_as_sent(self):
|
def mark_as_sent(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -63,7 +63,6 @@ class MainWindow(QtWidgets.QMainWindow, Singleton):
|
|||||||
self.actionSettings = QtWidgets.QAction(Form)
|
self.actionSettings = QtWidgets.QAction(Form)
|
||||||
self.actionSettings.setObjectName("actionSettings")
|
self.actionSettings.setObjectName("actionSettings")
|
||||||
self.audioSettings = QtWidgets.QAction(Form)
|
self.audioSettings = QtWidgets.QAction(Form)
|
||||||
self.videoSettings = QtWidgets.QAction(Form)
|
|
||||||
self.pluginData = QtWidgets.QAction(Form)
|
self.pluginData = QtWidgets.QAction(Form)
|
||||||
self.importPlugin = QtWidgets.QAction(Form)
|
self.importPlugin = QtWidgets.QAction(Form)
|
||||||
self.reloadPlugins = QtWidgets.QAction(Form)
|
self.reloadPlugins = QtWidgets.QAction(Form)
|
||||||
@ -76,7 +75,6 @@ class MainWindow(QtWidgets.QMainWindow, Singleton):
|
|||||||
self.menuSettings.addAction(self.actionNotifications)
|
self.menuSettings.addAction(self.actionNotifications)
|
||||||
self.menuSettings.addAction(self.actionNetwork)
|
self.menuSettings.addAction(self.actionNetwork)
|
||||||
self.menuSettings.addAction(self.audioSettings)
|
self.menuSettings.addAction(self.audioSettings)
|
||||||
self.menuSettings.addAction(self.videoSettings)
|
|
||||||
self.menuSettings.addAction(self.updateSettings)
|
self.menuSettings.addAction(self.updateSettings)
|
||||||
self.menuPlugins.addAction(self.pluginData)
|
self.menuPlugins.addAction(self.pluginData)
|
||||||
self.menuPlugins.addAction(self.importPlugin)
|
self.menuPlugins.addAction(self.importPlugin)
|
||||||
@ -96,7 +94,6 @@ class MainWindow(QtWidgets.QMainWindow, Singleton):
|
|||||||
self.actionInterface_settings.triggered.connect(self.interface_settings)
|
self.actionInterface_settings.triggered.connect(self.interface_settings)
|
||||||
self.actionNotifications.triggered.connect(self.notification_settings)
|
self.actionNotifications.triggered.connect(self.notification_settings)
|
||||||
self.audioSettings.triggered.connect(self.audio_settings)
|
self.audioSettings.triggered.connect(self.audio_settings)
|
||||||
self.videoSettings.triggered.connect(self.video_settings)
|
|
||||||
self.updateSettings.triggered.connect(self.update_settings)
|
self.updateSettings.triggered.connect(self.update_settings)
|
||||||
self.pluginData.triggered.connect(self.plugins_menu)
|
self.pluginData.triggered.connect(self.plugins_menu)
|
||||||
self.lockApp.triggered.connect(self.lock_app)
|
self.lockApp.triggered.connect(self.lock_app)
|
||||||
@ -131,7 +128,6 @@ class MainWindow(QtWidgets.QMainWindow, Singleton):
|
|||||||
self.actionAbout_program.setText(QtWidgets.QApplication.translate("MainWindow", "About program"))
|
self.actionAbout_program.setText(QtWidgets.QApplication.translate("MainWindow", "About program"))
|
||||||
self.actionSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Settings"))
|
self.actionSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Settings"))
|
||||||
self.audioSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Audio"))
|
self.audioSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Audio"))
|
||||||
self.videoSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Video"))
|
|
||||||
self.updateSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Updates"))
|
self.updateSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Updates"))
|
||||||
self.contact_name.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search"))
|
self.contact_name.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search"))
|
||||||
self.sendMessageButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Send message"))
|
self.sendMessageButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Send message"))
|
||||||
@ -463,10 +459,6 @@ class MainWindow(QtWidgets.QMainWindow, Singleton):
|
|||||||
self.audio_s = AudioSettings()
|
self.audio_s = AudioSettings()
|
||||||
self.audio_s.show()
|
self.audio_s.show()
|
||||||
|
|
||||||
def video_settings(self):
|
|
||||||
self.video_s = VideoSettings()
|
|
||||||
self.video_s.show()
|
|
||||||
|
|
||||||
def update_settings(self):
|
def update_settings(self):
|
||||||
self.update_s = UpdateSettings()
|
self.update_s = UpdateSettings()
|
||||||
self.update_s.show()
|
self.update_s.show()
|
||||||
@ -567,15 +559,14 @@ class MainWindow(QtWidgets.QMainWindow, Singleton):
|
|||||||
def call_finished(self):
|
def call_finished(self):
|
||||||
self.update_call_state('call')
|
self.update_call_state('call')
|
||||||
|
|
||||||
def update_call_state(self, state):
|
def update_call_state(self, fl):
|
||||||
|
# TODO: do smth with video call button
|
||||||
os.chdir(curr_directory() + '/images/')
|
os.chdir(curr_directory() + '/images/')
|
||||||
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(fl))
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(state))
|
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.callButton.setIcon(icon)
|
self.callButton.setIcon(icon)
|
||||||
self.callButton.setIconSize(QtCore.QSize(50, 50))
|
self.callButton.setIconSize(QtCore.QSize(50, 50))
|
||||||
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/videocall.png')
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}_video.png'.format(state))
|
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.videocallButton.setIcon(icon)
|
self.videocallButton.setIcon(icon)
|
||||||
self.videocallButton.setIconSize(QtCore.QSize(35, 35))
|
self.videocallButton.setIconSize(QtCore.QSize(35, 35))
|
||||||
|
@ -207,8 +207,8 @@ class DropdownMenu(QtWidgets.QWidget):
|
|||||||
super(DropdownMenu, self).__init__(parent)
|
super(DropdownMenu, self).__init__(parent)
|
||||||
self.installEventFilter(self)
|
self.installEventFilter(self)
|
||||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
|
||||||
self.setMaximumSize(120, 120)
|
self.setMaximumSize(180, 120)
|
||||||
self.setMinimumSize(120, 120)
|
self.setMinimumSize(180, 120)
|
||||||
self.screenshotButton = QRightClickButton(self)
|
self.screenshotButton = QRightClickButton(self)
|
||||||
self.screenshotButton.setGeometry(QtCore.QRect(0, 60, 60, 60))
|
self.screenshotButton.setGeometry(QtCore.QRect(0, 60, 60, 60))
|
||||||
self.screenshotButton.setObjectName("screenshotButton")
|
self.screenshotButton.setObjectName("screenshotButton")
|
||||||
@ -217,9 +217,15 @@ class DropdownMenu(QtWidgets.QWidget):
|
|||||||
self.fileTransferButton.setGeometry(QtCore.QRect(60, 60, 60, 60))
|
self.fileTransferButton.setGeometry(QtCore.QRect(60, 60, 60, 60))
|
||||||
self.fileTransferButton.setObjectName("fileTransferButton")
|
self.fileTransferButton.setObjectName("fileTransferButton")
|
||||||
|
|
||||||
|
self.audioMessageButton = QtWidgets.QPushButton(self)
|
||||||
|
self.audioMessageButton.setGeometry(QtCore.QRect(120, 60, 60, 60))
|
||||||
|
|
||||||
self.smileyButton = QtWidgets.QPushButton(self)
|
self.smileyButton = QtWidgets.QPushButton(self)
|
||||||
self.smileyButton.setGeometry(QtCore.QRect(0, 0, 60, 60))
|
self.smileyButton.setGeometry(QtCore.QRect(0, 0, 60, 60))
|
||||||
|
|
||||||
|
self.videoMessageButton = QtWidgets.QPushButton(self)
|
||||||
|
self.videoMessageButton.setGeometry(QtCore.QRect(120, 0, 60, 60))
|
||||||
|
|
||||||
self.stickerButton = QtWidgets.QPushButton(self)
|
self.stickerButton = QtWidgets.QPushButton(self)
|
||||||
self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60))
|
self.stickerButton.setGeometry(QtCore.QRect(60, 0, 60, 60))
|
||||||
|
|
||||||
@ -227,17 +233,22 @@ class DropdownMenu(QtWidgets.QWidget):
|
|||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.fileTransferButton.setIcon(icon)
|
self.fileTransferButton.setIcon(icon)
|
||||||
self.fileTransferButton.setIconSize(QtCore.QSize(50, 50))
|
self.fileTransferButton.setIconSize(QtCore.QSize(50, 50))
|
||||||
|
|
||||||
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/screenshot.png')
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/screenshot.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.screenshotButton.setIcon(icon)
|
self.screenshotButton.setIcon(icon)
|
||||||
self.screenshotButton.setIconSize(QtCore.QSize(50, 60))
|
self.screenshotButton.setIconSize(QtCore.QSize(50, 60))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/audio_message.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.audioMessageButton.setIcon(icon)
|
||||||
|
self.audioMessageButton.setIconSize(QtCore.QSize(50, 50))
|
||||||
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/smiley.png')
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/smiley.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.smileyButton.setIcon(icon)
|
self.smileyButton.setIcon(icon)
|
||||||
self.smileyButton.setIconSize(QtCore.QSize(50, 50))
|
self.smileyButton.setIconSize(QtCore.QSize(50, 50))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/video_message.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.videoMessageButton.setIcon(icon)
|
||||||
|
self.videoMessageButton.setIconSize(QtCore.QSize(55, 55))
|
||||||
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/sticker.png')
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/sticker.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.stickerButton.setIcon(icon)
|
self.stickerButton.setIcon(icon)
|
||||||
@ -245,6 +256,8 @@ class DropdownMenu(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self.screenshotButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send screenshot"))
|
self.screenshotButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send screenshot"))
|
||||||
self.fileTransferButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send file"))
|
self.fileTransferButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send file"))
|
||||||
|
self.audioMessageButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send audio message"))
|
||||||
|
self.videoMessageButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send video message"))
|
||||||
self.smileyButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Add smiley"))
|
self.smileyButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Add smiley"))
|
||||||
self.stickerButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send sticker"))
|
self.stickerButton.setToolTip(QtWidgets.QApplication.translate("MenuWindow", "Send sticker"))
|
||||||
|
|
||||||
@ -257,7 +270,7 @@ class DropdownMenu(QtWidgets.QWidget):
|
|||||||
def leaveEvent(self, event):
|
def leaveEvent(self, event):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def eventFilter(self, obj, event):
|
def eventFilter(self, object, event):
|
||||||
if event.type() == QtCore.QEvent.WindowDeactivate:
|
if event.type() == QtCore.QEvent.WindowDeactivate:
|
||||||
self.close()
|
self.close()
|
||||||
return False
|
return False
|
||||||
|
@ -247,12 +247,12 @@ class ProfileSettings(CenteredWidget):
|
|||||||
|
|
||||||
def set_avatar(self):
|
def set_avatar(self):
|
||||||
choose = QtWidgets.QApplication.translate("ProfileSettingsForm", "Choose avatar")
|
choose = QtWidgets.QApplication.translate("ProfileSettingsForm", "Choose avatar")
|
||||||
name = QtWidgets.QFileDialog.getOpenFileName(self, choose, None, 'Images (*.png)',
|
name = QtWidgets.QFileDialog.getOpenFileName(self, choose, None, None, 'Images (*.png)',
|
||||||
QtGui.QComboBoxQtWidgets.QFileDialog.DontUseNativeDialog)
|
QtWidgets.QFileDialog.DontUseNativeDialog)
|
||||||
if name[0]:
|
if name[0]:
|
||||||
bitmap = QtGui.QPixmap(name[0])
|
bitmap = QtGui.QPixmap(name[0])
|
||||||
bitmap.scaled(QtCore.QSize(128, 128), aspectMode=QtCore.Qt.KeepAspectRatio,
|
bitmap.scaled(128, 128, QtCore.Qt.KeepAspectRatio,
|
||||||
mode=QtCore.Qt.SmoothTransformation)
|
QtCore.Qt.SmoothTransformation)
|
||||||
|
|
||||||
byte_array = QtCore.QByteArray()
|
byte_array = QtCore.QByteArray()
|
||||||
buffer = QtCore.QBuffer(byte_array)
|
buffer = QtCore.QBuffer(byte_array)
|
||||||
@ -261,8 +261,9 @@ class ProfileSettings(CenteredWidget):
|
|||||||
Profile.get_instance().set_avatar(bytes(byte_array.data()))
|
Profile.get_instance().set_avatar(bytes(byte_array.data()))
|
||||||
|
|
||||||
def export_profile(self):
|
def export_profile(self):
|
||||||
directory = QtWidgets.QFileDialog.getExistingDirectory(options=QtWidgets.QFileDialog.DontUseNativeDialog,
|
directory = QtWidgets.QFileDialog.getExistingDirectory(self, '', curr_directory() + '/',
|
||||||
dir=curr_directory()) + '/'
|
QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)
|
||||||
|
|
||||||
if directory != '/':
|
if directory != '/':
|
||||||
reply = QtWidgets.QMessageBox.question(None,
|
reply = QtWidgets.QMessageBox.question(None,
|
||||||
QtWidgets.QApplication.translate("ProfileSettingsForm",
|
QtWidgets.QApplication.translate("ProfileSettingsForm",
|
||||||
@ -493,7 +494,8 @@ class PrivacySettings(CenteredWidget):
|
|||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
def new_path(self):
|
def new_path(self):
|
||||||
directory = QtWidgets.QFileDialog.getExistingDirectory(options=QtWidgets.QFileDialog.DontUseNativeDialog) + '/'
|
directory = QtWidgets.QFileDialog.getExistingDirectory(self, '', curr_directory() + '/',
|
||||||
|
QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog) + '/'
|
||||||
if directory != '/':
|
if directory != '/':
|
||||||
self.path.setPlainText(directory)
|
self.path.setPlainText(directory)
|
||||||
|
|
||||||
@ -795,83 +797,6 @@ class AudioSettings(CenteredWidget):
|
|||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
|
|
||||||
class VideoSettings(CenteredWidget):
|
|
||||||
"""
|
|
||||||
Audio calls settings form
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.initUI()
|
|
||||||
self.retranslateUi()
|
|
||||||
self.center()
|
|
||||||
|
|
||||||
def initUI(self):
|
|
||||||
self.setObjectName("videoSettingsForm")
|
|
||||||
self.resize(400, 120)
|
|
||||||
self.setMinimumSize(QtCore.QSize(400, 120))
|
|
||||||
self.setMaximumSize(QtCore.QSize(400, 120))
|
|
||||||
self.in_label = QtWidgets.QLabel(self)
|
|
||||||
self.in_label.setGeometry(QtCore.QRect(25, 5, 350, 20))
|
|
||||||
settings = Settings.get_instance()
|
|
||||||
font = QtGui.QFont()
|
|
||||||
font.setPointSize(16)
|
|
||||||
font.setBold(True)
|
|
||||||
font.setFamily(settings['font'])
|
|
||||||
self.in_label.setFont(font)
|
|
||||||
self.video_size = QtWidgets.QComboBox(self)
|
|
||||||
self.video_size.setGeometry(QtCore.QRect(25, 70, 350, 30))
|
|
||||||
self.input = QtWidgets.QComboBox(self)
|
|
||||||
self.input.setGeometry(QtCore.QRect(25, 30, 350, 30))
|
|
||||||
self.input.currentIndexChanged.connect(self.selectionChanged)
|
|
||||||
import cv2
|
|
||||||
self.devices = []
|
|
||||||
self.frame_max_sizes = []
|
|
||||||
for i in range(10):
|
|
||||||
v = cv2.VideoCapture(i)
|
|
||||||
if v.isOpened():
|
|
||||||
v.set(cv2.CAP_PROP_FRAME_WIDTH, 10000)
|
|
||||||
v.set(cv2.CAP_PROP_FRAME_HEIGHT, 10000)
|
|
||||||
|
|
||||||
width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
||||||
height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
||||||
del v
|
|
||||||
self.devices.append(i)
|
|
||||||
self.frame_max_sizes.append((width, height))
|
|
||||||
self.input.addItem('Device #' + str(i))
|
|
||||||
index = self.devices.index(settings.video['device'])
|
|
||||||
if index + 1:
|
|
||||||
self.input.setCurrentIndex(index)
|
|
||||||
|
|
||||||
def retranslateUi(self):
|
|
||||||
self.setWindowTitle(QtWidgets.QApplication.translate("videoSettingsForm", "Video settings"))
|
|
||||||
self.in_label.setText(QtWidgets.QApplication.translate("videoSettingsForm", "Device:"))
|
|
||||||
|
|
||||||
def closeEvent(self, event):
|
|
||||||
settings = Settings.get_instance()
|
|
||||||
settings.video['device'] = self.devices[self.input.currentIndex()]
|
|
||||||
text = self.video_size.currentText()
|
|
||||||
settings.video['width'] = int(text.split(' ')[0])
|
|
||||||
settings.video['height'] = int(text.split(' ')[-1])
|
|
||||||
settings.save()
|
|
||||||
|
|
||||||
def selectionChanged(self):
|
|
||||||
width, height = self.frame_max_sizes[self.input.currentIndex()]
|
|
||||||
self.video_size.clear()
|
|
||||||
dims = [
|
|
||||||
(320, 240),
|
|
||||||
(640, 360),
|
|
||||||
(640, 480),
|
|
||||||
(720, 480),
|
|
||||||
(1280, 720),
|
|
||||||
(1920, 1080),
|
|
||||||
(2560, 1440)
|
|
||||||
]
|
|
||||||
for w, h in dims:
|
|
||||||
if w <= width and h <= height:
|
|
||||||
self.video_size.addItem(str(w) + ' * ' + str(h))
|
|
||||||
|
|
||||||
|
|
||||||
class PluginsSettings(CenteredWidget):
|
class PluginsSettings(CenteredWidget):
|
||||||
"""
|
"""
|
||||||
Plugins settings form
|
Plugins settings form
|
||||||
|
@ -14,8 +14,6 @@ import avwidgets
|
|||||||
import plugin_support
|
import plugin_support
|
||||||
import basecontact
|
import basecontact
|
||||||
import items_factory
|
import items_factory
|
||||||
import cv2
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
class Profile(basecontact.BaseContact, Singleton):
|
class Profile(basecontact.BaseContact, Singleton):
|
||||||
@ -38,7 +36,6 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||||||
self._tox = tox
|
self._tox = tox
|
||||||
self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number)
|
self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number)
|
||||||
self._call = calls.AV(tox.AV) # object with data about calls
|
self._call = calls.AV(tox.AV) # object with data about calls
|
||||||
self._call_widgets = {} # dict of incoming call widgets
|
|
||||||
self._incoming_calls = set()
|
self._incoming_calls = set()
|
||||||
self._load_history = True
|
self._load_history = True
|
||||||
self._waiting_for_reconnection = False
|
self._waiting_for_reconnection = False
|
||||||
@ -1229,9 +1226,10 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
else:
|
else:
|
||||||
friend.actions = True
|
friend.actions = True
|
||||||
self._call_widgets[friend_number] = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
|
# TODO: dict of widgets
|
||||||
self._call_widgets[friend_number].set_pixmap(friend.get_pixmap())
|
self._call_widget = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
|
||||||
self._call_widgets[friend_number].show()
|
self._call_widget.set_pixmap(friend.get_pixmap())
|
||||||
|
self._call_widget.show()
|
||||||
|
|
||||||
def accept_call(self, friend_number, audio, video):
|
def accept_call(self, friend_number, audio, video):
|
||||||
"""
|
"""
|
||||||
@ -1241,7 +1239,8 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||||||
self._screen.active_call()
|
self._screen.active_call()
|
||||||
if friend_number in self._incoming_calls:
|
if friend_number in self._incoming_calls:
|
||||||
self._incoming_calls.remove(friend_number)
|
self._incoming_calls.remove(friend_number)
|
||||||
del self._call_widgets[friend_number]
|
if hasattr(self, '_call_widget'):
|
||||||
|
del self._call_widget
|
||||||
|
|
||||||
def stop_call(self, friend_number, by_friend):
|
def stop_call(self, friend_number, by_friend):
|
||||||
"""
|
"""
|
||||||
@ -1255,9 +1254,8 @@ class Profile(basecontact.BaseContact, Singleton):
|
|||||||
self._screen.call_finished()
|
self._screen.call_finished()
|
||||||
self._call.finish_call(friend_number, by_friend) # finish or decline call
|
self._call.finish_call(friend_number, by_friend) # finish or decline call
|
||||||
if hasattr(self, '_call_widget'):
|
if hasattr(self, '_call_widget'):
|
||||||
self._call_widget[friend_number].close()
|
self._call_widget.close()
|
||||||
del self._call_widget[friend_number]
|
del self._call_widget
|
||||||
threading.Timer(2.0, lambda: cv2.destroyWindow(str(friend_number))).start()
|
|
||||||
friend = self.get_friend_by_number(friend_number)
|
friend = self.get_friend_by_number(friend_number)
|
||||||
friend.append_message(InfoMessage(text, time.time()))
|
friend.append_message(InfoMessage(text, time.time()))
|
||||||
if friend_number == self.get_active_number():
|
if friend_number == self.get_active_number():
|
||||||
|
@ -47,7 +47,6 @@ class Settings(dict, Singleton):
|
|||||||
self.audio = {'input': p.get_default_input_device_info()['index'] if input_devices else -1,
|
self.audio = {'input': p.get_default_input_device_info()['index'] if input_devices else -1,
|
||||||
'output': p.get_default_output_device_info()['index'] if output_devices else -1,
|
'output': p.get_default_output_device_info()['index'] if output_devices else -1,
|
||||||
'enabled': input_devices and output_devices}
|
'enabled': input_devices and output_devices}
|
||||||
self.video = {'device': 0, 'width': 640, 'height': 480}
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_auto_profile():
|
def get_auto_profile():
|
||||||
|
@ -30,8 +30,7 @@ def tox_dns(email):
|
|||||||
netman.setProxy(proxy)
|
netman.setProxy(proxy)
|
||||||
for url in urls:
|
for url in urls:
|
||||||
try:
|
try:
|
||||||
request = QtNetwork.QNetworkRequest()
|
request = QtNetwork.QNetworkRequest(url)
|
||||||
request.setUrl(QtCore.QUrl(url))
|
|
||||||
request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json")
|
request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, "application/json")
|
||||||
reply = netman.post(request, bytes(json.dumps(data), 'utf-8'))
|
reply = netman.post(request, bytes(json.dumps(data), 'utf-8'))
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import shutil
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|
||||||
program_version = '0.3.0'
|
program_version = '0.2.8'
|
||||||
|
|
||||||
|
|
||||||
def log(data):
|
def log(data):
|
||||||
|
Reference in New Issue
Block a user