I have a post request that simply takes camera addresses from clients, Now I want to pass the response from this post request to another function that does the streaming via a web socket.
how can I pass the post-request function as input to get_stream ?
Post request function to take camera address
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from pydantic import BaseModel
import cv2
import uvicorn
app = FastAPI()
class Address(BaseModel):
camera_id: Union[str, int] = 0
#app.post("/camera_id")
async def address(address: Address):
print(type(address.camera_id))
print('----------------------')
webcam = address.camera_id.isnumeric() or address.camera_id.endswith('.txt') or address.camera_id.lower().startswith(
('rtsp://', 'rtmp://', 'http://', 'https://'))
if webcam:
return address
else:
return {
'message': "Incorrect Camera Address",
'status': address.camera_id,
"expect_input": "The camera address should be (0, 1) or startwith ('rtsp://', 'rtmp://', 'http://', 'https://')"
}
Function that process frame via websocket
#app.websocket("/ws")
async def get_stream(websocket: WebSocket):
await websocket.accept()
camera_id = await address(parameter)
camera = cv2.VideoCapture(camera_id)
try:
while True:
frame = camera.frame()
if frame is not None:
ret, buffer = cv2.imencode('.jpg', frame)
await websocket.send_bytes(buffer.tobytes())
del frame, result
gc.collect()
torch.cuda.empty_cache()
else:
print('No frame is rendered')
break
except WebSocketDisconnect:
print("Client disconnected")
This is how I am calling my function in the get_stream function camera_id = await address(parameter)
Related
I need to pass reader and writer from serial_asyncio between 2 asynchronous tasks, first task is reading user keyboard input, when user press x, function send information to Arduino, second task reading response from Arduino and for specyfic response do stuff
Code:
import warnings
import serial
import serial.tools.list_ports
import requests
import json
import asyncio
import keyboard
import time
from serial_asyncio import open_serial_connection
with open("config.json") as config:
cfg = json.load(config)
config.close()
headers = {'Accept': 'application/json'}
url = f"""https://{cfg["web"]}/login?username={cfg["user"]}&password={cfg["password"]}"""
get_token = requests.post(url, headers=headers)
token = get_token.json()
my_token = token["token"]
arduino_ports = [
p.device
for p in serial.tools.list_ports.comports()
if p.manufacturer and 'Arduino' in p.manufacturer
]
if not arduino_ports:
raise IOError("No Arduino found")
if len(arduino_ports) > 1:
warnings.warn("Multiple arduinos, select first")
async def arduino_listen(cfg, headers, reader):
print('arduino_listen task created')
while True:
await asyncio.sleep(0.1)
info = await reader.readline()
print('arduino respond:', info)
if "good" in str(info):
print('good from arduino')
if "bad" in str(info):
print('bad from arduino')
async def arduino_writer(cfg, headers, writer):
print('arduino_writer task created')
keypress = False
key = 'x'
while True:
if keypress and not keyboard.is_pressed(key):
print('x pressed on keyboard, send 5')
writer.write(b'5')
await asyncio.sleep(0.3)
keypress = False
elif keyboard.is_pressed(key) and not keypress:
keypress = True
async def serial():
print(f'serial task created, arduino port {arduino_ports[0]} selected')
reader, writer = await open_serial_connection(url=arduino_ports[0], baudrate=115200)
#reader, writer = await open_serial_connection(url=arduino_ports[0], baudrate=115200)
loop = asyncio.get_event_loop()
loop.create_task(serial())
loop.create_task(arduino_listen(cfg, headers, reader))
loop.create_task(arduino_writer(cfg, headers, writer))
loop.run_forever()
My question is, how to pass connection from serial function task to arduino_listen and arduino_writer tasks, I have try to add reader, writer = await open_serial_connection(url=arduino_ports[0], baudrate=115200) before loop.create_task but error unexpected ident appear
Avoid giving functions name that conflict with your module names, it's about serial in your case. Let's call it say start_serial.
As start_serial is the initial function that starts a serial connection arduino_listen and arduino_writer should be run in parallel right after it.
Change the final part of your code to the following:
async def start_serial():
print(f'serial task created, arduino port {arduino_ports[0]} selected')
reader, writer = await open_serial_connection(url=arduino_ports[0], baudrate=115200)
return reader, writer
async def main():
reader, writer = await start_serial()
await asyncio.gather(
arduino_listen(cfg, headers, reader),
arduino_writer(cfg, headers, writer))
asyncio.run(main())
I want to do the following thing:
I want to make a websocket, which prints me all events about esports and I want to use https://sofascore.com
I've inspected the network requests as usual and it seems that I need to send a Auth WebSocket Content first, then one for subscribing the right sport and then I will receive my events I need.
I've wrote the following code:
import websockets
import asyncio
from websockets.extensions import permessage_deflate
async def esports():
async with websockets.connect('wss://ws.sofascore.com:9222/', compression='deflate') as websocket:
msg = await websocket.recv()
print(f"From Server: {msg}")
t = await websocket.send(
'CONNECT {"no_responders":true,"protocol":1,"verbose":false,"pedantic":false,"user":"none","pass":"none","lang":"nats.ws","version":"1.8.1","headers":true}')
await websocket.send("PING")
pong = await websocket.recv()
print(f"From Server: {pong}")
await websocket.send(
'SUB sport.esports 6')
while (True):
msg = await websocket.recv()
print(f"From Server: {msg}")
asyncio.get_event_loop().run_until_complete(esports())
I know that the websocket is compressed as permessage_deflate, when I saw into the request headers of the websocket.
But I still get an error:
Traceback (most recent call last):
File "C:\Users\Coding\Desktop\websockett.py", line 23, in <module>
asyncio.get_event_loop().run_until_complete(esports())
File "C:\Users\Coding\AppData\Local\Programs\Python\Python39-32\lib\asyncio\base_events.py", line 642, in run_until_complete
return future.result()
File "C:\Users\Coding\Desktop\websockett.py", line 15, in esports
await websocket.send(
File "C:\Users\Coding\AppData\Roaming\Python\Python39\site-packages\websockets\legacy\protocol.py", line 620, in send
await self.ensure_open()
File "C:\Users\Coding\AppData\Roaming\Python\Python39\site-packages\websockets\legacy\protocol.py", line 921, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: received 1008 (policy violation) Authentication Timeout; then sent 1008 (policy violation) Authentication Timeout
Process finished with exit code 1
EDIT:
I have now found out that the whole thing works with the Nats network. Is there any way to use Nats with a Libary that also supports the websockets?
Haven't found one on github or pypi unfortunately....
Ideally you would be able to use the nats-py library:
import asyncio
import nats
async def handler(msg):
print(f"From server: {msg}")
async def main():
nc = await nats.connect("wss://ws.sofascore.com:9222")
await nc.subscribe("sport.esports", cb=handler)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
try:
loop.run_forever()
finally:
loop.close()
However, this library does not currently support connecting with WebSockets, so
the above doesn't work (yet - it looks like it's being worked on right
now).
For your code, the only reason it fails is that the messages you're sending
don't end with \r\n, which the NATS protocol requires. The code works as
expected with this change:
import asyncio
import websockets
async def esports():
async with websockets.connect('wss://ws.sofascore.com:9222') as websocket:
msg = await websocket.recv()
print(f"From Server: {msg}")
await websocket.send(
'CONNECT {"no_responders":true,"protocol":1,"verbose":false,"pedantic":false,"user":"none","pass":"none","lang":"nats.ws","version":"1.8.1","headers":true}\r\n'
)
await websocket.send("SUB sport.esports 1\r\n")
async for msg in websocket:
print(f"From Server: {msg}")
asyncio.run(esports())
Of course this will eventually get disconnected because it doesn't respond to
PING messages. Here's a little more fleshed out script which implements enough
of the NATS protocol to log the sport.esports messages:
import asyncio
import json
import textwrap
from dataclasses import dataclass
import websockets
class SofaError(Exception):
pass
def message_string(message, data=None, pretty=False):
s = message
if data is not None:
if pretty:
s += json.dumps(data, indent=2)
else:
s += json.dumps(data, separators=(",", ":"))
return s
def log(pre, message, data=None):
print(textwrap.indent(message_string(message, data, True), pre))
def recv_log(message, data=None):
log("< ", message, data)
async def send(websocket, message, data=None):
log("> ", message, data)
data = (message_string(message, data, False) + "\r\n").encode()
await websocket.send(data)
async def connect_and_subscribe(websocket):
connect_options = {
"no_responders": True,
"protocol": 1,
"verbose": False,
"pedantic": False,
"user": "none",
"pass": "none",
"lang": "nats.ws",
"version": "1.8.1",
"headers": True,
}
await send(websocket, "CONNECT ", connect_options)
await send(websocket, "SUB sport.esports 1")
#dataclass
class NatsMsg:
subject: str
sid: str
reply_to: str
size: int
payload: bytes
def parse_msg(info_line, pending_data):
if not info_line:
raise SofaError("No payload information received")
info = [b.decode(errors="replace") for b in info_line.split(b" ")]
if len(info) == 3:
subject, sid, size = info
reply_to = None
elif len(info) == 4:
subject, sid, reply_to, size = info
else:
raise SofaError("Unrecognized info format")
try:
size = int(size)
except ValueError:
raise SofaError("Bad payload size")
if len(pending_data) < size:
raise SofaError("Incomplete payload")
payload = pending_data[:size]
pending_data = pending_data[size:]
return NatsMsg(subject, sid, reply_to, size, payload), pending_data
async def handler(websocket, ws_message, connected):
while len(ws_message):
nats_message, _, ws_message = ws_message.partition(b"\r\n")
if not nats_message:
continue
op, _, rest = nats_message.partition(b" ")
if op == b"-ERR":
recv_log(nats_message.decode(errors="replace"))
err = rest.strip(b"'").decode(errors="replace") if rest else "(No message received)"
raise SofaError(f"Server error: {err}")
elif op == b"INFO":
info_options = json.loads(rest) if rest else None
recv_log("INFO ", info_options)
if not connected:
await connect_and_subscribe(websocket)
connected = True
elif op == b"PING":
recv_log("PING")
await send(websocket, "PONG")
elif op == b"MSG":
try:
msg, ws_message = parse_msg(rest, ws_message)
except SofaError as e:
recv_log(f"MSG (Error: {e}) {rest}")
continue
msg_info = (
f"MSG subject={msg.subject} sid={msg.sid} "
f"reply-to={msg.reply_to} nbytes={msg.size}:\n"
)
try:
decoded = msg.payload.decode()
data = json.loads(decoded)
except UnicodeError:
recv_log(f"{msg_info}{msg.payload}")
except json.JSONDecodeError:
recv_log(f"{msg_info}{decoded}")
else:
recv_log(msg_info, data)
else:
recv_log(f"(Unhandled op) {nats_message.decode(errors='replace')}")
return connected
async def main():
async with websockets.connect("wss://ws.sofascore.com:9222") as websocket:
connected = False
async for message in websocket:
connected = await handler(websocket, message, connected)
if __name__ == "__main__":
asyncio.run(main())
I have a Django project with Tornado websocket opened and is subscribed to a topic in my Redis Pub/Sub. I am using asyncio and aioredis. Before page refresh, browser close or navigation out of that page, I will call for the websocket to close, which will unsubscribe from that topic.
The issue here is sometimes when I change pages or refresh the page, a bunch of messages from the past will be pumped back into the newly opened websocket. It doesn't happen every time, and I'm not sure what else I can do to make sure the past messages don't come back on page refresh. I've already made sure the websocket will close and unsubscribe from the topic on page refresh/window unload.
Does Redis Pub/Sub keep old messages somewhere while the client is unsubscribed? And when the client subscribes back to the same topic, the old messages are sent out? Is this normal behaviour for Redis Pub/Sub? I'm under the impression that Redis Pub/Sub doesn't persist messages and if client is unsubscribed, the messages just get dropped for that client.
I need to make sure when page reloads, the old messages don't get pumped back to the websocket.
This is how I wrote the RedisChannel to execute the pub/sub functions:
import aioredis
class RedisChannel(object):
'''
Redis backed pub-sub websocket channel.
'''
async def subscribe(self, **kwargs):
'''
Subscribe to topics
'''
topics = kwargs.get('topics')
return await self.conn.subscribe(*topics)
async def unsubscribe(self, **kwargs):
'''
Unsubscribe to topics
'''
topics = kwargs.get('topics')
return await self.conn.unsubscribe(*topics)
async def send(self, **kwargs):
data = {}
# If client socket is provided, only send to this socket.
ws = kwargs.get('ws')
# Topic for this message. Compulsory for broadcast.
topic = kwargs.get('topic')
# Usually JSON
if kwargs.get('data'):
data['data'] = kwargs.get('data')
# I'm using 60 seconds right now just to try to limit the list of past messages
# But the behaviour I need is 0 past messages on page reload in browser
push_event = True
if kwargs.get('timestamp'):
event_timestamp = kwargs.get("timestamp", 0)
data['timestamp'] = event_timestamp
# logger.debug(data)
current_time = timezone.now()
if event_timestamp:
event_dt = get_utc_time(datetime.utcfromtimestamp(event_timestamp))
if event_dt:
time_apart = current_time - event_dt
duration = abs(time_apart.total_seconds())
logger.debug("Time apart between event and current time = {}".format(duration))
if duration >= 60:
push_event = False
if not push_event:
data = {}
return await self.conn.publish_json(topic, json.dumps(data, separators=(',', ': ')))
async def connect(self):
redis_settings = settings['redis']['channel']
self.conn = await aioredis.create_redis_pool(
(
redis_settings.get('host'),
redis_settings.get('port')
),
db=redis_settings.get('db'),
minsize=2,
maxsize=redis_settings.get('max_connections'),
encoding='utf-8'
)
This is how I wrote the websocket handler to subscribe/unsubscribe to a Redis topic:
import asyncio, json
ws_channel = RedisChannel()
asyncio.get_event_loop().create_task(ws_channel.connect())
async def reader(ch, ws):
while (await ch.wait_message()):
data = await ch.get_json()
if data:
ws.write_message(data)
await asyncio.sleep(0.001)
# time.sleep
class ResultsWsHandler(tornado.websocket.WebSocketHandler):
def open(self):
try:
self.write_message(json.dumps('Websocket opened.'))
except Exception as e:
logger.error(str(e))
def on_message(self, message):
asyncio.ensure_future(self.on_message_async(message))
async def on_message_async(self, message):
# async def on_message(self, message):
data = json.loads(message)
action = data.get('action', None)
topics = data.get('cameras', [])
if topics or action is not None:
try:
action = int(action)
if action == 0: # 0 - heartbeat
logger.debug('Heartbeat.')
param = {'type': 0}
self.write_message(json.dumps(param))
elif action == 1: # 1 - subscribe
channels = await ws_channel.subscribe(topics=topics)
logger.debug(f'Successfully subscribed from {topics}.')
self.write_message(json.dumps(f'Successfully subscribed to {topics}.'))
task_list = []
for c in channels:
task_list.append(asyncio.ensure_future(reader(c, self)))
await asyncio.wait(task_list)
elif action == 2: # 2 - unsubscribe
await ws_channel.unsubscribe(topics=topics)
logger.debug(f'Successfully unsubscribe from {topics}.')
self.write_message(json.dumps(f'Successfully unsubscribe from {topics}.'))
else:
logger.debug(f'Other: {data}')
except Exception as e:
logger.error(json.dumps(str(e), separators=(',', ': ')))
self.write_message(json.dumps(str(e), separators=(',', ': ')))
I've written the following code to transfer a video over GRPC. Im using client-side streaming. The client sends the video in frames over the stream. Each frame is converted to bytes datatype and is transferred.
But my video is playing slower than the actual speed. How do I ensure that the video plays at the actual speed?
imageTest.proto file
syntax = "proto3";
option java_multiple_files = true;
option objc_class_prefix = "HLW";
// The greeting service definition.
service ImageTest {
// Sends a greeting
rpc Analyse (stream MsgRequest) returns (MsgReply) {}
}
// The request message containing the image.
message MsgRequest {
bytes img = 1;
}
// The response message containing the reply
message MsgReply {
int32 reply = 1;
}
imageTest_server.py
class Greeter(imageTest_pb2_grpc.ImageTestServicer):
def Analyse(self, request_iterator, context):
for req in request_iterator:
frame = np.array(list(req.img))
frame = frame.reshape( (576,704) )
frame = np.array(frame, dtype = np.uint8 )
cv2.imshow('Processed Image', frame)
cv2.waitKey(1)
return imageTest_pb2.MsgReply(reply = cnt )
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
imageTest_pb2_grpc.add_ImageTestServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
imageTest_client.py
def run():
channel = grpc.insecure_channel('localhost:50051')
stub = imageTest_pb2_grpc.ImageTestStub(channel)
response = stub.Analyse( generateRequests() )
def generateRequests():
videogen = skvideo.io.vreader(URL)
for frame in videogen:
frame = cv2.cvtColor( frame, cv2.COLOR_RGB2GRAY )
frame = bytes(frame)
yield imageTest_pb2.MsgRequest(img= frame)
if __name__ == '__main__':
run()
When I trying to connect to its jupyter client in python code ,I encount a problem.
In the source code of jupyter, connection to zmq channel was established when websocket is opened
def open(self, kernel_id):
super(ZMQChannelsHandler, self).open()
try:
self.create_stream()
except web.HTTPError as e:
for channel, stream in self.channels.items():
if not stream.closed():
stream.close()
self.close()
else:
for channel, stream in self.channels.items():
//this is callback function when receive message from zqm channel
stream.on_recv_stream(self._on_zmq_reply)
while in the create_stream function, the zmq channel was established.
def create_stream(self):
km = self.kernel_manager
identity = self.session.bsession
for channel in ('shell', 'iopub', 'stdin'):
meth = getattr(km, 'connect_' + channel)
self.channels[channel] = stream = meth(self.kernel_id, identity=identity)
stream.channel = channel
... ignore no significance code
when the server receive message, on_message was invoke
def on_message(self, msg):
if not self.channels:
return
if isinstance(msg, bytes):
msg = deserialize_binary_message(msg)
else:
msg = json.loads(msg)
channel = msg.pop('channel', None)
if channel is None:
channel = 'shell'
if channel not in self.channels:
return
stream = self.channels[channel]
self.session.send(stream, msg)
At this time, zmq channel receive python code to be executed. After that, the execution result should be return, thus the function on_recv_stream above should be called and we got the result finally.
So I write the python code snippet like this:
from jupyter_client.multikernelmanager import MultiKernelManager
from jupyter_client.session import Session
from tornado import gen, web
from tornado.concurrent import Future
from tornado.ioloop import IOLoop
km = MultiKernelManager()
kernelid = km.start_kernel()
kernel =km.get_kernel(kernelid)
channel = km.connect_shell(kernelid)
print 'channel', channel
def on_reply(msg):
print 'we got return'
def on_timeout():
print("Timeout waiting for kernel_info_reply: %s", kernel_id)
kernel.session.send(channel, 'kernel_info_request')
channel.on_recv(on_reply)
Actually, I did not get the return message, that is to say,the on_reply function was not invoked. I did not what the problem is, Can anynone help me?
I solve the problem like this:
from jupyter_client.multikernelmanager import MultiKernelManager
km = MultiKernelManager()
kernelid = km.start_kernel('python2')
kn =km.get_kernel(kernelid)
kc = kn.client()
kc.start_channels()
msg_id = kc.execute('import math\nprint(math.sqrt(2))')
while True:
try:
msg = kc.get_iopub_msg()
print('\niopub msg is')
print(msg)
except Excption,e:
print(e)
break
if msg['parent_header'].get('msg_id') != msg_id:
continue
msg_type = msg['msg_type']
content = msg['content']
print('content is :')
print(content)
if msg_type == 'status':
if content['execution_state'] == 'idle':
break
else:
continue