first commit
commit
24c50947d2
@ -0,0 +1 @@
|
||||
*
|
@ -0,0 +1,26 @@
|
||||
FROM condaforge/mambaforge
|
||||
|
||||
ENV TZ=Europe/Samara
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ARG USER
|
||||
ARG GROUP
|
||||
ARG UID
|
||||
ARG GID
|
||||
|
||||
RUN groupadd --gid ${GID} ${GROUP}
|
||||
RUN useradd --shell /bin/bash --uid ${UID} --gid ${GID} -G sudo --create-home ${USER}
|
||||
RUN mkdir /wd
|
||||
RUN chown ${USER}:${GROUP} /wd
|
||||
|
||||
# SYSTEM CONFIGURATION
|
||||
#RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
|
||||
|
||||
USER ${USER}
|
||||
|
||||
# USER CONFIGURATION
|
||||
RUN pip install opencv-python-headless aiohttp aiortc
|
||||
|
||||
ENV SHELL=/bin/bash
|
||||
SHELL ["/bin/bash", "--login", "-i", "-c"]
|
||||
WORKDIR /wd
|
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
CURDIRNAME=${PWD##*/}
|
||||
|
||||
docker build . -t ${USER}_${CURDIRNAME} \
|
||||
--build-arg USER=${USER} \
|
||||
--build-arg GROUP=${USER} \
|
||||
--build-arg UID=$(id -u ${USER}) \
|
||||
--build-arg GID=$(id -g ${USER})
|
@ -0,0 +1,33 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
yOT1wNOhn28zKzKVpiJEtke7p8F2Dg==
|
||||
-----END PRIVATE KEY-----
|
@ -0,0 +1,108 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>WebRTC demo</title>
|
||||
<style>
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
video {
|
||||
width: 100%;
|
||||
height: 1024px;
|
||||
}
|
||||
|
||||
.option {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
.camera_block {
|
||||
margin: 10px
|
||||
}
|
||||
table {
|
||||
font-family: arial, sans-serif;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td, th {
|
||||
border: 1px solid #dddddd;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #dddddd;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
// peer connection
|
||||
var pc_rgb = null;
|
||||
|
||||
// data channel
|
||||
var dc = null, dcInterval = null;
|
||||
|
||||
function start_rgb() {
|
||||
var config = {
|
||||
sdpSemantics: 'unified-plan'
|
||||
};
|
||||
|
||||
pc_rgb = new RTCPeerConnection(config);
|
||||
|
||||
pc_rgb.addEventListener('track', function(evt) {
|
||||
if (evt.track.kind == 'video') {
|
||||
document.getElementById('video_rgb').srcObject = evt.streams[0];
|
||||
}
|
||||
});
|
||||
|
||||
fetch('/offer_video', {
|
||||
body: JSON.stringify({
|
||||
url: "demo.mp4"
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
method: 'POST'
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(server_offer) {
|
||||
pc_rgb.setRemoteDescription(server_offer);
|
||||
pc_rgb.createAnswer().then(function(answer) {
|
||||
pc_rgb.setLocalDescription(answer).then(function() {
|
||||
fetch('/answer_video', {
|
||||
body: JSON.stringify({
|
||||
sdp: pc_rgb.localDescription.sdp,
|
||||
type: pc_rgb.localDescription.type
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
method: 'POST'
|
||||
});
|
||||
})
|
||||
});
|
||||
}).catch(function(e) {
|
||||
alert(e);
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = (event) => {
|
||||
console.log("page is fully loaded");
|
||||
start_rgb();
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="cameras" style="display: flex; flex-direction: row;">
|
||||
<div id="camera_rgb" class="camera_block">
|
||||
<video id="video_rgb" autoplay="true" playsinline="true" class="camera_block" ></video>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
@ -0,0 +1,105 @@
|
||||
import time
|
||||
from av import VideoFrame
|
||||
from aiortc import VideoStreamTrack
|
||||
import numpy as np
|
||||
from aiohttp import web
|
||||
import argparse
|
||||
import asyncio
|
||||
import ssl
|
||||
import os
|
||||
from aiortc import RTCPeerConnection, RTCSessionDescription
|
||||
import cv2
|
||||
import json
|
||||
import time
|
||||
|
||||
|
||||
ROOT = "./"
|
||||
|
||||
class WebRTCVideoSink(VideoStreamTrack):
|
||||
def __init__(self, vr):
|
||||
""" vr - opencv is videocapture. """
|
||||
super().__init__() # don't forget this!
|
||||
self.counter = 0
|
||||
self.vr = vr
|
||||
|
||||
async def recv(self):
|
||||
# time.sleep(1/40)
|
||||
ok, frame = self.vr.read()
|
||||
pts, time_base = await self.next_timestamp()
|
||||
if not ok:
|
||||
return None
|
||||
else:
|
||||
frame = VideoFrame.from_ndarray(frame, format="bgr24")
|
||||
frame.pts = pts
|
||||
frame.time_base = time_base
|
||||
self.counter += 1
|
||||
return frame
|
||||
|
||||
pcs_rgb = dict()
|
||||
|
||||
async def offer_video(request):
|
||||
params = await request.json()
|
||||
pc = RTCPeerConnection()
|
||||
pcs_rgb[request.remote] = pc
|
||||
reader = cv2.VideoCapture("demo.mp4")
|
||||
video_track = WebRTCVideoSink(reader)
|
||||
pc.addTrack(video_track)
|
||||
offer = await pc.createOffer()
|
||||
print("Offer:", offer)
|
||||
await pc.setLocalDescription(offer)
|
||||
return web.Response(
|
||||
content_type="application/json",
|
||||
text=json.dumps(
|
||||
{"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
|
||||
),
|
||||
)
|
||||
|
||||
async def answer_video(request):
|
||||
params = await request.json()
|
||||
answer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
|
||||
print("Answer:", answer)
|
||||
pc = pcs_rgb.get(request.remote, None)
|
||||
if not pc is None:
|
||||
await pc.setRemoteDescription(answer)
|
||||
return web.Response(text="Ok")
|
||||
|
||||
async def index(request):
|
||||
content = open(os.path.join(ROOT, "index.html"), "r").read()
|
||||
return web.Response(content_type="text/html", text=content)
|
||||
|
||||
async def on_shutdown(app):
|
||||
# close peer connections
|
||||
coros = [pc.close() for remote, pc in pcs_rgb.items()]
|
||||
await asyncio.gather(*coros)
|
||||
pcs_rgb.clear()
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="WebRTC audio / video / data-channels demo"
|
||||
)
|
||||
parser.add_argument("--cert-file", help="SSL certificate file (for HTTPS)", default="./certs/cert.pem")
|
||||
parser.add_argument("--key-file", help="SSL key file (for HTTPS)", default="./certs/key.pem")
|
||||
parser.add_argument(
|
||||
"--host", default="0.0.0.0", help="Host for HTTP server (default: 0.0.0.0)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port", type=int, default=24000, help="Port for HTTP server (default: 8081)"
|
||||
)
|
||||
parser.add_argument("--verbose", "-v", action="count")
|
||||
args = parser.parse_args()
|
||||
|
||||
# run event loop
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
app = web.Application()
|
||||
app.on_shutdown.append(on_shutdown)
|
||||
app.router.add_get("/", index)
|
||||
app.router.add_post("/offer_video", offer_video)
|
||||
app.router.add_post("/answer_video", answer_video)
|
||||
|
||||
ssl_context = ssl.SSLContext()
|
||||
ssl_context.load_cert_chain(args.cert_file, args.key_file)
|
||||
|
||||
web.run_app(
|
||||
app, access_log=None, host=args.host, port=args.port, ssl_context=ssl_context
|
||||
)
|
Loading…
Reference in New Issue