start havine audio / video. Doesn't work correctly yet
This commit is contained in:
parent
659c5d2670
commit
e746ca9b1a
|
@ -17,10 +17,9 @@
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="spacing">1</property>
|
<property name="spacing">1</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkVBox" id="chat_control_vbox">
|
<object class="GtkPaned" id="paned1">
|
||||||
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="border_width">3</property>
|
|
||||||
<property name="spacing">1</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkVBox" id="vbox2">
|
<object class="GtkVBox" id="vbox2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -329,6 +328,7 @@
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||||
<property name="has_tooltip">True</property>
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Show a list of emoticons (Alt+M)</property>
|
||||||
<property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
|
<property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
|
@ -391,6 +391,7 @@
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||||
<property name="no_show_all">True</property>
|
<property name="no_show_all">True</property>
|
||||||
<property name="has_tooltip">True</property>
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Add this contact to roster (Ctrl+D)</property>
|
||||||
<property name="tooltip_text" translatable="yes">Add this contact to roster (Ctrl+D)</property>
|
<property name="tooltip_text" translatable="yes">Add this contact to roster (Ctrl+D)</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<child>
|
<child>
|
||||||
|
@ -481,6 +482,7 @@
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||||
<property name="has_tooltip">True</property>
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
|
||||||
<property name="tooltip_text" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
|
<property name="tooltip_text" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
|
@ -507,6 +509,7 @@
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||||
<property name="has_tooltip">True</property>
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Show the contact&apos;s profile (Ctrl+I)</property>
|
||||||
<property name="tooltip_text" translatable="yes">Show the contact's profile (Ctrl+I)</property>
|
<property name="tooltip_text" translatable="yes">Show the contact's profile (Ctrl+I)</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
|
@ -533,6 +536,7 @@
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||||
<property name="has_tooltip">True</property>
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Browse the chat history (Ctrl+H)</property>
|
||||||
<property name="tooltip_text" translatable="yes">Browse the chat history (Ctrl+H)</property>
|
<property name="tooltip_text" translatable="yes">Browse the chat history (Ctrl+H)</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
|
@ -540,7 +544,6 @@
|
||||||
<object class="GtkImage" id="history_image">
|
<object class="GtkImage" id="history_image">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
|
||||||
<property name="stock">gtk-justify-fill</property>
|
<property name="stock">gtk-justify-fill</property>
|
||||||
<property name="icon-size">1</property>
|
<property name="icon-size">1</property>
|
||||||
</object>
|
</object>
|
||||||
|
@ -571,6 +574,7 @@
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||||
<property name="has_tooltip">True</property>
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Show advanced functions (Alt+D)</property>
|
||||||
<property name="tooltip_text" translatable="yes">Show advanced functions (Alt+D)</property>
|
<property name="tooltip_text" translatable="yes">Show advanced functions (Alt+D)</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
|
@ -610,6 +614,7 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
|
<property name="has_tooltip">True</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
|
@ -631,6 +636,7 @@ audio-volume-medium</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
|
<property name="has_tooltip">True</property>
|
||||||
<property name="relief">none</property>
|
<property name="relief">none</property>
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="yalign">0.4699999988079071</property>
|
<property name="yalign">0.4699999988079071</property>
|
||||||
|
@ -701,6 +707,8 @@ audio-mic-volume-low</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
|
<property name="has_tooltip">True</property>
|
||||||
|
<property name="tooltip_markup" translatable="yes">Send message</property>
|
||||||
<property name="tooltip_text" translatable="yes">Send message</property>
|
<property name="tooltip_text" translatable="yes">Send message</property>
|
||||||
<property name="image">image4</property>
|
<property name="image">image4</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
|
@ -720,9 +728,80 @@ audio-mic-volume-low</property>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="resize">False</property>
|
||||||
<property name="fill">True</property>
|
<property name="shrink">True</property>
|
||||||
<property name="position">0</property>
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="video_hbox">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="no_show_all">True</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkFixed" id="outgoing_fixed">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="no_show_all">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport" id="viewport1">
|
||||||
|
<property name="width_request">160</property>
|
||||||
|
<property name="height_request">120</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkDrawingArea" id="outgoing_drawingarea">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="double_buffered">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="vbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport" id="viewport2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkDrawingArea" id="incoming_drawingarea">
|
||||||
|
<property name="width_request">320</property>
|
||||||
|
<property name="height_request">240</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="double_buffered">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="resize">True</property>
|
||||||
|
<property name="shrink">True</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -2363,7 +2363,7 @@ $T will be replaced by auto-not-available timeout</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">False</property>
|
||||||
<property name="position">0</property>
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
@ -2386,7 +2386,7 @@ $T will be replaced by auto-not-available timeout</property>
|
||||||
<object class="GtkTable" id="table8">
|
<object class="GtkTable" id="table8">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="n_rows">4</property>
|
<property name="n_rows">5</property>
|
||||||
<property name="n_columns">2</property>
|
<property name="n_columns">2</property>
|
||||||
<property name="column_spacing">6</property>
|
<property name="column_spacing">6</property>
|
||||||
<property name="row_spacing">6</property>
|
<property name="row_spacing">6</property>
|
||||||
|
@ -2494,6 +2494,21 @@ $T will be replaced by auto-not-available timeout</property>
|
||||||
<property name="y_options"/>
|
<property name="y_options"/>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="video_see_self_checkbutton">
|
||||||
|
<property name="label" translatable="yes">View own video source</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">2</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -1798,7 +1798,7 @@ class ChatControl(ChatControlBase):
|
||||||
# Video buttons
|
# Video buttons
|
||||||
self._video_button.set_sensitive(self.video_available)
|
self._video_button.set_sensitive(self.video_available)
|
||||||
|
|
||||||
# change tooltip text for audio and video buttons if python-farstream is
|
# change tooltip text for audio and video buttons if farstream is
|
||||||
# not installed
|
# not installed
|
||||||
audio_tooltip_text = _('Toggle audio session') + '\n'
|
audio_tooltip_text = _('Toggle audio session') + '\n'
|
||||||
video_tooltip_text = _('Toggle video session') + '\n'
|
video_tooltip_text = _('Toggle video session') + '\n'
|
||||||
|
|
|
@ -184,16 +184,19 @@ except Exception:
|
||||||
|
|
||||||
HAVE_FARSTREAM = True
|
HAVE_FARSTREAM = True
|
||||||
try:
|
try:
|
||||||
raise ImportError
|
import gi
|
||||||
farstream = __import__('farstream')
|
gi.require_version('Farstream', '0.2')
|
||||||
import gst
|
from gi.repository import Farstream
|
||||||
import glib
|
gi.require_version('Gst', '1.0')
|
||||||
|
from gi.repository import Gst
|
||||||
|
from gi.repository import GLib
|
||||||
try:
|
try:
|
||||||
conference = gst.element_factory_make('fsrtpconference')
|
Gst.init(None)
|
||||||
session = conference.new_session(farstream.MEDIA_TYPE_AUDIO)
|
conference = Gst.ElementFactory.make('fsrtpconference', None)
|
||||||
|
session = conference.new_session(Farstream.MediaType.AUDIO)
|
||||||
del session
|
del session
|
||||||
del conference
|
del conference
|
||||||
except:
|
except GLib.GError:
|
||||||
HAVE_FARSTREAM = False
|
HAVE_FARSTREAM = False
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -61,7 +61,7 @@ class ConnectionJingle(object):
|
||||||
"""
|
"""
|
||||||
if sid in self._sessions:
|
if sid in self._sessions:
|
||||||
#FIXME: Move this elsewhere?
|
#FIXME: Move this elsewhere?
|
||||||
for content in self._sessions[sid].contents.values():
|
for content in list(self._sessions[sid].contents.values()):
|
||||||
content.destroy()
|
content.destroy()
|
||||||
self._sessions[sid].callbacks = []
|
self._sessions[sid].callbacks = []
|
||||||
del self._sessions[sid]
|
del self._sessions[sid]
|
||||||
|
|
|
@ -23,9 +23,11 @@ from gi.repository import GLib
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
import farstream
|
from gi.repository import Farstream
|
||||||
import gst
|
import gi
|
||||||
from glib import GError
|
gi.require_version('Gst', '1.0')
|
||||||
|
from gi.repository import Gst
|
||||||
|
from gi.repository import GLib
|
||||||
|
|
||||||
from common import gajim
|
from common import gajim
|
||||||
|
|
||||||
|
@ -45,8 +47,8 @@ class JingleRTPContent(JingleContent):
|
||||||
JingleContent.__init__(self, session, transport)
|
JingleContent.__init__(self, session, transport)
|
||||||
self.media = media
|
self.media = media
|
||||||
self._dtmf_running = False
|
self._dtmf_running = False
|
||||||
self.farstream_media = {'audio': farstream.MEDIA_TYPE_AUDIO,
|
self.farstream_media = {'audio': Farstream.MediaType.AUDIO,
|
||||||
'video': farstream.MEDIA_TYPE_VIDEO}[media]
|
'video': Farstream.MediaType.VIDEO}[media]
|
||||||
|
|
||||||
self.pipeline = None
|
self.pipeline = None
|
||||||
self.src_bin = None
|
self.src_bin = None
|
||||||
|
@ -64,13 +66,13 @@ class JingleRTPContent(JingleContent):
|
||||||
|
|
||||||
def setup_stream(self, on_src_pad_added):
|
def setup_stream(self, on_src_pad_added):
|
||||||
# pipeline and bus
|
# pipeline and bus
|
||||||
self.pipeline = gst.Pipeline()
|
self.pipeline = Gst.Pipeline()
|
||||||
bus = self.pipeline.get_bus()
|
bus = self.pipeline.get_bus()
|
||||||
bus.add_signal_watch()
|
bus.add_signal_watch()
|
||||||
bus.connect('message', self._on_gst_message)
|
bus.connect('message', self._on_gst_message)
|
||||||
|
|
||||||
# conference
|
# conference
|
||||||
self.conference = gst.element_factory_make('fsrtpconference')
|
self.conference = Gst.ElementFactory.make('fsrtpconference', None)
|
||||||
self.pipeline.add(self.conference)
|
self.pipeline.add(self.conference)
|
||||||
self.funnel = None
|
self.funnel = None
|
||||||
|
|
||||||
|
@ -80,6 +82,7 @@ class JingleRTPContent(JingleContent):
|
||||||
# FIXME: Consider a workaround, here...
|
# FIXME: Consider a workaround, here...
|
||||||
# pidgin and telepathy-gabble don't follow the XEP, and it won't work
|
# pidgin and telepathy-gabble don't follow the XEP, and it won't work
|
||||||
# due to bad controlling-mode
|
# due to bad controlling-mode
|
||||||
|
|
||||||
params = {'controlling-mode': self.session.weinitiate, 'debug': False}
|
params = {'controlling-mode': self.session.weinitiate, 'debug': False}
|
||||||
if gajim.config.get('use_stun_server'):
|
if gajim.config.get('use_stun_server'):
|
||||||
stun_server = gajim.config.get('stun_server')
|
stun_server = gajim.config.get('stun_server')
|
||||||
|
@ -88,16 +91,16 @@ class JingleRTPContent(JingleContent):
|
||||||
if stun_server:
|
if stun_server:
|
||||||
try:
|
try:
|
||||||
ip = socket.getaddrinfo(stun_server, 0, socket.AF_UNSPEC,
|
ip = socket.getaddrinfo(stun_server, 0, socket.AF_UNSPEC,
|
||||||
socket.SOCK_STREAM)[0][4][0]
|
socket.SOCK_STREAM)[0][4][0]
|
||||||
except socket.gaierror as e:
|
except socket.gaierror as e:
|
||||||
log.warning('Lookup of stun ip failed: %s' % str(e))
|
log.warning('Lookup of stun ip failed: %s' % str(e))
|
||||||
else:
|
else:
|
||||||
params['stun-ip'] = ip
|
params['stun-ip'] = ip
|
||||||
|
|
||||||
self.p2pstream = self.p2psession.new_stream(participant,
|
self.p2pstream = self.p2psession.new_stream(participant,
|
||||||
farstream.DIRECTION_RECV)
|
Farstream.StreamDirection.RECV)
|
||||||
self.p2pstream.connect('src-pad-added', on_src_pad_added)
|
self.p2pstream.connect('src-pad-added', on_src_pad_added)
|
||||||
self.p2pstream.set_transmitter('nice', params)
|
self.p2pstream.set_transmitter_ht('nice', params)
|
||||||
|
|
||||||
def is_ready(self):
|
def is_ready(self):
|
||||||
return (JingleContent.is_ready(self) and self.candidates_ready)
|
return (JingleContent.is_ready(self) and self.candidates_ready)
|
||||||
|
@ -105,9 +108,9 @@ class JingleRTPContent(JingleContent):
|
||||||
def make_bin_from_config(self, config_key, pipeline, text):
|
def make_bin_from_config(self, config_key, pipeline, text):
|
||||||
pipeline = pipeline % gajim.config.get(config_key)
|
pipeline = pipeline % gajim.config.get(config_key)
|
||||||
try:
|
try:
|
||||||
bin = gst.parse_bin_from_description(pipeline, True)
|
bin = Gst.parse_bin_from_description(pipeline, True)
|
||||||
return bin
|
return bin
|
||||||
except GError as e:
|
except GLib.GError as e:
|
||||||
gajim.nec.push_incoming_event(InformationEvent(None,
|
gajim.nec.push_incoming_event(InformationEvent(None,
|
||||||
conn=self.session.connection, level='error',
|
conn=self.session.connection, level='error',
|
||||||
pri_txt=_('%s configuration error') % text.capitalize(),
|
pri_txt=_('%s configuration error') % text.capitalize(),
|
||||||
|
@ -144,8 +147,8 @@ class JingleRTPContent(JingleContent):
|
||||||
|
|
||||||
def _start_dtmf(self, event):
|
def _start_dtmf(self, event):
|
||||||
if event in ('*', '#'):
|
if event in ('*', '#'):
|
||||||
event = {'*': farstream.DTMF_EVENT_STAR,
|
event = {'*': Farstream.DTMFEvent.STAR,
|
||||||
'#': farstream.DTMF_EVENT_POUND}[event]
|
'#': Farstream.DTMFEvent.POUND}[event]
|
||||||
else:
|
else:
|
||||||
event = int(event)
|
event = int(event)
|
||||||
self.p2psession.start_telephony_event(event, 2)
|
self.p2psession.start_telephony_event(event, 2)
|
||||||
|
@ -155,30 +158,30 @@ class JingleRTPContent(JingleContent):
|
||||||
|
|
||||||
def _fill_content(self, content):
|
def _fill_content(self, content):
|
||||||
content.addChild(nbxmpp.NS_JINGLE_RTP + ' description',
|
content.addChild(nbxmpp.NS_JINGLE_RTP + ' description',
|
||||||
attrs={'media': self.media}, payload=self.iter_codecs())
|
attrs={'media': self.media}, payload=self.iter_codecs())
|
||||||
|
|
||||||
def _setup_funnel(self):
|
def _setup_funnel(self):
|
||||||
self.funnel = gst.element_factory_make('fsfunnel')
|
self.funnel = Gst.ElementFactory.make('funnel', None)
|
||||||
self.pipeline.add(self.funnel)
|
self.pipeline.add(self.funnel)
|
||||||
self.funnel.set_state(gst.STATE_PLAYING)
|
|
||||||
self.sink.set_state(gst.STATE_PLAYING)
|
|
||||||
self.funnel.link(self.sink)
|
self.funnel.link(self.sink)
|
||||||
|
self.sink.set_state(Gst.State.PLAYING)
|
||||||
|
self.funnel.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
def _on_src_pad_added(self, stream, pad, codec):
|
def _on_src_pad_added(self, stream, pad, codec):
|
||||||
if not self.funnel:
|
if not self.funnel:
|
||||||
self._setup_funnel()
|
self._setup_funnel()
|
||||||
pad.link(self.funnel.get_pad('sink%d'))
|
pad.link(self.funnel.get_request_pad('sink_%u'))
|
||||||
|
|
||||||
def _on_gst_message(self, bus, message):
|
def _on_gst_message(self, bus, message):
|
||||||
if message.type == gst.MESSAGE_ELEMENT:
|
if message.type == Gst.MessageType.ELEMENT:
|
||||||
name = message.structure.get_name()
|
name = message.get_structure().get_name()
|
||||||
log.debug('gst element message: %s: %s' % (name, message))
|
log.debug('gst element message: %s: %s' % (name, message))
|
||||||
if name == 'farstream-new-active-candidate-pair':
|
if name == 'farstream-new-active-candidate-pair':
|
||||||
pass
|
pass
|
||||||
elif name == 'farstream-recv-codecs-changed':
|
elif name == 'farstream-recv-codecs-changed':
|
||||||
pass
|
pass
|
||||||
elif name == 'farstream-codecs-changed':
|
elif name == 'farstream-codecs-changed':
|
||||||
if self.sent and self.p2psession.get_property('codecs'):
|
if self.sent and self.p2psession.props.codecs_without_config:
|
||||||
self.send_description_info()
|
self.send_description_info()
|
||||||
if self.transport.remote_candidates:
|
if self.transport.remote_candidates:
|
||||||
# those lines MUST be done after we get info on our
|
# those lines MUST be done after we get info on our
|
||||||
|
@ -187,29 +190,30 @@ class JingleRTPContent(JingleContent):
|
||||||
self.transport.remote_candidates)
|
self.transport.remote_candidates)
|
||||||
self.transport.remote_candidates = []
|
self.transport.remote_candidates = []
|
||||||
self.p2pstream.set_property('direction',
|
self.p2pstream.set_property('direction',
|
||||||
farstream.DIRECTION_BOTH)
|
Farstream.StreamDirection.BOTH)
|
||||||
|
|
||||||
elif name == 'farstream-local-candidates-prepared':
|
elif name == 'farstream-local-candidates-prepared':
|
||||||
self.candidates_ready = True
|
self.candidates_ready = True
|
||||||
if self.is_ready():
|
if self.is_ready():
|
||||||
self.session.on_session_state_changed(self)
|
self.session.on_session_state_changed(self)
|
||||||
elif name == 'farstream-new-local-candidate':
|
elif name == 'farstream-new-local-candidate':
|
||||||
candidate = message.structure['candidate']
|
candidate = message.get_structure().get_value('candidate').copy(
|
||||||
|
)
|
||||||
self.transport.candidates.append(candidate)
|
self.transport.candidates.append(candidate)
|
||||||
if self.sent:
|
if self.sent:
|
||||||
# FIXME: Is this case even possible?
|
# FIXME: Is this case even possible?
|
||||||
self.send_candidate(candidate)
|
self.send_candidate(candidate)
|
||||||
elif name == 'farstream-component-state-changed':
|
elif name == 'farstream-component-state-changed':
|
||||||
state = message.structure['state']
|
state = message.get_structure().get_value('state')
|
||||||
if state == farstream.STREAM_STATE_FAILED:
|
if state == Farstream.StreamState.FAILED:
|
||||||
reason = nbxmpp.Node('reason')
|
reason = nbxmpp.Node('reason')
|
||||||
reason.setTag('failed-transport')
|
reason.setTag('failed-transport')
|
||||||
self.session.remove_content(self.creator, self.name, reason)
|
self.session.remove_content(self.creator, self.name, reason)
|
||||||
elif name == 'farstream-error':
|
elif name == 'farstream-error':
|
||||||
log.error('Farstream error #%d!\nMessage: %s' % (
|
log.error('Farstream error #%d!\nMessage: %s' % (
|
||||||
message.structure['error-no'],
|
message.get_structure().get_value('error-no'),
|
||||||
message.structure['error-msg']))
|
message.get_structure().get_value('error-msg')))
|
||||||
elif message.type == gst.MESSAGE_ERROR:
|
elif message.type == Gst.MessageType.ERROR:
|
||||||
# TODO: Fix it to fallback to videotestsrc anytime an error occur,
|
# TODO: Fix it to fallback to videotestsrc anytime an error occur,
|
||||||
# or raise an error, Jingle way
|
# or raise an error, Jingle way
|
||||||
# or maybe one-sided stream?
|
# or maybe one-sided stream?
|
||||||
|
@ -217,21 +221,21 @@ class JingleRTPContent(JingleContent):
|
||||||
gajim.nec.push_incoming_event(InformationEvent(None,
|
gajim.nec.push_incoming_event(InformationEvent(None,
|
||||||
conn=self.session.connection, level='error',
|
conn=self.session.connection, level='error',
|
||||||
pri_txt=_('GStreamer error'), sec_txt=_('Error: %s\nDebug: '
|
pri_txt=_('GStreamer error'), sec_txt=_('Error: %s\nDebug: '
|
||||||
'%s' % (message.structure['gerror'],
|
'%s' % (message.get_structure().get_value('gerror'),
|
||||||
message.structure['debug']))))
|
message.get_structure().get_value('debug')))))
|
||||||
|
|
||||||
sink_pad = self.p2psession.get_property('sink-pad')
|
sink_pad = self.p2psession.get_property('sink-pad')
|
||||||
|
|
||||||
# Remove old source
|
# Remove old source
|
||||||
self.src_bin.get_pad('src').unlink(sink_pad)
|
self.src_bin.get_static_pad('src').unlink(sink_pad)
|
||||||
self.src_bin.set_state(gst.STATE_NULL)
|
self.src_bin.set_state(Gst.State.NULL)
|
||||||
self.pipeline.remove(self.src_bin)
|
self.pipeline.remove(self.src_bin)
|
||||||
|
|
||||||
if not self.stream_failed_once:
|
if not self.stream_failed_once:
|
||||||
# Add fallback source
|
# Add fallback source
|
||||||
self.src_bin = self.get_fallback_src()
|
self.src_bin = self.get_fallback_src()
|
||||||
self.pipeline.add(self.src_bin)
|
self.pipeline.add(self.src_bin)
|
||||||
self.src_bin.get_pad('src').link(sink_pad)
|
self.src_bin.get_static_pad('src').link(sink_pad)
|
||||||
self.stream_failed_once = True
|
self.stream_failed_once = True
|
||||||
else:
|
else:
|
||||||
reason = nbxmpp.Node('reason')
|
reason = nbxmpp.Node('reason')
|
||||||
|
@ -239,10 +243,10 @@ class JingleRTPContent(JingleContent):
|
||||||
self.session.remove_content(self.creator, self.name, reason)
|
self.session.remove_content(self.creator, self.name, reason)
|
||||||
|
|
||||||
# Start playing again
|
# Start playing again
|
||||||
self.pipeline.set_state(gst.STATE_PLAYING)
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
def get_fallback_src(self):
|
def get_fallback_src(self):
|
||||||
return gst.element_factory_make('fakesrc')
|
return Gst.ElementFactory.make('fakesrc', None)
|
||||||
|
|
||||||
def on_negotiated(self):
|
def on_negotiated(self):
|
||||||
if self.accepted:
|
if self.accepted:
|
||||||
|
@ -252,9 +256,9 @@ class JingleRTPContent(JingleContent):
|
||||||
self.p2pstream.add_remote_candidates(
|
self.p2pstream.add_remote_candidates(
|
||||||
self.transport.remote_candidates)
|
self.transport.remote_candidates)
|
||||||
self.transport.remote_candidates = []
|
self.transport.remote_candidates = []
|
||||||
# TODO: farstream.DIRECTION_BOTH only if senders='both'
|
# TODO: Farstream.StreamDirection.BOTH only if senders='both'
|
||||||
self.p2pstream.set_property('direction',
|
# self.p2pstream.set_property('direction',
|
||||||
farstream.DIRECTION_BOTH)
|
# Farstream.StreamDirection.BOTH)
|
||||||
JingleContent.on_negotiated(self)
|
JingleContent.on_negotiated(self)
|
||||||
|
|
||||||
def __on_remote_codecs(self, stanza, content, error, action):
|
def __on_remote_codecs(self, stanza, content, error, action):
|
||||||
|
@ -267,39 +271,39 @@ class JingleRTPContent(JingleContent):
|
||||||
if not codec['id'] or not codec['name'] or not codec['clockrate']:
|
if not codec['id'] or not codec['name'] or not codec['clockrate']:
|
||||||
# ignore invalid payload-types
|
# ignore invalid payload-types
|
||||||
continue
|
continue
|
||||||
c = farstream.Codec(int(codec['id']), codec['name'],
|
c = Farstream.Codec.new(int(codec['id']), codec['name'],
|
||||||
self.farstream_media, int(codec['clockrate']))
|
self.farstream_media, int(codec['clockrate']))
|
||||||
if 'channels' in codec:
|
if 'channels' in codec:
|
||||||
c.channels = int(codec['channels'])
|
c.channels = int(codec['channels'])
|
||||||
else:
|
else:
|
||||||
c.channels = 1
|
c.channels = 1
|
||||||
c.optional_params = [(str(p['name']), str(p['value'])) for p in \
|
for p in codec.iterTags('parameter'):
|
||||||
codec.iterTags('parameter')]
|
c.add_optional_parameter(p['name'], str(p['value']))
|
||||||
codecs.append(c)
|
codecs.append(c)
|
||||||
|
|
||||||
if codecs:
|
if codecs:
|
||||||
# FIXME: Handle this case:
|
# FIXME: Handle this case:
|
||||||
# glib.GError: There was no intersection between the remote codecs and
|
# GLib.GError: There was no intersection between the remote codecs and
|
||||||
# the local ones
|
# the local ones
|
||||||
self.p2pstream.set_remote_codecs(codecs)
|
self.p2pstream.set_remote_codecs(codecs)
|
||||||
|
|
||||||
def iter_codecs(self):
|
def iter_codecs(self):
|
||||||
codecs = self.p2psession.get_property('codecs')
|
codecs = self.p2psession.props.codecs_without_config
|
||||||
for codec in codecs:
|
for codec in codecs:
|
||||||
attrs = {'name': codec.encoding_name,
|
attrs = {'name': codec.encoding_name,
|
||||||
'id': codec.id,
|
'id': codec.id,
|
||||||
'channels': codec.channels}
|
'channels': codec.channels}
|
||||||
if codec.clock_rate:
|
if codec.clock_rate:
|
||||||
attrs['clockrate'] = codec.clock_rate
|
attrs['clockrate'] = codec.clock_rate
|
||||||
if codec.optional_params:
|
if codec.optional_params:
|
||||||
payload = (nbxmpp.Node('parameter', {'name': name,
|
payload = (nbxmpp.Node('parameter', {'name': p.name,
|
||||||
'value': value}) for name, value in codec.optional_params)
|
'value': p.value}) for p in codec.optional_params)
|
||||||
else:
|
else:
|
||||||
payload = ()
|
payload = ()
|
||||||
yield nbxmpp.Node('payload-type', attrs, payload)
|
yield nbxmpp.Node('payload-type', attrs, payload)
|
||||||
|
|
||||||
def __stop(self, *things):
|
def __stop(self, *things):
|
||||||
self.pipeline.set_state(gst.STATE_NULL)
|
self.pipeline.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.__stop()
|
self.__stop()
|
||||||
|
@ -341,31 +345,32 @@ class JingleAudio(JingleRTPContent):
|
||||||
# place 16kHz before 8kHz, as buggy psi versions will take in
|
# place 16kHz before 8kHz, as buggy psi versions will take in
|
||||||
# account only the first codec
|
# account only the first codec
|
||||||
|
|
||||||
codecs = [farstream.Codec(farstream.CODEC_ID_ANY, 'SPEEX',
|
codecs = [Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
|
||||||
farstream.MEDIA_TYPE_AUDIO, 16000),
|
Farstream.MediaType.AUDIO, 16000),
|
||||||
farstream.Codec(farstream.CODEC_ID_ANY, 'SPEEX',
|
Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
|
||||||
farstream.MEDIA_TYPE_AUDIO, 8000)]
|
Farstream.MediaType.AUDIO, 8000)]
|
||||||
self.p2psession.set_codec_preferences(codecs)
|
self.p2psession.set_codec_preferences(codecs)
|
||||||
|
|
||||||
# the local parts
|
# the local parts
|
||||||
# TODO: Add queues?
|
# TODO: Add queues?
|
||||||
self.src_bin = self.make_bin_from_config('audio_input_device',
|
self.src_bin = self.make_bin_from_config('audio_input_device',
|
||||||
'%s ! audioconvert', _("audio input"))
|
'%s ! audioconvert', _("audio input"))
|
||||||
|
|
||||||
self.sink = self.make_bin_from_config('audio_output_device',
|
self.sink = self.make_bin_from_config('audio_output_device',
|
||||||
'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output"))
|
'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output"))
|
||||||
|
|
||||||
self.mic_volume = self.src_bin.get_by_name('gajim_vol')
|
self.mic_volume = self.src_bin.get_by_name('gajim_vol')
|
||||||
self.out_volume = self.sink.get_by_name('gajim_out_vol')
|
self.out_volume = self.sink.get_by_name('gajim_out_vol')
|
||||||
|
|
||||||
# link gst elements
|
# link gst elements
|
||||||
self.pipeline.add(self.sink, self.src_bin)
|
self.pipeline.add(self.sink)
|
||||||
|
self.pipeline.add(self.src_bin)
|
||||||
|
|
||||||
self.src_bin.get_pad('src').link(self.p2psession.get_property(
|
self.src_bin.get_static_pad('src').link(self.p2psession.get_property(
|
||||||
'sink-pad'))
|
'sink-pad'))
|
||||||
|
|
||||||
# The following is needed for farstream to process ICE requests:
|
# The following is needed for farstream to process ICE requests:
|
||||||
self.pipeline.set_state(gst.STATE_PLAYING)
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
|
||||||
class JingleVideo(JingleRTPContent):
|
class JingleVideo(JingleRTPContent):
|
||||||
|
@ -381,7 +386,7 @@ class JingleVideo(JingleRTPContent):
|
||||||
|
|
||||||
# the local parts
|
# the local parts
|
||||||
if gajim.config.get('video_framerate'):
|
if gajim.config.get('video_framerate'):
|
||||||
framerate = 'videorate ! video/x-raw-yuv,framerate=%s ! ' % \
|
framerate = 'videorate ! video/x-raw,framerate=%s ! ' % \
|
||||||
gajim.config.get('video_framerate')
|
gajim.config.get('video_framerate')
|
||||||
else:
|
else:
|
||||||
framerate = ''
|
framerate = ''
|
||||||
|
@ -390,11 +395,11 @@ class JingleVideo(JingleRTPContent):
|
||||||
except:
|
except:
|
||||||
w = h = None
|
w = h = None
|
||||||
if w and h:
|
if w and h:
|
||||||
video_size = 'video/x-raw-yuv,width=%s,height=%s ! ' % (w, h)
|
video_size = 'video/x-raw,width=%s,height=%s ! ' % (w, h)
|
||||||
else:
|
else:
|
||||||
video_size = ''
|
video_size = ''
|
||||||
self.src_bin = self.make_bin_from_config('video_input_device',
|
self.src_bin = self.make_bin_from_config('video_input_device',
|
||||||
'%%s ! %svideoscale ! %sffmpegcolorspace' % (framerate, video_size),
|
'%%s ! %svideoscale ! %svideoconvert' % (framerate, video_size),
|
||||||
_("video input"))
|
_("video input"))
|
||||||
#caps = gst.element_factory_make('capsfilter')
|
#caps = gst.element_factory_make('capsfilter')
|
||||||
#caps.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=320, height=240'))
|
#caps.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=320, height=240'))
|
||||||
|
@ -403,20 +408,20 @@ class JingleVideo(JingleRTPContent):
|
||||||
#src_bin.link(caps)
|
#src_bin.link(caps)
|
||||||
|
|
||||||
self.sink = self.make_bin_from_config('video_output_device',
|
self.sink = self.make_bin_from_config('video_output_device',
|
||||||
'videoscale ! ffmpegcolorspace ! %s force-aspect-ratio=True',
|
'videoscale ! videoconvert ! %s force-aspect-ratio=True',
|
||||||
_("video output"))
|
_("video output"))
|
||||||
self.pipeline.add(self.sink)
|
self.pipeline.add(self.sink)
|
||||||
|
|
||||||
self.src_bin.get_pad('src').link(self.p2psession.get_property(
|
self.src_bin.get_static_pad('src').link(self.p2psession.get_property(
|
||||||
'sink-pad'))
|
'sink-pad'))
|
||||||
|
|
||||||
# The following is needed for farstream to process ICE requests:
|
# The following is needed for farstream to process ICE requests:
|
||||||
self.pipeline.set_state(gst.STATE_PLAYING)
|
self.pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
def get_fallback_src(self):
|
def get_fallback_src(self):
|
||||||
# TODO: Use avatar?
|
# TODO: Use avatar?
|
||||||
pipeline = 'videotestsrc is-live=true ! video/x-raw-yuv,framerate=10/1 ! ffmpegcolorspace'
|
pipeline = 'videotestsrc is-live=true ! video/x-raw,framerate=10/1 ! videoconvert'
|
||||||
return gst.parse_bin_from_description(pipeline, True)
|
return Gst.parse_bin_from_description(pipeline, True)
|
||||||
|
|
||||||
def get_content(desc):
|
def get_content(desc):
|
||||||
if desc['media'] == 'audio':
|
if desc['media'] == 'audio':
|
||||||
|
|
|
@ -343,8 +343,7 @@ class JingleTransportIBB(JingleTransport):
|
||||||
return transport
|
return transport
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raise ImportError
|
from gi.repository import Farstream
|
||||||
import farstream
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -353,24 +352,24 @@ class JingleTransportICEUDP(JingleTransport):
|
||||||
JingleTransport.__init__(self, TransportType.ICEUDP)
|
JingleTransport.__init__(self, TransportType.ICEUDP)
|
||||||
|
|
||||||
def make_candidate(self, candidate):
|
def make_candidate(self, candidate):
|
||||||
types = {farstream.CANDIDATE_TYPE_HOST: 'host',
|
types = {Farstream.CandidateType.HOST: 'host',
|
||||||
farstream.CANDIDATE_TYPE_SRFLX: 'srflx',
|
Farstream.CandidateType.SRFLX: 'srflx',
|
||||||
farstream.CANDIDATE_TYPE_PRFLX: 'prflx',
|
Farstream.CandidateType.PRFLX: 'prflx',
|
||||||
farstream.CANDIDATE_TYPE_RELAY: 'relay',
|
Farstream.CandidateType.RELAY: 'relay',
|
||||||
farstream.CANDIDATE_TYPE_MULTICAST: 'multicast'}
|
Farstream.CandidateType.MULTICAST: 'multicast'}
|
||||||
attrs = {
|
attrs = {
|
||||||
'component': candidate.component_id,
|
'component': candidate.component_id,
|
||||||
'foundation': '1', # hack
|
'foundation': '1', # hack
|
||||||
'generation': '0',
|
'generation': '0',
|
||||||
'ip': candidate.ip,
|
'ip': candidate.ip,
|
||||||
'network': '0',
|
'network': '0',
|
||||||
'port': candidate.port,
|
'port': candidate.port,
|
||||||
'priority': int(candidate.priority), # hack
|
'priority': int(candidate.priority), # hack
|
||||||
'id': gajim.get_an_id()
|
'id': gajim.get_an_id()
|
||||||
}
|
}
|
||||||
if candidate.type in types:
|
if candidate.type in types:
|
||||||
attrs['type'] = types[candidate.type]
|
attrs['type'] = types[candidate.type]
|
||||||
if candidate.proto == farstream.NETWORK_PROTOCOL_UDP:
|
if candidate.proto == Farstream.NetworkProtocol.UDP:
|
||||||
attrs['protocol'] = 'udp'
|
attrs['protocol'] = 'udp'
|
||||||
else:
|
else:
|
||||||
# we actually don't handle properly different tcp options in jingle
|
# we actually don't handle properly different tcp options in jingle
|
||||||
|
@ -389,33 +388,37 @@ class JingleTransportICEUDP(JingleTransport):
|
||||||
def parse_transport_stanza(self, transport):
|
def parse_transport_stanza(self, transport):
|
||||||
candidates = []
|
candidates = []
|
||||||
for candidate in transport.iterTags('candidate'):
|
for candidate in transport.iterTags('candidate'):
|
||||||
cand = farstream.Candidate()
|
foundation = str(candidate['foundation'])
|
||||||
cand.component_id = int(candidate['component'])
|
component_id = int(candidate['component'])
|
||||||
cand.ip = str(candidate['ip'])
|
ip = str(candidate['ip'])
|
||||||
cand.port = int(candidate['port'])
|
port = int(candidate['port'])
|
||||||
cand.foundation = str(candidate['foundation'])
|
base_ip = None
|
||||||
#cand.type = farstream.CANDIDATE_TYPE_LOCAL
|
base_port = 0
|
||||||
cand.priority = int(candidate['priority'])
|
|
||||||
|
|
||||||
if candidate['protocol'] == 'udp':
|
if candidate['protocol'] == 'udp':
|
||||||
cand.proto = farstream.NETWORK_PROTOCOL_UDP
|
proto = Farstream.NetworkProtocol.UDP
|
||||||
else:
|
else:
|
||||||
# we actually don't handle properly different tcp options in jingle
|
# we actually don't handle properly different tcp options in
|
||||||
cand.proto = farstream.NETWORK_PROTOCOL_TCP
|
# jingle
|
||||||
|
proto = Farstream.NetworkProtocol.TCP
|
||||||
cand.username = str(transport['ufrag'])
|
priority = int(candidate['priority'])
|
||||||
cand.password = str(transport['pwd'])
|
types = {'host': Farstream.CandidateType.HOST,
|
||||||
|
'srflx': Farstream.CandidateType.SRFLX,
|
||||||
#FIXME: huh?
|
'prflx': Farstream.CandidateType.PRFLX,
|
||||||
types = {'host': farstream.CANDIDATE_TYPE_HOST,
|
'relay': Farstream.CandidateType.RELAY,
|
||||||
'srflx': farstream.CANDIDATE_TYPE_SRFLX,
|
'multicast': Farstream.CandidateType.MULTICAST}
|
||||||
'prflx': farstream.CANDIDATE_TYPE_PRFLX,
|
|
||||||
'relay': farstream.CANDIDATE_TYPE_RELAY,
|
|
||||||
'multicast': farstream.CANDIDATE_TYPE_MULTICAST}
|
|
||||||
if 'type' in candidate and candidate['type'] in types:
|
if 'type' in candidate and candidate['type'] in types:
|
||||||
cand.type = types[candidate['type']]
|
type_ = types[candidate['type']]
|
||||||
else:
|
else:
|
||||||
print('Unknown type %s' % candidate['type'])
|
log.warning('Unknown type %s' % candidate['type'])
|
||||||
|
type_ = Farstream.CandidateType.HOST
|
||||||
|
username = str(transport['ufrag'])
|
||||||
|
password = str(transport['pwd'])
|
||||||
|
ttl = 0
|
||||||
|
|
||||||
|
cand = Farstream.Candidate.new_full(foundation, component_id, ip,
|
||||||
|
port, base_ip, base_port, proto, priority, type_, username,
|
||||||
|
password, ttl)
|
||||||
|
|
||||||
candidates.append(cand)
|
candidates.append(cand)
|
||||||
self.remote_candidates.extend(candidates)
|
self.remote_candidates.extend(candidates)
|
||||||
return candidates
|
return candidates
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
## You should have received a copy of the GNU General Public License
|
## You should have received a copy of the GNU General Public License
|
||||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import gst
|
from gi.repository import Gst
|
||||||
|
|
||||||
|
|
||||||
class DeviceManager(object):
|
class DeviceManager(object):
|
||||||
|
@ -29,29 +29,24 @@ class DeviceManager(object):
|
||||||
return self.devices
|
return self.devices
|
||||||
|
|
||||||
def detect_element(self, name, text, pipe='%s'):
|
def detect_element(self, name, text, pipe='%s'):
|
||||||
try:
|
if Gst.ElementFactory.find(name):
|
||||||
if gst.element_factory_find(name):
|
element = Gst.ElementFactory.make(name, '%spresencetest' % name)
|
||||||
element = gst.element_factory_make(name,
|
if hasattr(element.props, 'device'):
|
||||||
'%spresencetest' % name)
|
element.set_state(Gst.State.READY)
|
||||||
if isinstance(element, gst.interfaces.PropertyProbe):
|
devices = element.get_properties('device')
|
||||||
element.set_state(gst.STATE_READY)
|
if devices:
|
||||||
element.probe_property_name('device')
|
self.devices[text % _(' Default device')] = pipe % name
|
||||||
devices = element.probe_get_values_name('device')
|
for device in devices:
|
||||||
if devices:
|
element.set_state(Gst.State.NULL)
|
||||||
self.devices[text % _(' Default device')] = pipe % name
|
element.set_property('device', device)
|
||||||
for device in devices:
|
element.set_state(Gst.State.READY)
|
||||||
element.set_state(gst.STATE_NULL)
|
device_name = element.get_property('device-name')
|
||||||
element.set_property('device', device)
|
self.devices[text % device_name] = pipe % \
|
||||||
element.set_state(gst.STATE_READY)
|
'%s device=%s' % (name, device)
|
||||||
device_name = element.get_property('device-name')
|
element.set_state(Gst.State.NULL)
|
||||||
self.devices[text % device_name] = pipe % \
|
else:
|
||||||
'%s device=%s' % (name, device)
|
self.devices[text] = pipe % name
|
||||||
element.set_state(gst.STATE_NULL)
|
else:
|
||||||
else:
|
|
||||||
self.devices[text] = pipe % name
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
except gst.ElementNotFoundError:
|
|
||||||
print('element \'%s\' not found' % name)
|
print('element \'%s\' not found' % name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,6 @@ from common import gpg
|
||||||
from common import ged
|
from common import ged
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raise ImportError
|
|
||||||
from common.multimedia_helpers import AudioInputManager, AudioOutputManager
|
from common.multimedia_helpers import AudioInputManager, AudioOutputManager
|
||||||
from common.multimedia_helpers import VideoInputManager, VideoOutputManager
|
from common.multimedia_helpers import VideoInputManager, VideoOutputManager
|
||||||
HAS_GST = True
|
HAS_GST = True
|
||||||
|
|
|
@ -97,7 +97,7 @@ class FeaturesWindow:
|
||||||
_('Requires python-docutils.')),
|
_('Requires python-docutils.')),
|
||||||
_('Audio / Video'): (self.farstream_available,
|
_('Audio / Video'): (self.farstream_available,
|
||||||
_('Ability to start audio and video chat.'),
|
_('Ability to start audio and video chat.'),
|
||||||
_('Requires python-farstream and gstreamer-plugins-bad.'),
|
_('Requires gir1.2-farstream-0.2, gir1.2-gstreamer-1.0, gstreamer1.0-libav and gstreamer1.0-plugins-ugly.'),
|
||||||
_('Feature not available under Windows.')),
|
_('Feature not available under Windows.')),
|
||||||
_('UPnP-IGD'): (self.gupnp_igd_available,
|
_('UPnP-IGD'): (self.gupnp_igd_available,
|
||||||
_('Ability to request your router to forward port for file transfer.'),
|
_('Ability to request your router to forward port for file transfer.'),
|
||||||
|
|
|
@ -129,7 +129,7 @@ class Interface:
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
cls(obj.pri_txt, obj.sec_txt)
|
cls(obj.pri_txt, GLib.markup_escape_text(obj.sec_txt))
|
||||||
|
|
||||||
def handle_ask_new_nick(self, account, room_jid):
|
def handle_ask_new_nick(self, account, room_jid):
|
||||||
title = _('Unable to join group chat')
|
title = _('Unable to join group chat')
|
||||||
|
|
Loading…
Reference in New Issue