Skip to content

Instantly share code, notes, and snippets.

@xxl007
Last active November 22, 2022 21:01
Show Gist options
  • Save xxl007/5db5a3625f3a5914541c84c37b380bfa to your computer and use it in GitHub Desktop.
Save xxl007/5db5a3625f3a5914541c84c37b380bfa to your computer and use it in GitHub Desktop.
IP camera appliance with Ubuntu on a Raspberry Pi 3

IP camera appliance with Ubuntu on a Raspberry Pi 3

Setup - Raspberry Pi 3 with camera module

Hardware bill of materials and setup instructions

Install Ubuntu 18.04 LTS (32 bit)

Link to the Ubuntu 18.04 image for the Raspberry Pi 3

Install PiCamera (python interface to camera)

The application will be coded in python 3. The associated pip version needs to be installed:

$ sudo apt install python3-pip

Picamera can be installed directly via pip:

$ sudo pip3 install picamera

To configure the Raspberry Pi to enable the camera module, open up the board configuration file with a text editor:

$ sudo nano /boot/firmware/config.txt

Add the following line to enable the camera:

start_x=1

Install complementary packages

While the official image includes compatible firmware, bootloader and kernel, there are a few packages available in an unofficial PPA (ppa:ubuntu-raspi2/ppa) which are useful on the Raspberry Pi, including:

libraspberrypi-bin - VideoCore utilities from https://github.com/raspberrypi/userland such as vcgencmd, raspistill, etc.

To install required but the missing libraries:

$ sudo add-apt-repository ppa:ubuntu-raspi2/ppa

$ sudo apt-get update

$ sudo apt-get install libraspberrypi-bin

Create python script

Create a project directory. In the project directory, create a python file with a text editor:

$ sudo nano ipcamera.py

Then enter the following script that will stream the camera input to a web server:

#!/usr/bin/env python3

import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server

PAGE="""\
<html>
<head>
<title>Your Ubuntu IP Camera Appliance</title>
</head>
<body>
<h1>Ubuntu IP Camera Appliance</h1>
<img src="stream.mjpg" width="1280" height="720" />
</body>
</html>
"""

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        self.buffer = io.BytesIO()
        self.condition = Condition()

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # New frame, copy the existing buffer's content and notify all
            # clients it's available
            self.buffer.truncate()
            with self.condition:
                self.frame = self.buffer.getvalue()
                self.condition.notify_all()
            self.buffer.seek(0)
        return self.buffer.write(buf)

class StreamingHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGE.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/stream.mjpg':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
            self.end_headers()
            try:
                while True:
                    with output.condition:
                        output.condition.wait()
                        frame = output.frame
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame))
                    self.end_headers()
                    self.wfile.write(frame)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        else:
            self.send_error(404)
            self.end_headers()

class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

with picamera.PiCamera(resolution='1280x720', framerate=24) as camera:
    output = StreamingOutput()
    camera.start_recording(output, format='mjpeg')
    try:
        address = ('', 8000)
        server = StreamingServer(address, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()

Test application

Run $ python ipcamera.py to launch the application.

The video stream is accessible through any browser from any computer in the local network.

http://<board IP address>:8000/

The network IP address of the board can be obtained via command: hostname -I.

The firewall can be configured to allow access to the video stream from any computer connected to the web.

Package application into a snap

In the same project directory, create the snapcraft.yaml file for the application:

$ sudo nano snapcraft.yaml

The content of the snapcraft.yaml file is below:

name: rpi-ipcamera
version: '0.1'
summary: demo IP camera app on Ubuntu for the Raspberry Pi 3
description: |
  A demo IP camera appliance on Ubuntu. Video from a camera attached to the
  Raspberry Pi CSI interface is streamed to the web on port 8000. 

  To watch the video stream go to http://<IP address of your Board>:8000/

grade: stable
confinement: strict

base: core18

architectures:
  - build-on: armhf
    run-on: armhf

apps:
  ipcamera:
    command: ipcamera
    daemon: simple
    plugs:
      - mount-observe
      - network
      - network-bind
      - opengl

parts:
  picamera:
    plugin: python
    python-version: python3
    python-packages:
      - picamera
    build-environment:
      - READTHEDOCS: 'True'
  pilibs:
    plugin: nil
    override-pull: |
      sudo add-apt-repository ppa:ubuntu-raspi2/ppa
      sudo apt-get update
    build-packages:
      - software-properties-common
    after: [ picamera ]
  ipcamera:
    source: .
    plugin: nil
    override-build: |
      mkdir -p $SNAPCRAFT_PART_INSTALL/bin
      cp ipcamera $SNAPCRAFT_PART_INSTALL/bin/
    stage-packages:
      - libraspberrypi-bin
    after: [ pilibs ]

Build the application snap

The application snap will be built in a lightweight virtual machine on the Raspberry Pi. To this end, LXD needs to be installed and initialised.

$ sudo snap install lxd

$ sudo lxd init

After the installation, a VM instance of Ubuntu 18.04 will be created to build the snap.

$ sudo lxd launch bionic

The snap can be built by simply executing the snapcraft command in the project directory:

$ sudo lxc exec xenial — bash

$ mkdir ipcamera && cd ipcamera

$ wget https://raw.githubusercontent.com/xxl007/ipcamera-snap/master/snapcraft.yaml

$ wget https://raw.githubusercontent.com/xxl007/ipcamera-snap/master/ipcamera

$ snapcraft

Push application to the snap store

To share your snaps you need to publish them in the Snap Store. First, create an account on the dashboard. Here you can customise how your snaps are presented, review your uploads and control publishing.

You’ll need to choose a unique “developer namespace” as part of the account creation process. This name will be visible by users and associated with your published snaps.

Make sure the snapcraft command is authenticated using the email address attached to your Snap Store account:

$ snapcraft login

Use snapcraft to push the snap to the Snap Store.

$ snapcraft push --release=edge ipcamera_*.snap

If you’re happy with the result, you can commit the snapcraft.yaml to your GitHub repo and turn on automatic builds so any further commits automatically get released to edge, without requiring you to manually build locally.

Download and install Ubuntu Core

Follow the instructions to download Ubuntu Core 18 for the Raspberry Pi 3

Install application as a snap

Install your snap from the global snap store on your board running Ubuntu Core.

snap install ipcamera

Run application

Upon installation, the video stream is accessible through any browser from any computer in the local network. (Do not forget to connect and enable the camera in the configuration file as described above).

http://<board IP address>:8000/

Update application

$ snapcraft release ipcamera 1 candidate

$ snap refresh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment