Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* prevent the user from selecting a virtual device as the webcam capture device client side
* in auto mode, choose a non-virtual device if there is one
* emit signals without holding the webcam lock (via idle_add) - as this could potentially cause deadlocks?
* keep track of acks, stop the webcam if the server fails to send them back
* webcam_changed signal handler should not cause a stop or start, only update the GUI!
* webcam util now returns dicts, with the device number and device string

git-svn-id: https://xpra.org/svn/Xpra/trunk@11863 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Feb 6, 2016
1 parent a4adf60 commit aac7847
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 31 deletions.
10 changes: 7 additions & 3 deletions src/xpra/client/gtk_base/gtk_tray_menu_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -827,10 +827,10 @@ def client_signalled_change(obj):
return menu

def make_webcammenuitem(self):
def webcam_toggled(webcam, *args):
def webcam_toggled(*args):
active = self.client.webcam_device is not None
v = webcam.get_active()
webcamlog("webcam_toggled(%s, %s) active=%s, menu=%s", webcam, args, active, v)
webcamlog("webcam_toggled%s active=%s, menu=%s", args, active, v)
changed = active != v
if not changed:
return
Expand All @@ -843,7 +843,11 @@ def webcam_toggled(webcam, *args):
webcam.set_active(active)
webcam = self.checkitem("Webcam", webcam_toggled)
def webcam_changed(*args):
webcam_toggled(webcam)
active = self.client.webcam_device is not None
v = webcam.get_active()
webcamlog("webcam_changed%s active=%s, menu=%s", args, active, v)
if webcam.get_active()!=active:
webcam.set_active(active)
self.client.connect("webcam-changed", webcam_changed)
#webcam = self.menuitem("Webcam", "webcam.png", "Forward webcam", None)
set_sensitive(webcam, False)
Expand Down
60 changes: 41 additions & 19 deletions src/xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@

RPC_TIMEOUT = int(os.environ.get("XPRA_RPC_TIMEOUT", "5000"))

WEBCAM_ALLOW_VIRTUAL = os.environ.get("XPRA_WEBCAM_ALLOW_VIRTUAL", "0")=="1"


def r4cmp(v, rounding=1000.0): #ignore small differences in floats for scale values
return iround(v*rounding)
Expand Down Expand Up @@ -174,6 +176,7 @@ def __init__(self):
self.webcam_forwarding = False
self.webcam_device = None
self.webcam_device_no = -1
self.webcam_last_ack = -1
self.webcam_lock = RLock()
self.server_supports_webcam = False
self.server_virtual_video_devices = 0
Expand Down Expand Up @@ -1989,24 +1992,24 @@ def start_sending_webcam(self):
self.do_start_sending_webcam(self.webcam_option)

def do_start_sending_webcam(self, device_str):
webcamlog("do_start_sending_webcam(%s)", device_str)
assert self.server_supports_webcam
device = 0
if device_str=="auto":
if os.name=="posix":
try:
from xpra.platform.xposix.webcam_util import get_virtual_video_devices, get_all_video_devices
virt_devices = get_virtual_video_devices()
non_virtual = [x.replace("/dev/video", "") for x in get_all_video_devices() if x not in virt_devices]
webcamlog("found %s non-virtual video devices: %s", len(non_virtual), non_virtual)
for x in non_virtual:
try:
device = int(x)
break
except:
pass
except ImportError as e:
webcamlog("no webcam_util: %s", e)
virt_devices, all_video_devices, non_virtual = {}, {}, {}
if os.name=="posix":
try:
from xpra.platform.xposix.webcam_util import get_virtual_video_devices, get_all_video_devices
virt_devices = get_virtual_video_devices()
all_video_devices = get_all_video_devices()
non_virtual = dict([(k,v) for k,v in all_video_devices.items() if k not in virt_devices])
webcamlog("virtual video devices=%s", virt_devices)
webcamlog("all_video_devices=%s", all_video_devices)
webcamlog("found %s known non-virtual video devices: %s", len(non_virtual), non_virtual)
except ImportError as e:
webcamlog("no webcam_util: %s", e)
webcamlog("do_start_sending_webcam(%s)", device_str)
if device_str in ("auto", "on", "yes"):
if len(non_virtual)>0:
device = non_virtual.keys()[0]
else:
webcamlog("device_str: %s", device_str)
try:
Expand All @@ -2019,6 +2022,14 @@ def do_start_sending_webcam(self, device_str):
device = int(device_str[p+len("video"):])
except:
device = 0
if device in virt_devices:
webcamlog.warn("Warning: video device %s is a virtual device", virt_devices.get(device, device))
if WEBCAM_ALLOW_VIRTUAL:
webcamlog.warn(" environment override - this may hang..")
else:
webcamlog.warn(" corwardly refusing to use it")
webcamlog.warn(" set WEBCAM_ALLOW_VIRTUAL=1 to force enable it")
return
import cv2
webcamlog("do_start_sending_webcam(%s) device=%i", device_str, device)
self.webcam_frame_no = 0
Expand All @@ -2036,12 +2047,20 @@ def do_start_sending_webcam(self, device_str):
self.webcam_device_no = device
self.webcam_device = webcam_device
self.send("webcam-start", device, w, h)
self.emit("webcam-changed")
self.idle_add(self.emit, "webcam-changed")
webcamlog("webcam started")
def check_acks():
webcamlog("check_acks: webcam_last_ack=%s", self.webcam_last_ack)
if self.webcam_last_ack<=0:
webcamlog.warn("Warning: no acknowledgements received from the server, stopping webcam")
self.stop_sending_webcam()
self.timeout_add(10*1000, check_acks)
#FIXME: add timer to stop if we don't get an ack
except Exception as e:
webcamlog.warn("webcam test capture failed: %s", e)

def stop_sending_webcam(self):
webcamlog("stop_sending_webcam()")
with self.webcam_lock:
self.do_stop_sending_webcam()

Expand All @@ -2055,11 +2074,12 @@ def do_stop_sending_webcam(self):
self.webcam_device = None
self.webcam_device_no = -1
self.webcam_frame_no = 0
self.webcam_last_ack = -1
try:
wd.release()
except Exception as e:
webcamlog.error("Error closing webcam device %s: %s", wd, e)
self.emit("webcam-changed")
self.idle_add(self.emit, "webcam-changed")

def _process_webcam_stop(self, packet):
device_no = packet[1]
Expand All @@ -2071,17 +2091,19 @@ def _process_webcam_ack(self, packet):
webcamlog("process_webcam_ack: %s", packet)
with self.webcam_lock:
if self.webcam_device:
frame_no = packet[2]
self.webcam_last_ack = frame_no
self.send_webcam_frame()

def send_webcam_frame(self):
webcamlog("send_webcam_frame() webcam_device=%s", self.webcam_device)
assert self.webcam_device_no>=0 and self.webcam_device
try:
import cv2
ret, frame = self.webcam_device.read()
assert ret and frame.ndim==3
w, h, Bpp = frame.shape
assert Bpp==3 and frame.size==w*h*Bpp
webcamlog("send_webcam_frame strides=%s", frame.strides)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
from PIL import Image
image = Image.fromarray(rgb)
Expand Down
14 changes: 6 additions & 8 deletions src/xpra/platform/xposix/webcam_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def get_virtual_video_devices():
log.warn(" make sure that the v4l2loopback kernel module is installed and loaded")
return []
contents = os.listdir(v4l2_virtual_dir)
devices = []
devices = {}
for f in contents:
if not f.startswith("video"):
continue
Expand All @@ -38,26 +38,24 @@ def get_virtual_video_devices():
except:
continue
dev_file = "/dev/%s" % f
devices.append(dev_file)
devices[no] = dev_file
log("devices: %s", devices)
log("found %i virtual video device%s", len(devices), engs(devices))
return devices

def get_all_video_devices():
contents = os.listdir("/dev")
devices = []
devices = {}
for f in contents:
if not f.startswith("video"):
continue
dev_file = "/dev/%s" % f
if not os.path.isfile(dev_file):
continue
try:
no = int(f[len("video"):])
assert no>=0
except:
continue
devices.append(f)
devices[no] = dev_file
return devices


Expand All @@ -71,8 +69,8 @@ def main():
with program_context("Webcam Info", "Webcam Info"):
devices = get_virtual_video_devices()
log.info("Found %i virtual video device%s:", len(devices), engs(devices))
for d in devices:
log.info("%s", d)
for no, d in devices.items():
log.info("%-2i: %s", no, d)

if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src/xpra/server/server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2511,7 +2511,7 @@ def start_virtual_webcam(self, ss, device, w, h):
return
if self.webcam_forwarding_device:
self.stop_virtual_webcam()
device_str = devices[0]
device_str = devices.values()[0]
try:
from xpra.codecs.v4l2.pusher import Pusher #@UnresolvedImport
p = Pusher()
Expand Down

0 comments on commit aac7847

Please sign in to comment.