Django ASGI/channels with Streaminghttpresponse - python

I add channels to my project and it's creating a problem,
I have a stream func that streama video with streaminghttpresponse , and that work well before I add asgi/channels to run the server .
I saw that it's some bug that will be fixed in django 4.2 ,but I look for solution for now/ or if someone suggests another library for a socket that will not cause a similar problem
this is my code
Django v - 4.0
channels - 3.0.5
views.py
def stream(source,startVideo):
if startvideo :
url = source + str('.mp4')
cap = cv2.VideoCapture(url)
while (cap.isOpened()):
ret, frame = cap.read()
if not ret:
check = False
break
results = model(frame,augment=False,size=320)
det = results.pred[0]
results.render()
annotator = Annotator(frame,line_width=2, pil=not ascii)
im0 = annotator.result()
image_bytes = cv2.imencode('.jpg', im0)[1].tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + image_bytes + b'\r\n')
cap.release()
cv2.destroyAllWindows()
#csrf_exempt
def video_feed(request):
if request.method == 'POST':
global startvideo,start,content
startvideo=True
content = request.GET.get('url','not')
return StreamingHttpResponse(stream(content,startvideo), content_type='multipart/x-mixed-replace; boundary=frame')
ASGI.PY
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket':AuthMiddlewareStack(URLRouter(
webcam.routing.websocket_urlpatterns
))
})
setting.py
INSTALLED_APPS = [
'channels',
......
'webcam',
......
]
ASGI_APPLICATION = 'stream.asgi.application'
routing.py
websocket_urlpatterns = [
re_path(r'ws/socket-server/',consumers.ChatConsumer.as_asgi()),
]
consumers.py
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_group_name = 'test'
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def receive(self,text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
await self.channel_layer.group_send(
self.room_group_name, {
'type':'chat_message',
'message':message
}
)
async def chat_message(self,event):
message = event['message']
await self.send(text_data=json.dumps({
'type':'chat',
'message':message
}))

Related

How to pass a fastapi response as input to another function

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)

manipulating websocket values

I'm trying to handling websockets with python for the first time.
I created two clients and I want to do some calculate with these two results.
(which is realtime crypto price)
Is it possible to add or multiply numbers of theses result?
import websockets
import asyncio
import json
from binance import AsyncClient, BinanceSocketManager
async def upbit_ws_client():
uri = "wss://api.upbit.com/websocket/v1"
async with websockets.connect(uri) as websocket:
subscribe_fmt = [
{"ticket": "test"},
{
"type": "ticker",
"codes": ["KRW-BTC"],
"isOnlyRealtime": True
},
{"format": "SIMPLE"}
]
subscribe_data = json.dumps(subscribe_fmt)
await websocket.send(subscribe_data)
while True:
data = await websocket.recv()
data = json.loads(data)
print(data['cd'], data['hp'])
async def binance_ws_client():
client = await AsyncClient.create()
bm = BinanceSocketManager(client)
ts = bm.symbol_book_ticker_socket("BTCUSDT")
async with ts as tscm:
while True:
res = await tscm.recv()
print(res['b'], res['a'])
if __name__ == '__main__':
my_loop = asyncio.get_event_loop()
my_loop.run_until_complete(asyncio.gather(*[upbit_ws_client(), binance_ws_client()]))
my_loop.close()
The results are strings so you need to convert them to floats first in order to have a data type which you can calculate:
a = float(res['a'])
b = float(res['b'])
Then you can substract a and b:
result = a - b
print(result)
which you can add to your code like below:
import websockets
import asyncio
import json
from binance import AsyncClient, BinanceSocketManager
async def upbit_ws_client():
uri = "wss://api.upbit.com/websocket/v1"
async with websockets.connect(uri) as websocket:
subscribe_fmt = [
{"ticket": "test"},
{
"type": "ticker",
"codes": ["KRW-BTC"],
"isOnlyRealtime": True
},
{"format": "SIMPLE"}
]
subscribe_data = json.dumps(subscribe_fmt)
await websocket.send(subscribe_data)
while True:
data = await websocket.recv()
data = json.loads(data)
print(data['cd'], data['hp'])
async def binance_ws_client():
client = await AsyncClient.create()
bm = BinanceSocketManager(client)
ts = bm.symbol_book_ticker_socket("BTCUSDT")
async with ts as tscm:
while True:
res = await tscm.recv()
print(res['b'], res['a'])
a = float(res['a'])
b = float(res['b'])
result = a - b
print(result)
if __name__ == '__main__':
my_loop = asyncio.get_event_loop()
my_loop.run_until_complete(asyncio.gather(*[upbit_ws_client(), binance_ws_client()]))
my_loop.close()

how to implement a websocket aware reverse-proxy with aiohttp (python 3.6)

I am trying to implement an application specific reverse-proxy for jupyter notebooks using aiohttp. It works fine for http requests, but the websocket forwarding does not work. Requests from the browser arrive and get forwarded, but there are no responses from jupyter forthcoming. I assume my websocket client code somehow does not react to incoming messages from jupyter.
The only indication on the jupyter side that something is amiss are messages like this:
WebSocket ping timeout after 90009 ms.
so here is my attempt at writing the proxy
from aiohttp import web
from aiohttp import client
import aiohttp
import logging
import pprint
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
baseUrl = 'http://0.0.0.0:8888'
mountPoint = '/fakeUuid'
async def handler(req):
proxyPath = req.match_info.get('proxyPath','no proxyPath placeholder defined')
reqH = req.headers.copy()
if reqH['connection'] == 'Upgrade' and reqH['upgrade'] == 'websocket' and req.method == 'GET':
ws_server = web.WebSocketResponse()
await ws_server.prepare(req)
logger.info('##### WS_SERVER %s' % pprint.pformat(ws_server))
client_session = aiohttp.ClientSession()
async with client_session.ws_connect(baseUrl+req.path_qs,
headers = { 'cookie': reqH['cookie'] },
) as ws_client:
logger.info('##### WS_CLIENT %s' % pprint.pformat(ws_client))
async for server_msg in ws_server:
logger.info('>>> msg from browser: %s',pprint.pformat(server_msg))
if server_msg.type == aiohttp.WSMsgType.TEXT:
await ws_client.send_str(server_msg.data)
else:
await ws_client.send_bytes(server_msg.data)
async for client_msg in ws_client:
logger.info('>>> msg from jupyter: %s',pprint.pformat(client_msg))
if client_msg.tp == aiohttp.WSMsgType.TEXT:
await ws_server.send_str(client_msg.data)
else:
await ws_server.send_bytes(client_msg.data)
return ws_server
else:
async with client.request(
req.method,baseUrl+mountPoint+proxyPath,
headers = reqH,
allow_redirects=False,
data = await req.read()
) as res:
headers = res.headers.copy()
body = await res.read()
return web.Response(
headers = headers,
status = res.status,
body = body
)
return ws_server
app = web.Application()
app.router.add_route('*',mountPoint + '{proxyPath:.*}', handler)
web.run_app(app,port=3984)
Lesson learned: the two async for are blocking in the flow of the current function. By running them with asyncio.wait I can get them to run at the same time. The resulting program looks like this:
from aiohttp import web
from aiohttp import client
import aiohttp
import asyncio
import logging
import pprint
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
baseUrl = 'http://0.0.0.0:8888'
mountPoint = '/fakeUuid'
async def handler(req):
proxyPath = req.match_info.get('proxyPath','no proxyPath placeholder defined')
reqH = req.headers.copy()
if reqH['connection'] == 'Upgrade' and reqH['upgrade'] == 'websocket' and req.method == 'GET':
ws_server = web.WebSocketResponse()
await ws_server.prepare(req)
logger.info('##### WS_SERVER %s' % pprint.pformat(ws_server))
client_session = aiohttp.ClientSession(cookies=req.cookies)
async with client_session.ws_connect(
baseUrl+req.path_qs,
},
) as ws_client:
logger.info('##### WS_CLIENT %s' % pprint.pformat(ws_client))
async def wsforward(ws_from,ws_to):
async for msg in ws_from:
logger.info('>>> msg: %s',pprint.pformat(msg))
mt = msg.type
md = msg.data
if mt == aiohttp.WSMsgType.TEXT:
await ws_to.send_str(md)
elif mt == aiohttp.WSMsgType.BINARY:
await ws_to.send_bytes(md)
elif mt == aiohttp.WSMsgType.PING:
await ws_to.ping()
elif mt == aiohttp.WSMsgType.PONG:
await ws_to.pong()
elif ws_to.closed:
await ws_to.close(code=ws_to.close_code,message=msg.extra)
else:
raise ValueError('unexpecte message type: %s',pprint.pformat(msg))
finished,unfinished = await asyncio.wait([wsforward(ws_server,ws_client),wsforward(ws_client,ws_server)],return_when=asyncio.FIRST_COMPLETED)
return ws_server
else:
async with client.request(
req.method,baseUrl+mountPoint+proxyPath,
headers = reqH,
allow_redirects=False,
data = await req.read()
) as res:
headers = res.headers.copy()
body = await res.read()
return web.Response(
headers = headers,
status = res.status,
body = body
)
return ws_server
app = web.Application()
app.router.add_route('*',mountPoint + '{proxyPath:.*}', handler)
web.run_app(app,port=3984)

Send JSON with image as bytes using WebSocket

I want to send and receive image from cv2.Videocapture using WebSocket.
It could get json, but it couldn't decoded.
We need result that can be opened using cv2.imshow().
Somebody help me...
This is Client
ret, image_np = cap.read()
IMAGE_SHAPE = image_np.shape
encoded_image = base64.b64encode(image_np)
print(type(encoded_image))
payload = {
'from': 'rasp',
'image': str(encoded_image),
'shape': IMAGE_SHAPE,
}
data = json.dumps(payload)
try:
# Send encoded image data.
await websocket.send(data)
# receive server message
received_data = await websocket.recv()
print('< {}'.format(received_data))
# image = base64.b64decode(received_data)
# np_image = np.fromstring(image, dtype=np.uint8)
# source = np_image.reshape(IMAGE_SHAPE)
return websocket
except Exception:
print('WebSocket send or receive error.')
exit(1)
This is Server
async def server_on(websocket, path):
payload = {
'from': 'image-server',
# 'result': {
# # Default is null.
# 'isPerson': <bool>,
# 'centerPoint': <(x, y)>,
# },
}
data = json.dumps(payload)
try:
payload = await websocket.recv()
receive_data = json.loads(payload)
# At this line doesnt work...
decoded_image = base64.b64decode(receive_data['image'])
image_np = np.fromstring(decoded_image, dtype=np.uint8)
source = image_np.reshape(receive_data['shape'])
await websocket.send(data)
except Exception:
websocket.close()
return
In your Client I would say that you have an extra operation not needed.
Based on your latest comment, you might not need to use: str(encoded_image).
You could try to use: base64.encodestring(image_np) that will send you back a string container.
ret, image_np = cap.read()
IMAGE_SHAPE = image_np.shape
encoded_image = base64.encodestring(image_np)
print(type(encoded_image))
payload = {
'from': 'rasp',
'image': encoded_image.decode('utf-8'),
'shape': IMAGE_SHAPE,
}

Video transferred over GRPC plays slow

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()

Categories

Resources