Host: Add GIF streaming tool
This commit is contained in:
parent
fefb33736a
commit
34f2064f05
2 changed files with 111 additions and 6 deletions
111
host/gifstream.py
Executable file
111
host/gifstream.py
Executable file
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env python3
|
||||
# This script eats CRAP and outputs a live GIF-over-HTTP stream. The required bandwidth is about 20kB/s
|
||||
|
||||
import io
|
||||
import math
|
||||
import threading
|
||||
import struct
|
||||
import argparse
|
||||
from itertools import chain
|
||||
from contextlib import suppress
|
||||
|
||||
from PIL import Image, ImageFile
|
||||
from flask import Flask, Response
|
||||
|
||||
import config
|
||||
|
||||
import crap
|
||||
|
||||
|
||||
frame = None
|
||||
frame_cond = threading.Condition()
|
||||
|
||||
def framestream():
|
||||
global frame
|
||||
while True:
|
||||
with frame_cond:
|
||||
frame_cond.wait()
|
||||
img = Image.frombuffer('RGB', (config.display_width, config.display_height), frame, 'raw', 'RGB', 0, 1)
|
||||
yield encode_image(img)
|
||||
|
||||
def putframe(f):
|
||||
global frame
|
||||
with frame_cond:
|
||||
frame = f
|
||||
frame_cond.notify_all()
|
||||
|
||||
def file_headers(w, h):
|
||||
yield (b'GIF89a'
|
||||
+ struct.pack('<H', w) # image width
|
||||
+ struct.pack('<H', h) # image height
|
||||
+ b'\x70' # flags bits 2-4: color resolution = 7
|
||||
+ b'\x00' # background color index
|
||||
+ b'\x00' # pixel aspect ratio (1:1)
|
||||
# Netscape headers
|
||||
+ b'\x21' # extension block
|
||||
+ b'\xff' # app extension
|
||||
+ b'\x0b' # block size
|
||||
+ b'NETSCAPE2.0' # app name
|
||||
+ b'\x03' # sub-block size
|
||||
+ b'\x01' # loop sub-block id
|
||||
+ b'\x01\x00' # repeat count (0 == Infinity)
|
||||
+ b'\x00' # block terminator
|
||||
)
|
||||
|
||||
def encode_image(img):
|
||||
img = img.convert('P', palette=Image.ADAPTIVE, colors=256)
|
||||
palbytes = img.palette.palette
|
||||
# assert len(img.palette) in (2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0x100)
|
||||
imio = io.BytesIO()
|
||||
ImageFile._save(img, imio, [("gif", (0, 0)+img.size, 0, 'P')])
|
||||
return (
|
||||
# GCE
|
||||
b'\x21' # extension block
|
||||
+ b'\xf9' # graphic control extension
|
||||
+ b'\x04' # block size
|
||||
+ b'\x00' # flags
|
||||
+ b'\x00\x00' # frame delay
|
||||
+ b'\x00' # transparent color index
|
||||
+ b'\x00' # block terminator
|
||||
# Image headers
|
||||
+ b'\x2c' # image block
|
||||
+ b'\x00\x00' # image x
|
||||
+ b'\x00\x00' # image y
|
||||
+ struct.pack('<H', img.width) # image width
|
||||
+ struct.pack('<H', img.height) # image height
|
||||
+ bytes((0x80 | int(math.log(len(palbytes)//3, 2.0))-1,)) # flags (local palette and palette size)
|
||||
# Palette
|
||||
+ palbytes
|
||||
# LZW code size
|
||||
+ b'\x08'
|
||||
# Image data. We're using pillow here because I was too lazy finding a suitable LZW encoder.
|
||||
+ imio.getbuffer()
|
||||
# End of image data
|
||||
+ b'\x00'
|
||||
)
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
#app.debug = True
|
||||
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
return Response(chain(file_headers(config.display_width, config.display_height), framestream()), mimetype='image/gif')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('addr', default='127.0.0.1', nargs='?')
|
||||
parser.add_argument('port', type=int, default=1337, nargs='?')
|
||||
args = parser.parse_args()
|
||||
|
||||
udp_server = crap.CRAPServer(args.addr, args.port, blocking=True, log=lambda *_a: None)
|
||||
|
||||
def receiver():
|
||||
for _title, frame in udp_server:
|
||||
putframe(frame)
|
||||
frame_runner = threading.Thread(target=receiver, daemon=True)
|
||||
frame_runner.start()
|
||||
|
||||
app.run()
|
||||
udp_server.close()
|
||||
|
|
@ -1,14 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import socket
|
||||
from time import time
|
||||
import sys
|
||||
from contextlib import suppress
|
||||
import argparse
|
||||
import atexit
|
||||
|
||||
import config
|
||||
|
||||
import bdf
|
||||
import crap
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue