Skip to content
This repository has been archived by the owner on Jun 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #23 from dkanukov/feature/streaming
Browse files Browse the repository at this point in the history
test streaming
  • Loading branch information
dkanukov authored May 29, 2024
2 parents e161901 + 4517502 commit cb88133
Show file tree
Hide file tree
Showing 18 changed files with 97 additions and 38 deletions.
6 changes: 6 additions & 0 deletions frontend/api/services/camera.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import CustomedApi from '../generated/customed-api'

export const getStream = async (cameraId: string) => {
const response = await CustomedApi.stream.streamDetail(cameraId)
console.log(response.data)
}
1 change: 1 addition & 0 deletions frontend/api/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * as authService from './auth'
export * as objectService from './objects'
export * as imageService from './image'
export * as geosearchService from './geosearch'
export * as streamService from './camera'
1 change: 1 addition & 0 deletions frontend/api/services/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const updateDevice = async (device: Device) => {
mac_address: device.macAddress,
name: device.name,
type: device.type,
camera_connection_url: device.connectionURL,
})
return true
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/admin/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default function Id({ params } : { params: { id: string } }) {
className={styles.drawerContent}
>
<Input
placeholder={'Названте слоя'}
placeholder={'Название слоя'}
value={newLayer?.floorName}
onChange={(e) => handleLayerNameChange(e.target.value)}
/>
Expand Down
4 changes: 4 additions & 0 deletions frontend/app/admin/devices/camera/camera.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.root {
padding: 20px;
}

.video {
max-width: calc(100vw - 40px);
}
11 changes: 11 additions & 0 deletions frontend/app/admin/devices/camera/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
'use client'
import { Typography } from 'antd'
import { useSearchParams } from 'next/navigation'

import styles from './camera.module.css'

const { Title } = Typography

export default function Camera() {
const searchParams = useSearchParams()

return (
<div className={styles.root}>
<Title>Просмотр камеры</Title>
<img
className={styles.video}
src={`http://localhost:8081/stream/${searchParams.get('cameraId')}`}
/>
{/*<video>*/}
{/* <source src={`http://localhost:8081/stream/${searchParams.get('cameraId')}`}/>*/}
{/*</video>*/}
</div>
)
}

Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client'
import Image from 'next/image'
import React from 'react'
import AboutBackGround from "@public/about-background.png"
import Backend from "@public/backend-image.png"

import AboutBackGround from '@public/about-background.png'
import Backend from '@public/backend-image.png'

export const BackendSummaryBody = () => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Input, AutoComplete } from 'antd'
import { useCallback, useState } from 'react'
import { fromLonLat } from 'ol/proj'
import { Coordinate } from 'ol/coordinate'
import { debounce } from 'lodash'
import debounce from 'lodash/debounce'

import styles from './create-objects-form.module.css'

Expand Down
15 changes: 14 additions & 1 deletion frontend/components/device-drawer/device-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ const DEVICE_TYPES = [
]

export const DeviceDrawer = (props: Props) => {
const isCamera = props.device.type === BackendInternalEntityDeviceType.Camera

const handleDeviceChange = (key: DeviceKeys, value: string) => {
if (!props.whenChange) {
return
}

props.whenChange(key, value)
}

return (
<Drawer
title={`Устройство ${props.device.name}`}
Expand All @@ -59,7 +62,7 @@ export const DeviceDrawer = (props: Props) => {
readOnly={!props.whenChange}
/>
</div>
<div className={styles.inputWithLabel}>
<div className={styles.inputWithLabel}>
<Text>Долгота</Text>
<InputNumber
className={styles.inputNumber}
Expand Down Expand Up @@ -93,6 +96,16 @@ export const DeviceDrawer = (props: Props) => {
readOnly={!props.whenChange}
/>
</div>
{isCamera && (
<div className={styles.inputWithLabel}>
<Text>URL</Text>
<Input
value={props.device.connectionURL}
onChange={({ target }) => handleDeviceChange('connectionURL', target.value)}
readOnly={!props.whenChange}
/>
</div>
)}
<div className={styles.inputWithLabel}>
<Text>Тип устройства</Text>
<Select
Expand Down
6 changes: 6 additions & 0 deletions frontend/components/devices-table/devices-table.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
.dropdown {
padding: 8px;
}

.nameCell {
display: flex;
gap: 6px;
align-items: center;
}
2 changes: 1 addition & 1 deletion frontend/components/devices-table/devices-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const DevicesTable = (props: Props) => {
dataIndex: 'name',
key: 'name',
render: (name, device) => (
<div>
<div className={styles.nameCell}>
{name}
{device.type === BackendInternalEntityDeviceType.Camera && (
<Link
Expand Down
1 change: 1 addition & 0 deletions frontend/hooks/devices/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { useDevicesList } from './use-devices-list'
export { useCamera } from './use-camera'
11 changes: 11 additions & 0 deletions frontend/hooks/devices/use-camera.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { streamService } from '@api'

export const useCamera = (cameraId: string) => {
const fetchStream = async () => {
await streamService.getStream(cameraId)
}

return {
fetchStream,
}
}
3 changes: 1 addition & 2 deletions frontend/hooks/objects/use-object-edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export const useObjectEdit = (objectId: string) => {
const deviceIndex = devices.findIndex((d) => d.id === device.id)
devices[deviceIndex] = newDevice

console.log(newDevice)
setLayer({
...layer,
devices: [...devices],
Expand All @@ -114,7 +113,7 @@ export const useObjectEdit = (objectId: string) => {

newDevice.coordinates = newCoords.coords as [number, number]
const deviceIndex = devices.findIndex((d) => d.id === device.id)
devices[deviceIndex] = newDevice
devices[deviceIndex] = { ...newDevice }

setLayer({
...layer,
Expand Down
2 changes: 2 additions & 0 deletions frontend/models/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export class Device {
ip?: string
macAddress?: string
coordinates!: [number, number]
connectionURL?: string
layerId!: string
type!: BackendInternalEntityDeviceType
angle!: number
Expand All @@ -17,6 +18,7 @@ export class Device {
this.isActive = Boolean(dto.is_active)
this.ip = dto.ip
this.macAddress = dto.mac_address
this.connectionURL = dto.camera_connection_url
this.layerId = dto.layer_id || 'no-layer-id-device'
this.type = dto.type || BackendInternalEntityDeviceType.Computer
this.coordinates = [dto.location_y || 0, dto.location_x || 0]
Expand Down
47 changes: 23 additions & 24 deletions streaming-service/app.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
import time

from flask import Flask, Response, request
from generate_frame import VideoCamera
import requests


app = Flask("stream")

camera_instance = None # Maintain a single instance of VideoCamera


def gen(camera : VideoCamera):
def gen(camera):
while True:
frame = camera.get_frame()
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
if frame is not None:
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
else:
del camera
break # Stops the stream if no frame is available


@app.route("/", methods=['GET'])
def stream():
args = request.args
return Response(gen(VideoCamera(args.get("url"))), mimetype='multipart/x-mixed-replace; boundary=frame')
camera = VideoCamera(args.get("url"))
return Response(gen(camera), mimetype='multipart/x-mixed-replace; boundary=frame')


@app.route("/isLowLight/", methods=["GET"])
def is_low_lighted():
args = request.args
frame = VideoCamera(args.get("url")).get_frame()
camera = VideoCamera(args.get("url"))
frame = camera.get_frame()
if frame is None:
return Response(status=404)
files = {
'file': frame
}
resp = requests.post("http://imaging-service:8088/", files=files)
print(resp.text)
return Response(resp.text, mimetype="application/json")
return Response("No frame available", status=404)
files = {'file': ('frame.jpg', frame, 'image/jpeg')}
del camera
try:
resp = requests.post("http://imaging-service:8088/", files=files)
return Response(resp.text, mimetype="application/json")
except requests.RequestException as e:
return Response(str(e), status=500)


if __name__=="__main__":
# http_server = WSGIServer(('', 8181), app)
# http_server.serve_forever()

app.run(host="0.0.0.0", port=8181)



# @app.route('/video_feed')
# def video_feed():
# return Response(gen(VideoCamera()), mimetype='multipart/x-mixed-replace; boundary=frame')
app.run(host="0.0.0.0", port=8181, threaded=True)
14 changes: 9 additions & 5 deletions streaming-service/generate_frame.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import cv2, time
import cv2


class VideoCamera(object):
def __init__(self, video_source : str):
def __init__(self, video_source: str):
self.video = cv2.VideoCapture(video_source)

def __del__(self):
self.video.release()

def get_frame(self):
_, image = self.video.read()
_, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
ok, image = self.video.read()
if not ok:
return None
ok, jpeg = cv2.imencode('.jpg', image)
if not ok:
return None
return jpeg.tobytes()
2 changes: 1 addition & 1 deletion token-service/internal/token/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func validate(inputToken string, isRefresh bool) (userId string, err error) {

})
if err != nil {
return userId, err
return userId, fmt.Errorf("jwt.Parse: %w", err)
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
Expand Down

0 comments on commit cb88133

Please sign in to comment.