4 ms·
Show HN: MaryJane – Mjpeg server in 30 lines of async Python
- lostsock 5y agoSeems to be a useful and "dumb" way of syncing playback across multiple devices, windows or browsers. Nice work
- deleted 5y ago[deleted]
- mung1 5y agoThere is was a tiny http server called hibachi that was about 1500bytes(or whatever ioccc max?) and many years back I implemented mjpeg cgi script for it in busybox posix shell script. I forget how long it was but I would guess no more that 100 lines of sh.
- andrewstuart 5y agoMaryJane MJPEG server in < 30 lines of Python. Here is a demo: http://18.116.60.15:8080/maryjane/ http://18.116.60.15:8080/maryjane/ Here's the 30 lines of Python: https://raw.githubusercontent.com/bootrino/maryjane/master/mjpegserverpython.py https://raw.githubusercontent.com/bootrino/maryjane/master/m... Here is the github: https://github.com/bootrino/maryjane https://github.com/bootrino/maryjane Browsers are able to display images such as PNG, JPG and GIF. A less known capability of browsers is to display a stream of JPG images. This is called MJPEG (motion JPEG). All you need to do is specify the MPJEG server address in an image tag and it just works. It's written in less than 30 lines of async Python. This project is MIT licensed.
- tdeck 5y agoIf I understand multipart/x-mixed-replace right, this could be used with other image formats as well? Really cool use of an almost forgotten technology.
- bawolff 5y agoTypically though other image formats have better options. gif, apng, webm, etc (and chrome killed support for the non image case). The main place x-mixed-replace makes sense is a dead simple webcam that takes a picture every x seconds. Even then, in the modern world it probably makes more sense to just implement some streaming video stuff. MJPEG is not bandwidth efficient.
- ramses0 5y ago#but-it-just-works
- andrewstuart 5y agoThe fact that "it just works" by putting an address in an IMG tag is really important. Streaming setups can get complex fast and sometimes it's worth paying the bandwidth penalty of mjpeg in order to build something super simple.
- andrewstuart 5y agoAs mentioned in a different comment - mjpeg is certainly less bandwidth efficient, but it's super simple to implement - just the Python code I've posted plus an image tag. Also, streaming protocols tend to have a number of seconds latency, which for certain types of applications isn't acceptable. That's when mjpeg might do the job because it just instantly starts blasting frames down the tubes.
- monocasa 5y ago> Also, streaming protocols tend to have a number of seconds latency, which for certain types of applications isn't acceptable. That's when mjpeg might do the job because it just instantly starts blasting frames down the tubes. In this case you might run into issues there. This demo is all on TCP which means head of line blocking. That's why you typically see MJPEG wrapped in rtp rather than http.
- bawolff 5y ago
- sitkack 5y agoGreat name. I wrote this in Tornado a bunch of years ago. Where do you go next? What else could you do?
- andrewstuart 5y agoA fun thing to do might be have write on on demand video thumbnail player. To do this you'd have a bunch of video files at the back end, specify which one you want in the URL, then get Python to run ffmpeg, extracting jpeg frames from the specified video and writing the frames to the ramdrive /dev/shm. As the user rolls the mouse over, some JavaScript changes the img tag to point to the mjpeg server and the video plays. It should be possible to do this in only four or five extra lines I think. See the github readme for the correct ffmpeg command line to do this. I do wonder if there's a way to stream audio in sync.
- foxhop 5y agoI like that. Could be neat if uploaded could specify key frames to jump to to start the preview.
- nemosaltat 5y agoAlso seems like a hacky way to get video to play on my Tesla browser. I could probably couple this with a Raspberry Pi and pipe the audio to Bluetooth for the speakers, then stream the video content from the Pi to a web server that I access through the Tesla browser.
- deleted 5y ago[deleted]
- a-dub 5y agohmmm. i'd consider replacing the sleep with a wait on inotify. having the server and image renderer run on different non-synchronized clocks means that frames may occasionally be doubled/dropped.
- a-dub 5y agohere we go, aionotify: https://pypi.org/project/aionotify/ https://pypi.org/project/aionotify/
- andrewstuart 5y ago>> having the server and image renderer run on different non-synchronized clocks Would you mind please helping me understand what this means, in detail? I'm pretty sure there's no clock synchronisation anywhere.
- ficklepickle 5y agoI think they mean the clock involved with replacing the image every X ms + the clock in your python code. You could remove the python clock code in favour of being notified when the file changes. That's my tired understanding, anyway.
- andrewstuart 5y agoThere's no frame rate built into MJPEG, so the code that calculates frame budget cannot be avoided. It might be a good idea to insert a inotify immediately before the file read. You'd still need the timing code though.
- a-dub 5y agohere's an example: f_rate = frame rate = 30 fps (for example) f_period = 1/f_rate = 1/30 = 33ms imagine the server serves every f_period, and the image gets updated on the ramdisk every f_period + delta where delta = f_period/16 because the clock from the image updater is not synchronized with the server and is a little slower (by some idealized fixed amount). in this case, every 16 frames, the image updater would advance past the server, and the server would serve a duplicate frame. in practice, delta is often not fixed, and can be positive or negative and instead is described with a distribution called jitter. both the server and the image updater have jitter, so duplicate/dropped frames will occur as they leap ahead and behind each other with the +/- jitters for each unsynchronized clock getting added to their respective periods for each frame. (this is, of course, assuming they're running at the same rate, which they often do not- which is why ultimately you want one to be the primary clock and the other to be driven by it)
- muxator 5y ago<a href={"https://github.com/bootrino/maryjane https://github.com/bootrino/maryjane"}> What are the curly braces around the URL in <a>'s href attribute? Is there a spec for it?
- andrewstuart 5y agoA bug.
- leipeleon 5y agoI've used the ffserver from ffmpeg for a few projects: https://trac.ffmpeg.org/wiki/ffserver https://trac.ffmpeg.org/wiki/ffserver https://github.com/LeipeLeon/destaat https://github.com/LeipeLeon/destaat sadly (but understandable) ffserver has been removed from ffmpeg on 2018-01-06.
- unraveller 5y agothat's a 404 repo, my dude.
- leipeleon 5y ago:facepalm:.... i've made it public, no need to keep it a secret :P
- eichin 5y agoOne use for this is using old android tablets as ambient display screens - you just autostart a browser on an mjpeg url and update the image to update the screen. Note that mjpeg doesn't have a frame rate - it just renders the frames as they show up, so you don't actually have to keep sending them when they're not changing... except that chrome is buggy about not actually rendering the image right when it shows up :( (I did a version of this in bash with netcat, and here-documents for the mime parts, which is where I ran into the problem. https://underjord.io/live-server-push-without-js.html https://underjord.io/live-server-push-without-js.html was the inspiration for the display-screen case...)
- thewakalix 5y agoYou can do it in even fewer with Haskell :P https://hub.darcs.net/thewakalix/flapper/browse/app/Main.hs https://hub.darcs.net/thewakalix/flapper/browse/app/Main.hs