Raspberry Pi, Camera and Node.js – Live Streaming with Websockets #IoT

This post is part 6 of 9 in the series Raspberry Pi
Tweet about this on TwitterShare on LinkedIn4Share on Google+1Share on Reddit0Buffer this pageFlattr the authorEmail this to someonePrint this page

A few days ago Bala Kolluru has reached out to me asking if we can control a Raspberry Pi camera module using Web browser, so he can view a live stream from any HTML5 powered device. I was intrigued by this idea and wanted to give it a try.

In this post, we will see how we can implement a system that can “stream” a video from our pi to a browser. The completed system would look like

Pretty sweet right! I am able to see my aquarium from any where and check on my only gold fish. This can be extended to do anything.

For instance, you can hook the camera up pointing at the front door, as soon as someone rings the bell, you can see who is at the door by opening the video stream URL in your mobile/tablet/computer and confirm if you need to wear pants to open the door.

So let us see how we can build such an awesome multi-purpose system.

You can find the complete code for this system here.

Prerequisites

If you are new to Raspberry pi and have not yet installed Node.js on it, I would recommend going through Getting Started with Raspberry pi and Node.js.

If you are new to electronics devices and circuits, I would recommend going through the video lectures from All About Circuits.

Components needed

  1. 1 – Raspberry pi B+
  2. 1 – Raspberry pi camera

If you have not already set up the camera module, please follow this.

Understanding MJPEG

Based on what I have googled and understood, there is no straight forward way of streaming the live video from the camera module to a web browser.

After going through a few similar solutions, I kind of sort of decided that MJPEGs are the way to go when dealing with Live streaming from the Pi.

MJPEG is Motion JPEG. You can know more about MJPEG in the below video

In this post we are not really using MJPEG from a technology standpoint, but we are using a similar principle.

The idea is that we keep clicking pictures using the camera say for every 100ms and then save it to the same file. And then we keep sending the same image to the client as it changes every time.

This is really not the best of solutions, but it kind of gets the job done. I am looking for other alternatives too and will update this post as it goes.

Building the system

So, the idea is very simple, we will have a web server setup on the Pi. When we get a request to start the stream, we will trigger a child process in node that will start capturing the picture, once for every 100ms.

Then we have a file watcher on that image file and whenever the file changes, we trigger the client to load the new image.

We are using Web Sockets to emit and act on the events accordingly.

Now, login to your pi via ssh – terminal/putty. As soon as you ssh into pi, you will be landing inside the /home/pi folder. We will create a new folder here named node_programs. And inside this folder, we will be maintaining all our programs. Run

mkdir node_programs

To step inside that folder, run

cd node_programs

For this post, we will create a new folder named liveStreaming and will step inside this folder. Run

mkdir liveStreaming && cd liveStreaming

Note : You can run multiple commands separated by a &&.

First we will initialize a new node project here. Run

npm init

Fill it up as applicable.

Now, we will install express and socket.io modules on our pi. Run

npm install express socket.io --save

Once they are installed, create a new file named index.js. And we will open the same in the nano editor. Run

nano index.js

Paste the below code into the nano editor

Things to notice

Line 1 – 6 : Essential requires

Line 8 : Cache spawn method on child_process

Line 9 : Global proc variable that we store the spawned process

Line 11 : Make the stream folder as a static folder

Line 14 : Default route that will dispatch the index.html

Line 18 : Global sockets object. This will store all the connected sockets

Line 22 : When a client connects to the server, a new socket will be created. This is store in the global variable.

Line 25 : We delete the disconnected client from the global object and if there are no more clients we will stop the streaming (power saving)

Line 36 : We start the streaming on start-stream event.

Line 42 : We start the server

Line 56 : If the capturing is already started, we will not re-init the same. And then emit the last saved image to the client

Line 62 : If the capturing is not started, we will start a new child process and then spawn it with raspistill command. And then register a watch on the file which changes. And whenever the file changes we emit a URL to all the connected clients.

Note : The _t param on the image is to avoid caching

Argument to the raspistill command

  • -w : width 640px
  • -h : height 480px
  • -o : output file ./stream/image_stream.jpg
  • -t : Timeout before the camera stops capturing
  • -tl – Time Limit between captures 100ms

A simple Express/Socket IO server

Let us save the file now. To save the program, press (ctrl+x). This will ask you to save the file. Press Y and press enter key to complete the operation.

Now we will create a new folder named stream at the root of the liveStreaming folder. This is where our image will be saved.

Finally the Websocket client

Things to notice

Line 28 : Init sockets

Line 29 : When there is new image saved, liveStream event will be broadcasted. And then we will fetch the image.

Line 34 : We start streaming when a user clicks on the Start button. If the video has already started by another user, we simply hide the button and show the last saved image.

Line 51 : The Image tag

That is it! save the file as we did above and then we will start the node server. Run

node index.js

And then access the port 3000 on your Raspberry pi port. My pi runs on 192.168.2.2, so my URL would be

http://192.168.2.2:3000

Click on start camera and Bam!! You should see the live feed.

As mentioned earlier it is a big laggy and buggy. You can tweak  -tl – Time Limit between captures to < 50ms and see how it works for you.

Hope this post gave you an ideas as how to “stream” a video from your pi camera to the browser.


Thanks for reading! Do comment.
@arvindr21

 

Series Navigation<< Raspberry Pi, Camera, Node.js – Video & Email The IntruderRaspberry Pi, 16×2 LCD and Node.js – Print stuff >>
Tweet about this on TwitterShare on LinkedIn4Share on Google+1Share on Reddit0Buffer this pageFlattr the authorEmail this to someonePrint this page
  • Arpitha

    awesome work yaar !! But how can i save images telecasted from stream on my pc for further processing

  • raj shah

    Awsm work.. But I need one help..
    Am working on same thing but mine requirement is like Client can download past videos. So how can I store data using Web Socket. And Web Socket is mendet.
    So help me out..

    Thanks..

  • you

    can´t live thit thot

    let app = require(‘express’)();
    let http = require(‘http’).Server(app);
    let io = require(‘socket.io’)(http);

    io.on(‘connection’, (socket) => {
    console.log(‘user connected’);

    socket.on(‘disconnect’, function(){
    console.log(‘user disconnected’);
    });

    socket.on(‘add-message’, (message) => {
    io.emit(‘message’, {type:’new-message’, text: message});
    });
    });

    http.listen(5000, () => {
    console.log(‘started on port 5000′);
    });

    it´s amazing it´s like turbo pascal but more pascal turbo on ms-dos plus 3.4 Vv.rar

  • Michelle Labriaga

    Is iit the same mechanics since I’ve been using IP Camera?

  • Lovely Paari

    wow its great, very amazing blog…….

  • M.Mahrous

    Thanks for sharing the code I modify the code to use a web camera without fs.watch
    https://github.com/mmahrous/Web-Cam-to-Web

  • Christian Carter

    Everything works great, except I cannot get the image to display on the browser screen. I am suppressing the preview on the pi with the “-n” command so I do not see anything. I know the image is being saved into the proper directory, it is just not being broadcast to the webpage. Any thoughts?

  • Sanjay Pandit

    Awesome great articles. Just want know the code in nodejs programming to “How to check, in meeting room someone inside or not? ” via Wifi or Intranet.

    could you please help on it.

  • Diego De Santiago Ruiz

    Thank you, it’s really useful.

  • Anselme Chorein

    As an alternative you can send image directly through websocket using base64 encoding. It should be more efficient and let you avoid the math.random trick (ex: ).

    You can also use node-raspicam module for capture ( https://github.com/troyth/node-raspicam)

  • heesuk

    Hi! this code is very nice. but, i don’t know app.set (whatchigfile ..) and app.get(whatchingfile) .. what is this code? also, I want to frame rate up. this frame rate is slow. if speed up frame rate, use media server? For example, wowza server, Darwin server.. etc… if you comment, very thank!

  • Michel Nicolas

    Hi Arvind,
    great tutorial.

    When i access the camera module from another network, the live stream appears on the raspberry pi screen and not on the webpage from where I accessed using a different network.

    How can I solve this issue ?

    Thank you in advance,

    Micheal

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hey Micheal, Thanks! You cannot access local data from one network on another. You need to make the data/stream from network public may be using https://ngrok.com/.

      • Michel Nicolas

        actually I am able to connect to my raspberry from a different network.
        The problem is that i’m having the live stream on the screen of the raspberry and not on the page where i clicked ‘start camera’ !

        is it more clear ?

  • Hoshos14

    Hi very nice work..thank you but I am not able to see the video i receive this error ..No data received from sensor. Check all connections, including the Sunny one on the camera board

  • Kwangteag Na

    S.O.S
    Thanks a lot!
    I have did your instruction.
    And, live streaming-video is appear on server.
    But disappear on my client computer.
    Why does it???
    Bellow pictures, left Pic is client, right is server.

    • Severin Puschkarski

      you may have been reading the tutorial too fast.
      You need to create a directory:
      mkdir stream

  • andrewhodel

    Here’s some code to do it without websockets (a raw HTTP MJPEG stream)…

    https://github.com/andrewhodel/flirpi-mjpeg/blob/master/app.js

  • Ryan Lee

    I just did a fresh install of latest rasbian yesterday and tried to install this now. Download and install are fine and present no issues but when I run “node -v”, I get the following:

    [email protected] ~ $ node -v
    node: /usr/lib/arm-linux-gnueabihf/libstdc++.so.6: version GLIBCXX_3.4.20' not found (required by node)
    node: /lib/arm-linux-gnueabihf/libc.so.6: version
    GLIBC_2.16′ not found (required by node)

  • Alan

    Hey, great walk through! Your information really helped me in a similar project I was trying to figure out in a DIY home security system. Only question is what is happening with the following line?

    app.set(‘watchingFile’, true);

    I’ve tried to dig around express but I can’t find the ‘watchingFile’ property anywhere to understand how it fits in. I can tell it’s important in keeping the image updated I just don’t fully grasp why. Any information would be appreciated!

    Thanks!

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello Alan, Thanks.

      In Expressjs, you can use set and get custom values on the app context. This is like storing variables on the Expressjs instance itself.

      Take a look at : http://expressjs.com/api.html#app.set

      Thanks.

  • spiovesan

    Anvrid, thanks for sharing. I just changed the sample to write to a ram drive to avoid to write too much on the SD card, as explained here: https://www.domoticz.com/wiki/Setting_up_a_RAM_drive_on_Raspberry_Pi. Because my raspicam went broken, I used a USB one replacing the spawn(‘raspistill’, args); with spawn(‘fswebcam’, args);

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Thanks spiovesan. Are you facing any issue with this approach?

      • spiovesan

        Arvind, it works fine without problems. And using dyndns service with port mapping I am also able to monitor remotely the camera.

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          Great!

  • http://www.christopherstevens.cc/ Christopher Stevens

    Very cool. Thanks for sharing. I’m exploring how I can apply this concept as a starter for broadcasting images from Mars to Mission Control (a lab of Lego Mindstorms rovers sending images to a lab of computers next door). It’s pretty straight forward thanks to you! Kids will love it and it could make missions more engaging at the Space Foundation Discovery Center: http://www.spacefoundation.org/museum

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello Christopher, Thanks for sharing. Glad that I could be of help. Let me know about other exciting projects you are doing with this. Also do let me know if you need me to implement some more apps like this.

      Thanks.

      • http://www.christopherstevens.cc/ Christopher Stevens

        Will do! At the moment, I’m drafting up some ideas with a co-worker. We may mix in some extra sensor data and such to go along with the camera image(s). I’ll send you a link when something happens, likely sharing the code as well. 😀

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          That would be awesome.! Thanks.

  • camillo777

    Good! Apart from MJPEG, do You have a solution to stream audio as well together with video?

  • Jan Szymanski

    Hi,
    Have you done a similar thing, but using USB camera instead?

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello Jan. I have not tried it, but I think that should be possible. May be we need to jump through to get the video feed but I think it’s doable. Do let me know, if you try it out. Thanks.

  • Rashiqa Zahid

    hello arvind,,, your tutorials are really very helpful.. but there is a problem im facing in implementation of this code..
    The code has no build errors… the webserver is running perfectly,, but,,, whenever i click the button start camera, nothing happens…

    can you please help?

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Thanks Rashiqa. Can add a few console.log() in your program flow check till where things are running? If there was an error message some where I can probably help you with that.

      • Rashiqa Zahid

        I worked a little and it worked.. its awesome..!! Thanks alot..! 😀

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          Great! What was the issues?

  • Rashiqa Zahid

    hello arvind.. awesome tutorials i must say,, as i am newbie to raspberry pi, i find your tutorials really very helpful..
    There is a problem that im facing with this tutorial,, i have copied the code exactly as you have written and still i get the following error:

    /home/pi/node_programs/livestreaming/index.js:11
    app.use(‘/’ express.static(path.join(_dirname, ‘stream’)));
    ^^^^^^^
    SyntaxError: Unexpected identifier
    at exports.runInThisContext (vm.js:73:16)
    at Module._compile (module.js:443:25)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

    can you please help?

  • Rashiqa Zahid

    /home/pi/node_programs/livestreaming/index.js:11
    app.use(‘/’ express.static(path.join(_dirname, ‘stream’)));
    ^^^^^^^
    SyntaxError: Unexpected identifier
    at exports.runInThisContext (vm.js:73:16)
    at Module._compile (module.js:443:25)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:501:10)
    at startup (node.js:129:16)
    at node.js:814:3

  • ahmed

    Hi, very nice work,thank you… If I may, the stream is not considered a video because there is a big latency (about 4 seconds), i.e. each 4 seconds it shows the current image while the images in between seem to be not viewwed. I tried to change the values * 100000 and 100 msec. but didn’t make a difference. Do you suggest something? or is that the best that can be provided by raspistill and node.js ??

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Thanks Ahmed. Yes, as I mentioned, these are motion jpegs. This is a simulated video streaming, and yes, there is a huge delay in the video. As of now I don’t have any solution for this. If you do get any, let me know. Thanks.

    • https://github.com/kendou kent72

      I believe fs.watchFile() is the cause of the latency, use fs.watch() instead, or check out a revised version at: https://github.com/kendou/pi_livestreaming.
      Use a ramdisk can also help.

      • ajay kota

        can please tell me ,how much latency you are getting.

  • ken

    Hi,
    I am having a school project using my raspberry pi 2. and I have question, I got everything setup and the last step when I click the start camera botton, the display screen didn’t show on my desktop monitor, but it shows on the raspberry pi 2 monitor. can you help me? please!

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello Ken, Try closing the screen on the pi and launching the link from your desktop browser.

      • ken

        Hi, what do you mean by closing the screen on the pi?(I have tried to unplug the HDMI cable and run the program again, but it still not show on my monitor) Do you have an example with more detail to show how this work? I watched your video at the beginning of this article and it work just fine, but it didn’t work on mine. Please help me!! I am still a beginner for this.

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          Hi Ken, Can you try forking the repo and running it as is and see if things work? Thanks.

  • Mo

    when i write this command
    node index.js
    I get
    -bash: node: command not found
    could you help me with that !?

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Please read the Prerequisites section.

      • mo

        ok thanks

  • Johan

    I would like to add a microphone streaming sound to this system, how can i do it ?

  • randomite123

    Awesome, great tutorial

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Thanks

  • Juan 

    (sorry for my english first of all)
    Hi Arvind, I’m doing a project and this tutorial is helpful.

    but I’m doing it, tcp-client send the information from the camera to a tcp server. and display it for html. but the tcp do not send this information. Might you help me?

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello Juan, Thanks! Can you let me know at what step it is failing?

  • Pat

    Is it possible to use a ps3 camera instead of rasp-camera with this code ?

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Not sure Pat. Did not try on ps3. But would be an interesting experiment.

  • Patrick

    I had the same issue. Cam did start but no image was saved. Solved the issue by simply removing the quotation in args from var args = [… “‘./stream/image_stream.jpg'”…]; to var args = [… “./stream/image_stream.jpg”…];

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Thanks for sharing Patrick. Okay, looks like this is working for most of the people. But I am still facing issues. We give it one more try and update the source code.

  • Shrikant

    Thanks Arvind, I am able to see the image/video on the console where raspberry monitor is connected… but when I browse the url from my mobile , its not showing the image/video on the mobile console…but it opens up diagloag showing the image/video on the monitor when raspberry is connected.

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hey Shrikant. Thanks. I did not get your question. Are you able to see or not able see it on your mobile when the recording is going on?

      • shrikant

        No I did not see the recording on my mobile. whenever I click on the start camera button from the mobile browser, it opens up recording window on the monitor where I connected my monitor and not non the mobile.

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          If you click start on mobile, it will show the feed on all the devices which have that webpage open, that includes both your desktop monitor and your phone browser, we are using the emit on sockets, hence the behavior.

          Is this what you were referring to?

          Thanks.

          • shrikant

            Thanks for the reply Arvind… I do not see it on my mobile browser

          • http://thejackalofjavascript.com/ Arvind Ravulavaru

            Strange! Can you close your desktop browser and open the link only in the mobile browser?

  • http://blog.whatgeek.com.pt Bruno Santos

    Yellow !

    Love your script ! After fighting for an hour i finally got it working.
    I was having the same problem as Arvind, but after a bit of code scrutiny – and a lot of try and error – your index.js line 61 – the arguments for the raspistill image location – :

    “‘./stream/image_stream.jpg'”

    has an error – both apostrophes must be removed keeping only the double quotation marks:

    “./stream/image_stream.jpg”

    After that, it started working !

    Best regards and thank you !

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Thanks Bruno! I am not sure why but when I run the program without quotes, it does not work. I was aware that the extra quotes were not required. Let me see, if more people can get it running without the quotes, I will update the code. Thanks for sharing!

      • http://blog.whatgeek.com.pt Bruno Santos

        Hello Arvind ! Thank you for the reply

        I’m using raspbian . At least with that distro it doesn’t work and no error is reported… Probably with other RPI distros is different… It can be the bash version, or node.js version… At least, if someone reports the same, it knows what the problem might be !

        Best regards and thank you for the tutorial ! Keep up !

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          I was thinking the same too. Lets see, Thanks.

      • Ted Pedersen

        Hey Arvind and Bruno, I was only able to get the program working after removing the quote as well, FYI.

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          Thanks Ted. Fixed the code.

  • kgray

    Hey, thanks for posting this great tutorial! I am trying to use your code to set this up and I am running into a problem, though. I am able to run index.js and it appears to be working fine, but there is no camera feed when I access http://:3000. The interface is there, with “Start Camera” button and everything, but no video stream. Any suggestions?

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello kgray, Thanks!

      Couple of things

      1. Are there any images being saved to your local folder on your Pi?

      2. Can you add a few console logs to check if the capture script is actually getting triggered?

      And any errors on the node console or browser dev tools console?

      Thanks,
      Arvind

      • kgray

        Hey, Arvind!
        Here’s a log from the terminal running index.js:

        [email protected] ~ $ cd node_programs/liveStreaming
        [email protected] ~/node_programs/liveStreaming $ node index.js
        listening on *:3000
        Total clients connected : 1
        Total clients connected : 1
        Watching for changes…
        [email protected] ~/node_programs/liveStreaming $ cd stream

        [email protected] ~/node_programs/liveStreaming/stream $ ls
        [email protected] ~/node_programs/liveStreaming/stream $

        Also, you can see no .jpg being stored in the ‘stream’ folder.
        Thanks for your help!

        • http://thejackalofjavascript.com/ Arvind Ravulavaru

          Hello Kgray,

          Let us take a step back

          1. Can you check if camera module is enabled in BIOS?
          2. Are you able to take pictures/record video using Pi’s Prompt?

          3. Can you create a new js file test.js and add the below code and run
          node test.js

          And see if the image is getting generated?

          And you will know which step fails, depending on that you can fix the issue.

          Thanks,
          Arvind.

          • kgray

            Hey, Arvind.

            The camera is definitely enabled. I am able to stream video to a PC using vlc and take stills. I ran the test.js file as you suggested and there is still no image being saved to the ‘stream’ folder. I’m pretty sure the problem is with the image saving bit. I can see the camera initialize when I use the “Start Camera’ button from the html interface (I still have the led enabled).
            The image should be saving here: [email protected] ~/node_programs/liveStreaming/stream $, right?

            Thanks again.
            Kyle

          • http://thejackalofjavascript.com/ Arvind Ravulavaru

            Hello Kyle,

            Yup you are correct. For now, just to make sure things are fine with node js and pi, can you use this node module : https://www.npmjs.org/package/raspicam and try and see if you are able to take a picture or a video?

            Thanks.

          • kgray

            Arvind,

            I was able to use the node module to take a still and save it to a file.

            Kyle

          • http://thejackalofjavascript.com/ Arvind Ravulavaru

            Not sure if this would work but, can you try below 2 solutions
            1. Manually creating the folder where image gets saved and set permissions to write for any user
            2. Delete the folder and let camera API create it for you while saving

          • Matthew Harwood

            Make sure you create a folder in ./stream if not it wont save the file. otherwise great tut.

          • http://thejackalofjavascript.com/ Arvind Ravulavaru

            Thanks Matthew!

    • http://thejackalofjavascript.com/ Arvind Ravulavaru

      Hello kgray. Are you still facing the issue?