├── README.md ├── play_video_stream ├── read.py ├── screenshot.gif ├── stream_webcam └── write.py /README.md: -------------------------------------------------------------------------------- 1 | # Video streaming with RethinkDB 2 | 3 | This demo shows that streaming video is possible with [RethinkDB 4 | changefeeds](http://rethinkdb.com/blog/realtime-web/). It even 5 | supports recording multiple streams at the same time and streaming to 6 | multiple viewers. 7 | 8 | ![Screenshot](screenshot.gif) 9 | 10 | # Usage 11 | 12 | Record video from your webcam into a RethinkDB table called 'hello': 13 | 14 | ``` 15 | ./stream_webcam 127.0.0.1 28015 hello 16 | ``` 17 | 18 | Live stream the 'hello' table: 19 | 20 | ``` 21 | ./play_stream 127.0.0.1 28015 hello 22 | ``` 23 | 24 | 25 | Stream from the 'hello' table starting at the beginning: 26 | 27 | ``` 28 | ./play_stream 127.0.0.1 28015 hello 0 29 | ``` 30 | 31 | # Data format 32 | 33 | Data is formatted as chunks (with a default size of 1 KiB). Each chunk 34 | is associated with an auto-incremented integer id and stored in the 35 | table associated with the stream. 36 | 37 | ``` 38 | { 39 | "id": 0, 40 | "chunk": r.binary(...) 41 | } 42 | ``` 43 | 44 | The last document in the stream has an `end` field instead of `chunk`. 45 | 46 | ``` 47 | { 48 | "id": 2294, 49 | "end": true 50 | } 51 | ``` -------------------------------------------------------------------------------- /play_video_stream: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ./play_video_stream 4 | 5 | ./read.py "$@" | mplayer -cache 1000 - 6 | -------------------------------------------------------------------------------- /read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # ./read.py [n] 4 | # Stream binary data from the table 'name' starting from chunk number 'n' 5 | 6 | from sys import argv 7 | from os import write 8 | import rethinkdb as r 9 | 10 | conn = r.connect(argv[1], int(argv[2])) 11 | 12 | table = argv[3] 13 | db_table = r.db('streams').table(table) 14 | 15 | try: 16 | i = int(argv[4]) 17 | except: 18 | i = None 19 | 20 | try: 21 | db_table.info().run(conn) 22 | except: 23 | i = 0 24 | write(2, 'Waiting for table ' + table + '...') 25 | (r.db('rethinkdb') 26 | .table('table_status') 27 | .filter({'name': table, 'db': 'streams'}) 28 | .changes() 29 | .run(conn) 30 | .next()) 31 | db_table.wait().run(conn) 32 | write(2, ' found\n') 33 | 34 | def read(): 35 | global i 36 | end = False 37 | changes = db_table.changes()['new_val'].run(conn) 38 | try: 39 | ended = 'end' in db_table.max(index='id').run(conn) 40 | if ended: 41 | changes.close() 42 | except: 43 | ended = False 44 | if not ended: 45 | last_i = changes.next()['id'] 46 | else: 47 | last_i = None 48 | if not i is None: 49 | for row in (db_table 50 | .between(i, last_i, right_bound='closed') 51 | .order_by(index= 'id') 52 | .run(conn)): 53 | i = row['id'] 54 | if 'end' in row: 55 | end = True 56 | break 57 | else: 58 | yield row['chunk'] 59 | if not end and not ended: 60 | future = {} 61 | for row in changes: 62 | future[row['id']] = row 63 | while last_i + 1 in future: 64 | last_i = last_i + 1 65 | row = future[last_i] 66 | del future[last_i] 67 | if 'end' in row: 68 | changes.close() 69 | else: 70 | yield row['chunk'] 71 | 72 | for chunk in read(): 73 | write(1, chunk) 74 | -------------------------------------------------------------------------------- /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AtnNn/rethinkdb-stream/add48dba353138a645390f737d89a105b7d04b2d/screenshot.gif -------------------------------------------------------------------------------- /stream_webcam: -------------------------------------------------------------------------------- 1 | !/bin/bash 2 | 3 | # ./stream_webcam 4 | 5 | # Record a video from a linux webcam into a RethinkDB table 6 | 7 | ffmpeg -f video4linux2 -r 25 -g 5 -s 640x480 -i /dev/video0 -c:v libx264 -f h264 -tune zerolatency - | ./write.py "$@" -------------------------------------------------------------------------------- /write.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # ./write.py 4 | 5 | # Stream data into the table 'name' in 1k chunks 6 | 7 | from sys import argv 8 | from os import read 9 | from itertools import count 10 | import rethinkdb as r 11 | 12 | conn = r.connect(argv[1], int(argv[2])) 13 | 14 | table = argv[3] 15 | 16 | try: 17 | r.db_create('streams').run(conn) 18 | except: 19 | pass 20 | 21 | try: 22 | r.db('streams').table_drop(table).run(conn) 23 | except r.errors.RqlError: 24 | pass 25 | r.db('streams').table_create(table).run(conn) 26 | 27 | try: 28 | for i in count(): 29 | data = read(0, 1024) 30 | if not data: 31 | break 32 | r.db('streams').table(table).insert({'id': i, 'chunk': r.binary(data)}).run(conn, durability='soft') 33 | finally: 34 | r.db('streams').table(table).insert({'id': i, 'end': True}).run(conn, durability='soft') 35 | 36 | --------------------------------------------------------------------------------