Skip to content

Commit

Permalink
Bootstap WebRTC PC connection using aiortc
Browse files Browse the repository at this point in the history
  • Loading branch information
varunkumar committed Sep 21, 2019
1 parent 47cae78 commit b9320cb
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.pythonPath": "/Library/Frameworks/Python.framework/Versions/3.7/bin/python3"
}
14 changes: 14 additions & 0 deletions core/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Camlio Core

Core module responsible for processing webcam stream

## Start engine

```bash
python engine.py --port 8081 --cert-file secrets/server.crt --key-file secrets/server.key --video_device_index=4
```

### FAQs

1. How to find the webcam index?

```bash
ffmpeg -f avfoundation -list_devices true -i ""
```
101 changes: 101 additions & 0 deletions core/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import argparse
import asyncio
import json
import logging
import os
import platform
import ssl

import aiohttp_cors
from aiohttp import web
from aiortc import RTCPeerConnection, RTCSessionDescription
from aiortc.contrib.media import MediaPlayer

from video_stream import CamlioVideoStreamTrack

ROOT = os.path.dirname(__file__)


async def offer(request):
params = await request.json()
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])

pc = RTCPeerConnection()
pcs.add(pc)

@pc.on("iceconnectionstatechange")
async def on_iceconnectionstatechange():
print("ICE connection state is %s" % pc.iceConnectionState)
if pc.iceConnectionState == "failed":
await pc.close()
pcs.discard(pc)

# open media source
if args.play_from:
player = MediaPlayer(args.play_from)
else:
player = None

await pc.setRemoteDescription(offer)
for t in pc.getTransceivers():
if t.kind == "audio" and player and player.audio:
pc.addTrack(player.audio)
elif t.kind == "video":
pc.addTrack(CamlioVideoStreamTrack(args.video_device_index))

answer = await pc.createAnswer()
await pc.setLocalDescription(answer)

return web.Response(
content_type="application/json",
text=json.dumps(
{"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
),
)


pcs = set()


async def on_shutdown(app):
# close peer connections
coros = [pc.close() for pc in pcs]
await asyncio.gather(*coros)
pcs.clear()


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Camlio engine")
parser.add_argument("--video-device-index",
help="Index of video camera", type=int, default=0)
parser.add_argument("--cert-file", help="SSL certificate file (for HTTPS)")
parser.add_argument("--key-file", help="SSL key file (for HTTPS)")
parser.add_argument(
"--play-from", help="Read the media from a file and stream it."),
parser.add_argument(
"--port", type=int, default=8080, help="Port for HTTP server (default: 8080)"
)
parser.add_argument("--verbose", "-v", action="count")
args = parser.parse_args()

if args.verbose:
logging.basicConfig(level=logging.DEBUG)

if args.cert_file:
ssl_context = ssl.SSLContext()
ssl_context.load_cert_chain(args.cert_file, args.key_file)
else:
ssl_context = None

app = web.Application()
cors = aiohttp_cors.setup(app, defaults={
# Allow all to read all CORS-enabled resources from
# *.
"*": aiohttp_cors.ResourceOptions(expose_headers="*",
allow_headers="*"),
})
app.on_shutdown.append(on_shutdown)
cors.add(
app.router.add_route("POST", "/offer", offer)
)
web.run_app(app, port=args.port, ssl_context=ssl_context)
Empty file added core/requirements.txt
Empty file.
22 changes: 22 additions & 0 deletions core/video_stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import cv2
import numpy
from aiortc import VideoStreamTrack
from av import VideoFrame


class CamlioVideoStreamTrack(VideoStreamTrack):
def __init__(self, video_device_index):
super().__init__()
self.video_capture = cv2.VideoCapture(video_device_index)

async def recv(self):
try:
pts, time_base = await self.next_timestamp()
ret, raw = self.video_capture.read()

frame = VideoFrame.from_ndarray(raw, format="bgr24")
frame.pts = pts
frame.time_base = time_base
return frame
except Exception as e:
print(e)

0 comments on commit b9320cb

Please sign in to comment.