[vlc-commits] Fixes a bug in the client/server implementation

Saveliy Yusufov git at videolan.org
Wed Apr 29 13:12:13 CEST 2020


vlc/python | branch: master | Saveliy Yusufov <saveliy.m.yusufov at gmail.com> | Fri Feb 28 12:18:25 2020 -0500| [b45d75bc51d7615c0faf173d900d16bc149a01b5] | committer: Saveliy Yusufov

Fixes a bug in the client/server implementation

The recv() system call returns any data that is available, up to the
requested amount, rather than waiting for receipt of the full amount of
data requested. The previous implementation attempted to split messages
into sections using a ',', but this is a poor implementation due to the
assumptions that were made about receipt of data.

The fix adds two methods to the Client class, namely recv_all() and
recv_msg(). We use a struct in order to send the length of the data as a
message header (in network byte order). That struct is unpacked on the
client's end. recv_msg() first recvs and unpacks the 4 byte msg header
that tells us how many bytes we are supposed to recv. Finally,
recv_msg() calls recv_all() with that number of bytes and keeps recving
until the buffer is filled with `size` number of bytes. The
data_receiver() method was simplified by calling recv_mesg() and then
decoding, parsing, and queueing the messages.

The diagram in the README was rereated to paint a clearer picture of the
architecture.

Added the GPL3 header to the pyqt5 example.

> http://git.videolan.org/gitweb.cgi/vlc/python.git/?a=commit;h=b45d75bc51d7615c0faf173d900d16bc149a01b5
---

 examples/pyqt5vlc.py           |  18 +++++++++++++++
 examples/video_sync/README.md  |   4 ++--
 examples/video_sync/figure.png | Bin 118904 -> 28647 bytes
 examples/video_sync/network.py |  49 ++++++++++++++++++++++++++++++-----------
 4 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/examples/pyqt5vlc.py b/examples/pyqt5vlc.py
index abf0cf2..cb3d294 100644
--- a/examples/pyqt5vlc.py
+++ b/examples/pyqt5vlc.py
@@ -1,3 +1,21 @@
+#
+# PyQt5 example for VLC Python bindings
+# Copyright (C) 2009-2010 the VideoLAN team
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+#
 """
 A simple example for VLC python bindings using PyQt5.
 
diff --git a/examples/video_sync/README.md b/examples/video_sync/README.md
index c3792d8..120b658 100644
--- a/examples/video_sync/README.md
+++ b/examples/video_sync/README.md
@@ -1,6 +1,6 @@
 ## Video Synchronization with python-vlc
 
-<img src="figure.png" width="500" align="right">
+<img src="figure.png" width="600" align="center">
 
 Each video player is launched as a separate process. `main.py` is the entry point into the program. It creates the "main" video player with all of its respective controls. The main video player is started alongside a multithreaded server that "broadcasts" signals (e.g. play, pause, stop, and etc.) and the current time of the video player, to all client/slave videos. Specifically, this is accomplished by putting the current time or the signal of the main video player into a `queue` (FIFO). The multithreaded server gets the value from the queue and sends the value to each connected client.
 
@@ -8,4 +8,4 @@ In a somewhat similar fashion, each "slave" video player is launched as a separa
 
 In order to facilitate interprocess communication, UNIX domain sockets are used to send and receive data. For the time being, Windows users have to use TCP/IP sockets.
 
-Note: for the sake of clarity, the figure on the right only shows the case of 3 client/slave videos.
+Note: for the sake of clarity, the figure only shows the case of 3 client/slave videos.
diff --git a/examples/video_sync/figure.png b/examples/video_sync/figure.png
index f0feb1d..63268cb 100644
Binary files a/examples/video_sync/figure.png and b/examples/video_sync/figure.png differ
diff --git a/examples/video_sync/network.py b/examples/video_sync/network.py
index d657764..9835cf7 100644
--- a/examples/video_sync/network.py
+++ b/examples/video_sync/network.py
@@ -27,8 +27,9 @@ Date: 25 January 2019
 import os
 import platform
 import socket
-import threading
 import sys
+import struct
+import threading
 import logging
 from concurrent import futures
 
@@ -104,11 +105,13 @@ class Server:
 
     def data_sender(self):
         while True:
-            data = "{},".format(self.data_queue.get())
+            data = self.data_queue.get()
+            data = str(data).encode()
+            msg = struct.pack(">I", len(data)) + data
 
             with futures.ThreadPoolExecutor(max_workers=5) as ex:
                 for client in self.clients:
-                    ex.submit(self.sendall, client, data.encode())
+                    ex.submit(self.sendall, client, msg)
 
     def sendall(self, client, data):
         """Wraps socket module's `sendall` function"""
@@ -153,22 +156,42 @@ class Client:
         thread.daemon = True
         thread.start()
 
+    def recv_all(self, size):
+        """Helper function to recv `size` number of bytes, or return False"""
+        data = bytearray()
+
+        while (len(data) < size):
+            packet = self.sock.recv(size - len(data))
+            if not packet:
+                return False
+
+            data.extend(packet)
+
+        return data
+
+    def recv_msg(self):
+        """Receive the message size, n, and receive n bytes into a buffer"""
+        raw_msg_size = self.recv_all(4)
+        if not raw_msg_size:
+            return False
+
+        msg_size = struct.unpack(">I", raw_msg_size)[0]
+        return self.recv_all(msg_size)
+
     def data_receiver(self):
         """Handles receiving, parsing, and queueing data"""
         logger.info("New data receiver thread started.")
 
         try:
             while True:
-                data = self.sock.recv(4096)
-                if data:
-                    data = data.decode()
-
-                    for char in data.split(','):
-                        if char:
-                            if char == 'd':
-                                self.data_queue.queue.clear()
-                            else:
-                                self.data_queue.put(char)
+                raw_data = self.recv_msg()
+                if raw_data:
+                    data = raw_data.decode()
+                    if 'd' in set(data):
+                        self.data_queue.queue.clear()
+                        continue
+                    else:
+                        self.data_queue.put(data)
         except:
             logger.exception("Closing socket: %s", self.sock)
             self.sock.close()



More information about the vlc-commits mailing list