python: release 3.0.6109
[vlc/bindings/python.git] / generated / 3.0 / examples / video_sync / network.py
1 #
2 # PyQt5-based video-sync example for VLC Python bindings
3 # Copyright (C) 2009-2010 the VideoLAN team
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
18 #
19 """
20 The client/server classes that keep multiple VLC python bindings players
21 synchronized.
22
23 Author: Saveliy Yusufov, Columbia University, sy2685@columbia.edu
24 Date: 25 January 2019
25 """
26
27 import os
28 import platform
29 import socket
30 import threading
31 import sys
32 import logging
33 from concurrent import futures
34
35 logger = logging.getLogger(__name__)
36 logger.setLevel(logging.DEBUG)
37
38 logger.setLevel(logging.DEBUG)
39
40 # create console handler and set level to debug
41 ch = logging.StreamHandler()
42 ch.setLevel(logging.DEBUG)
43
44 # create formatter
45 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
46
47 # add formatter to ch
48 ch.setFormatter(formatter)
49
50 # add ch to logger
51 logger.addHandler(ch)
52
53
54 class Server:
55     """Data sender server"""
56
57     def __init__(self, host, port, data_queue):
58
59         if platform.system() == "Windows":
60
61             # Create a TCP/IP socket
62             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
63
64             # Bind the socket to the port
65             logger.info("Server started on %s port %s", host, port)
66             self.sock.bind((host, port))
67
68         else:
69             server_address = "./uds_socket"
70
71             # Make sure the socket does not already exist
72             try:
73                 os.unlink(server_address)
74             except OSError:
75                 if os.path.exists(server_address):
76                     raise
77
78             # Create a UDS socket
79             self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
80
81             # Bind the socket to the address
82             logger.info("Server starting up on %s", server_address)
83             self.sock.bind(server_address)
84
85         # Listen for incoming connections
86         self.sock.listen(5)
87
88         self.clients = set()
89         self.data_queue = data_queue
90         listener_thread = threading.Thread(target=self.listen_for_clients, args=())
91         listener_thread.daemon = True
92         listener_thread.start()
93
94     def listen_for_clients(self):
95         logger.info("Now listening for clients")
96         t = threading.Thread(target=self.data_sender, args=())
97         t.daemon = True
98         t.start()
99
100         while True:
101             client, _ = self.sock.accept()
102             logger.info("Accepted Connection from: %s", client)
103             self.clients.add(client)
104
105     def data_sender(self):
106         while True:
107             data = "{},".format(self.data_queue.get())
108
109             with futures.ThreadPoolExecutor(max_workers=5) as ex:
110                 for client in self.clients:
111                     ex.submit(self.sendall, client, data.encode())
112
113     def sendall(self, client, data):
114         """Wraps socket module's `sendall` function"""
115         try:
116             client.sendall(data)
117         except socket.error:
118             logger.exception("Connection to client: %s was broken!", client)
119             client.close()
120             self.clients.remove(client)
121
122
123 class Client:
124     """Data receiver client"""
125
126     def __init__(self, address, port, data_queue):
127         self.data_queue = data_queue
128
129         if platform.system() == "Windows":
130
131             # Create a TCP/IP socket
132             self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
133
134             # Connect the socket to the port where the server is listening
135             logger.info("Connecting to %s port %s", address, port)
136             self.sock.connect((address, port))
137         else:
138
139             # Create a UDS socket
140             self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
141
142             # Connect the socket to the port where the server is listening
143             server_address = "./uds_socket"
144             logger.info("New client connecting to %s", server_address)
145
146             try:
147                 self.sock.connect(server_address)
148             except socket.error:
149                 logger.exception()
150                 sys.exit(1)
151
152         thread = threading.Thread(target=self.data_receiver, args=())
153         thread.daemon = True
154         thread.start()
155
156     def data_receiver(self):
157         """Handles receiving, parsing, and queueing data"""
158         logger.info("New data receiver thread started.")
159
160         try:
161             while True:
162                 data = self.sock.recv(4096)
163                 if data:
164                     data = data.decode()
165
166                     for char in data.split(','):
167                         if char:
168                             if char == 'd':
169                                 self.data_queue.queue.clear()
170                             else:
171                                 self.data_queue.put(char)
172         except:
173             logger.exception("Closing socket: %s", self.sock)
174             self.sock.close()
175             return