first commit

main
protsenkovi 10 months ago
commit 24c50947d2

@ -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 @@
# Пример работы webrtc из python

@ -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-----
MIIFvTCCA6WgAwIBAgIUH5r7oChsDwr2GnJ4pNhBA9Z6S28wDQYJKoZIhvcNAQEL
BQAwbjELMAkGA1UEBhMCUlUxFTATBgNVBAgMDFNhbWFyYVJlZ2lvbjEPMA0GA1UE
BwwGU2FtYXJhMQ8wDQYDVQQKDAZNeUhvbWUxEjAQBgNVBAsMCVNhbWFyYURlcDES
MBAGA1UEAwwJbXlob21lLnJ1MB4XDTIzMDIyNjE1MTAyNVoXDTI0MDIyNjE1MTAy
NVowbjELMAkGA1UEBhMCUlUxFTATBgNVBAgMDFNhbWFyYVJlZ2lvbjEPMA0GA1UE
BwwGU2FtYXJhMQ8wDQYDVQQKDAZNeUhvbWUxEjAQBgNVBAsMCVNhbWFyYURlcDES
MBAGA1UEAwwJbXlob21lLnJ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEAw0CCeRE48FhUIuifEZ8o/akLpmYHqJRCcV6XLflXQjQ7uQn3tTplrW2Mgy4j
W+hqvjr4yAJQVL7TefNO+MPjEBMea+PEHDiesNZzP3QtT5b0AtQvU7VawNGALb6r
4M9gMgg2RD73069b93PfMFZulsVM+Yt9Tpdl3cGPeWyLkWrKoQ2dc+FwGEjgvcli
BHJvSLblVy4+BnaiuJsfSwlFBP8RD2/gjdpf/GUN/rmwO/RmYRuF/RZTSlUYrmID
sKczBmvP27/yFxKICR/BsNSBru8n56zrAU5BW4kCxOcxqnBnYaPfd+tGJyEIYHeH
9N8qcRoLh1qL5zGEFwRB0D3Ja49Ws1+qetII+/I/CEZ0Rf4t097ZDMNP4qKQCm2A
kwt0w23ao8L4nNAX4LhuW/3gwIV6sbwNxcR0UI4q2fsIyzd128RWs53PNugtD61x
VAYfrwHzpytRxmn9UzFGecvKUxQt618ISGSFXmEFvF9mZpx7RHqD8LMyp1tVRNXI
lVJ9QEferCd6UeHFGgkj3S8IGnsGmXO6zxcmoK3+fTvY7S5lFj3CC+2UdOdAlxEd
jlMj/8rMf6eHE4EYVGD46MDglxihdcAixeyrIy8MfvxkuNLSTdB7qehXeSwKM6pQ
PbbublvYOFli46c1JzMGKROKL7PnWS49Fdna290av8Bdnx0CAwEAAaNTMFEwHQYD
VR0OBBYEFDTjvQ1M7pinWct8Nrrd95Yuf5CFMB8GA1UdIwQYMBaAFDTjvQ1M7pin
Wct8Nrrd95Yuf5CFMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJokgctOQmKdxVKPHT4ZqoOS1ISBuwRNF2WYpjSRAxfSojmmeFJtZp7BUHE+kbsk
+drnwAFLxo32BXbDBCUL79VUc6e8etH0JHtPxim4cBKR2TlulYF2NNVF10SMOK5A
9qvl73UfOK0AfXxXaGbZ82W3ZIjmjlnpPY1syLnAoa1iygP1RKAlY272ApJ8gmEv
QetoNaRN09IShX3GcHUNB7X59CuOn9cLe1DWEASu8rb8+yc4pLob8jg2rhkSZ2tu
gzk2O7TgrSVz+2Na0d/w7G3WupWjF3omxvc6J0aQDCAly5NnDwGql/91eedlQtI+
Fd8SObkU83xxlKgi8TjC/pFoMM3SL78C+/vOoVRLgKFriCwXfeKmkabeGWUkdDXI
YV4Y8oWQILEwAUS13A5RXbZfjmXyyEYKHeDCfsonbRCxGDA70Rs6rpQFcGEh+fDk
gKb0j2+BknH7PHfzvntO9R7kzJaNzmFe/WDRq92A6VheznG33LBK9TaKGAFFpqdR
ryTOi7X/z9tsYMGdKxyuLV1v+dJRz9GDeEi98XlhM3IoEX9M1druOl94iaL6iZnK
SC6u9ybIeMpKqBVxqSfOrQIySzOie75fukAssNtGwbLgABl6qBUk4tjRie99VGEJ
Ug+O8W+0hud0ZEJ/cbsOGTaR9FWLTDJuvykkwOqvgObZ
-----END CERTIFICATE-----

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
yOT1wNOhn28zKzKVpiJEtke7p8F2Dg==
-----END PRIVATE KEY-----

Binary file not shown.

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

@ -0,0 +1,11 @@
#!/bin/bash
CURDIRNAME=${PWD##*/}
docker run \
-it \
--rm \
-v $(pwd):/wd \
--net host \
--name ${USER}_${CURDIRNAME} \
${USER}_${CURDIRNAME} \
python main.py
Loading…
Cancel
Save