{"id":13439781,"url":"https://github.com/waveform80/pistreaming","last_synced_at":"2025-04-12T23:41:25.213Z","repository":{"id":19347450,"uuid":"22586673","full_name":"waveform80/pistreaming","owner":"waveform80","description":"A little demo of streaming the Pi's camera to web browsers","archived":false,"fork":false,"pushed_at":"2023-05-09T23:06:30.000Z","size":42,"stargazers_count":766,"open_issues_count":27,"forks_count":206,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-04-04T03:07:40.637Z","etag":null,"topics":["python","raspberry-pi","raspberry-pi-camera","streaming-video"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/waveform80.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2014-08-04T00:06:20.000Z","updated_at":"2025-03-28T11:33:51.000Z","dependencies_parsed_at":"2024-01-14T20:16:33.741Z","dependency_job_id":"dbab5165-bedc-4c5c-a68c-cf5e3b440623","html_url":"https://github.com/waveform80/pistreaming","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waveform80%2Fpistreaming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waveform80%2Fpistreaming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waveform80%2Fpistreaming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/waveform80%2Fpistreaming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/waveform80","download_url":"https://codeload.github.com/waveform80/pistreaming/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248647254,"owners_count":21139081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","raspberry-pi","raspberry-pi-camera","streaming-video"],"created_at":"2024-07-31T03:01:17.047Z","updated_at":"2025-04-12T23:41:25.188Z","avatar_url":"https://github.com/waveform80.png","language":"JavaScript","readme":"# Pi Video Streaming Demo\n\nThis is a demonstration for low latency streaming of the Pi's camera module to\nany reasonably modern web browser, utilizing Dominic Szablewski's excellent\n[JSMPEG project](https://github.com/phoboslab/jsmpeg). Other dependencies are\nthe Python [ws4py library](http://ws4py.readthedocs.org/), my [picamera\nlibrary](http://picamera.readthedocs.org/) (specifically version 1.7 or above),\nand [FFmpeg](http://ffmpeg.org).\n\n\n## Installation\n\nFirstly make sure you've got a functioning Pi camera module (test it with\n`raspistill` to be certain). Then make sure you've got the following packages\ninstalled:\n\n    $ sudo apt-get install ffmpeg git python3-picamera python3-ws4py\n\nNext, clone this repository:\n\n    $ git clone https://github.com/waveform80/pistreaming.git\n\n\n## Usage\n\nRun the Python server script which should print out a load of stuff\nto the console as it starts up:\n\n    $ cd pistreaming\n    $ python3 server.py\n    Initializing websockets server on port 8084\n    Initializing HTTP server on port 8082\n    Initializing camera\n    Initializing broadcast thread\n    Spawning background conversion process\n    Starting websockets thread\n    Starting HTTP server thread\n    Starting broadcast thread\n\nNow fire up your favourite web-browser and visit the address\n`http://pi-address:8082/` - it should fairly quickly start displaying the feed\nfrom the camera. You should be able to visit the URL from multiple browsers\nsimultaneously (although obviously you'll saturate the Pi's bandwidth sooner or\nlater).\n\nIf you find the video stutters or the latency is particularly bad (more than a\nsecond), please check you have a decent network connection between the Pi and\nthe clients. I've found ethernet works perfectly (even with things like\npowerline boxes in between) but a poor wifi connection doesn't provide enough\nbandwidth, and dropped packets are not handled terribly well.\n\nTo shut down the server press Ctrl+C - you may find it'll take a while\nto shut down unless you close the client web browsers (Chrome in particular\ntends to keep connections open which will prevent the server from shutting down\nuntil the socket closes).\n\n\n## Inside the server script\n\nThe server script is fairly simple but may look a bit daunting to Python\nnewbies. There are several major components which are detailed in the following\nsections.\n\n\n### HTTP server\n\nThis is implemented in the `StreamingHttpServer` and `StreamingHttpHandler`\nclasses, and is quite simple:\n\n* In response to an HTTP GET request for \"/\" it will redirect the client to\n  \"/index.html\".\n* In response to an HTTP GET request for \"/index.html\" it will serve up the\n  contents of index.html, replacing @ADDRESS@ with the Pi's IP address and\n  the websocket port.\n* In response to an HTTP GET request for \"/jsmpg.js\" it will serve up the\n  contents of jsmpg.js verbatim.\n* In response to an HTTP GET request for anything else, it will return 404.\n* In response to an HTTP HEAD request for any of the above, it will simply\n  do the same as for GET but will omit the content.\n* In response to any other HTTP method it will return an error.\n\n\n### Websockets server\n\nThis is implemented in the `StreamingWebSocket` class and is ridiculously\nsimple. In response to a new connection it will immediately send a header\nconsisting of the four characters \"jsmp\" and the width and height of the video\nstream encoded as 16-bit unsigned integers in big-endian format. This header is\nexpected by the jsmpg implementation. Other than that, the websocket server\ndoesn't do much. The actual broadcasting of video data is handled by the\nbroadcast thread object below.\n\n\n### Broadcast output\n\nThe `BroadcastOutput` class is an implementation of a [picamera custom\noutput](http://picamera.readthedocs.org/en/latest/recipes2.html#custom-outputs).\nOn initialization it starts a background FFmpeg process (`avconv`) which is\nconfigured to expect raw video data in YUV420 format, and will encode it as\nMPEG1. As unencoded video data is fed to the output via the `write` method, the\nclass feeds the data to the background FFmpeg process.\n\n\n### Broadcast thread\n\nThe `BroadcastThread` class implements a background thread which continually\nreads encoded MPEG1 data from the background FFmpeg process started by the\n`BroadcastOutput` class and broadcasts it to all connected websockets. In the\nevent that no websockets are currently connected the `broadcast` method simply\ndiscards the data. In the event that no more data is available from the FFmpeg\nprocess, the thread checks that the FFmpeg process hasn't finished (with\n`poll`) and terminates if it has.\n\n\n### Main\n\nFinally, the `main` method may look long and complicated but it's mostly\nboiler-plate code which constructs all the necessary objects, wraps several of\nthem in background threads (the HTTP server gets one, the main websockets\nserver gets another, etc.), configures the camera and starts it recording to\nthe `BroadcastOutput` object. After that it simply sits around calling\n`wait_recording` until someone presses Ctrl+C, at which point it shuts\neverything down in an orderly fashion and exits.\n\n\n## Background\n\nSince authoring the [picamera](http://picamera.readthedocs.org/) library, a\nfrequent (almost constant!) request has been \"how can I stream video to a web\npage with little/no latency?\" I finally had cause to look into this while\nimplementing a security camera system using the Pi.\n\nMy initial findings were that streaming video over a network is pretty easy:\nopen a network socket, shove video over it, done! Low latency isn't much of an\nissue either; you just need a player that's happy to use a small buffer (e.g.\nmplayer). Better still there's plenty of applications which will happily decode\nand play the H.264 encoded video streams which the Pi's camera produces ...\nunfortunately none of them are web browsers.\n\nWhen it comes to streaming video to web browsers, the situation at the time of\nwriting is pretty dire. There's a fair minority of browsers that don't support\nH.264 at all. Even those that do have rather variable support for streaming\nincluding weird not-really-standards like Apple's HLS (which usually involves\nlots of latency). Then there's the issue that the Pi's camera outputs raw\nH.264, and what most browsers want is a nice MPEG transport stream (TS). FFmpeg\nseemed like the answer to that, but the version that ships with Raspbian\ndoesn't seem to like outputting valid PTS (Presentation Time Stamps) with the\nPi's output. Perhaps later versions work better, but I was looking for a\nsolution that wouldn't involve users having to jump through hoops to create a\ncustom FFmpeg build (mostly because I could just imagine the amount of extra\nsupport questions I'd get from going that route)!\n\nSo, what about other formats? Transcoding to almost anything else (WebM, Ogg,\netc.) is basically out of the question because the Pi's CPU just isn't fast\nenough, not to mention none of those really solve the \"universal client\"\nproblem as there's plenty of browsers that don't support these formats either.\nMJPEG looked an intruiging (if thoroughly backward) possibility but I found it\nrather astonishing that we'd have to resort to something as primitive as that.\nSurely in this day and age we could at least manage a proper video format?!\n\nThen, out of the blue, and by sheer coincidence a group in Canada got in\ncontact to ask whether the Pi could produce raw (i.e. unencoded) video output.\nThis wasn't something I'd ever been asked before but it turned out to be\nfairly simple, so I added it to the list of tickets for 1.7 and finished the\ncode for it about a week later. I confess I pretty much skimmed the rest of\ntheir e-mail the first time I read it, but with the implementation done I went\nback and read it properly. They wanted to know whether they could use the\npicamera library with Dominic Szablewski's [Javascript-based MPEG1\ndecoder](http://phoboslab.org/log/2013/09/html5-live-video-streaming-via-websockets).\n\nThis was an interesting idea! Javascript implementations are near universal in\nbrowsers these days, and Dominic's decoder was fast enough that it would run\nhappily even on relatively small platforms (for example it runs on iPhones and\nreasonably modern Androids like a Nexus). Furthermore, the Pi is just about\nfast enough to handle MPEG1 transcoding with FFmpeg (at least at low\nresolutions).\n\nOkay, it's not a modern codec like the excellent H.264. It's not using \"proper\"\nHTML5 video tags. All round, it's still basically a hack, and yes it's pretty\nappalling that we have to resort to hacks like this just to come up with a\nuniversally accessible video streaming solution. But hey ... it works, and it's\nnot (quite) as primitive as MJPEG so I'm happy to declare victory. I spent an\nevening bashing together a Python version of the server side. It turned out a\nbit too complex to include as a recipe in the docs, hence why it's here, but I\nthink it provides a reasonable basis for others to work from and extend.\n\nEnjoy!\n\nDave.\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaveform80%2Fpistreaming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwaveform80%2Fpistreaming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwaveform80%2Fpistreaming/lists"}