As a YouTuber, I'm always looking for ways to streamline different tasks. Live streaming is definitely something that has the opportunity to be a LOT simpler. It would be nice if you just had a camera that you can take with you, turn on, and live stream. So that will be the primary goal of this project: to make a dedicated camera for live streaming to YouTube.

My idea is to make a video camera using a Raspberry Pi 3, 2.8" LCD Touchscreen, and the Pi Camera as the basic platform. We'll also need a small USB microphone to record audio, and some type of rechargeable battery to power it all. Then I'm going to 3D print a custom case shaped like the YouTube logo to power it all.

More of a visual learner? Then feel free to watch my four part video series on making this project from start to finish! View the successes and pitfalls as I go step by step through the entire project.

These are the parts you will need to finish this project, along with access to soldering equipment and a 3D printer

1 x Raspberry Pi 3 Model B
Raspberry Pi 3 - Model B - ARMv8 with 1G RAM
1 x 2.8" TFT LCD Touchscreen
Adafruit PiTFT - 320x240 2.8" TFT+Touchscreen for Raspberry Pi
1 x Raspberry Pi Camera
Raspberry Pi Camera Board v2 - 8 Megapixels
1 x Mini USB Microphone
Mini USB Microphone
1 x 2500 mAh battery
Lithium Ion Polymer Battery - 3.7v 2500mAh
1 x LiPo Battery Charger
USB LiIon/LiPoly charger - v1.2
1 x Slide Switch
Breadboard-friendly SPDT Slide Switch

As with any project using a Raspberry Pi, the first step is to get it set up. To do this, you'll need:

  • A Raspberry Pi 3
  • A micro SD Card (8gb or greater)
  • HDMI or composite monitor and cable
  • Keyboard and Mouse
  • Power supply

With the hardware set, we next need to load the software onto the PI. You can download the latest Raspbian software from here. It's a large file, so it may take a while to download. But once it has, you can burn it to your SD card using the Etcher.io software for Windows, Mac and Linux.

After you've successfully copied the files to the SD card, you can put it in your Raspberry Pi, plug in the monitor, keyboard, mouse and then the power. After a minute or so, it should boot to the Desktop.

raspberry_pi_01_steps_copy.jpg

raspberry_pi_02_steps_copy.jpg

raspberry_pi_03_steps_copy.jpg

The first thing you want to do is connect to your internet by clicking on the internet icon in the upper right and selecting your wireless network. Then inter your password and click "connect". Next you can right click on that same icon, and select "Wireless and Wired Network Settings". Here, you'll want to make sure wlan0 is selected and then give it a static IP address that matches the IP scheme of your network.

raspberry_pi_04_steps_copy.jpg

raspberry_pi_05_steps_copy.jpg

raspberry_pi_06_steps_copy.jpg

To finish up, click on the start menu in the upper left go to Preferences > Raspberry Pi Configuration. On the "Interfaces" tab, select to enable SSH. Then on the "Localisation" tab, set your keyboard language settings to match your country. Then reboot your machine. Now you should be able to log in from a remote computer using SSH. On Linux or Mac machines, you can do this by typing 

ssh pi@your_static_ip

and on Windows you can download Putty and use it to SSH into your Pi.

raspberry_pi_07_steps_copy.jpg

raspberry_pi_08_steps_copy.jpg

raspberry_pi_09_steps_copy.jpg

Installing the Camera

The camera I'm using is the Raspberry Pi Camera Module v2. I like it because it's very flat in shape and doesn't take up any USB ports. It has it's own dedicated IO port. So with the Pi off, insert the Camera Module (as seen below). Then power your PI back on.

raspberry_pi_12_steps.jpg

raspberry_pi_11_steps.jpg

raspberry_pi_10_steps.jpg

Before we can use the camera, we have to enable it. So after the Pi boots back up, you can SSH into it and run

sudo raspi-config
#Interfacing Options > Camera > Enable
sudo reboot

Once your Pi boots back up, you should be able to utilize the camera.

Adding A Microphone

Since the Raspberry Pi doesn't have audio input by default, we're gonna have to use a USB based audio input. I just got a cheap mini USB microphone and plugged it in.

raspberry_pi_14_steps.jpg

raspberry_pi_13_steps.jpg

The Pi should automatically detect it and make it useable. I'm going to be using the microphone in conjunction with the Alsa audio library, so we'll need to install that first. Then we can test out the microphone by making a simple 30 second recording (make sure to have some speakers or headphones plugged in in order to hear the playback).

sudo apt-get install libasound2-dev
arecord -D plughw:1 --duration=30 -f cd -vv ~/test.wav 
omxplayer -p -o hdmi ~/test.wav  

I'm using an Adafruit 2.8" Touchscreen LCD. It comes connected with header pins that make it easy to fit right on top of the Raspberry Pi GPIO Pins.

Adafruit has some great documentation about setting up the Raspberry Pi, and they even have their own Raspbian Image that you can download and install with everything already on it. Sadly, the pre-made image didn't work for me, which is why I installed a fresh version of Raspbian.

The LCD requires a Kernel in order for it to work, so I'll need to download it manually from the Adafruit website (these steps are also on their instruction page).

sudo apt-get update
curl -SLs https://apt.adafruit.com/add-pin | sudo bash
sudo apt-get install raspberrypi-bootloader 

Installing the bootloader will take a few minutes, but when it's done, you can shutdown your Pi, and unplug any other external monitors before turning it back on.

Once the Pi boots back up, we'll need to modify the Pi's boot config file and adjust it so that it displays to the LCD properly.

sudo nano /boot/config.txt

and then add this text to the bottom of it:

dtparam=i2c_arm=on 
dtparam=i2s=on 
dtparam=spi=on 
dtoverlay=pitft28-resistive,rotate=90,speed=32000000,fps=20 

In order to get anything to display on the LCD, we need to tell the operating system to use it as it's primary display. To do this, you can use these commands:

export FRAMEBUFFER=/dev/fb1 
startx 

Now we can test it by displaying an image on it using the fbi (not that F.B.I.) image viewer.

sudo apt-get install fbi
wget http://www.tinkernut.com/wp-content/uploads/2015/01/logo_gear_sm3.png -O image.png
sudo fbi -T 2 -d /dev/fb1 -noverbose -a image.png

The LCD part works! Now we need to test the touch screen. By default, the touchscreen should work with the Adafruit kernel. But there are a few commands you can run to help calibrate it.

sudo TSLIB_FBDEVICE=/dev/fb1 TSLIB_TSDEVICE=/dev/input/touchscreen ts_calibrate
sudo TSLIB_FBDEVICE=/dev/fb1 TSLIB_TSDEVICE=/dev/input/touchscreen ts_test

These commands will give you visual feedback on your screen that you can touch to calibrate your screen.

Fixing Issues With The Adafruit Touchscreen

If you are using the Adafruit PiTFT for this project, you may find that no matter how much you calibrate the LCD, it still won't work with your Pygame code. This is because there is an issue with the Adafruit touchscreens and the current version of Raspbbian.

The touchscreens work well with the Raspbian "Wheezy" distro, but they don't play well with Raspbian "Jessie". There is a workaround, however, to get it functional again. It involves downloading and installing the "Wheezy" driver.

#enable wheezy package sources
echo "deb http://archive.raspbian.org/raspbian wheezy main
" >> /etc/apt/sources.list.d/wheezy.list
#set stable as default package source (currently jessie)
echo "APT::Default-release \"stable\";
" >> /etc/apt/apt.conf.d/10defaultRelease
#set the priority for libsdl from wheezy higher then the jessie package
echo "Package: libsdl1.2debian
Pin: release n=jessie
Pin-Priority: -10
Package: libsdl1.2debian
Pin: release n=wheezy
Pin-Priority: 900
" >> /etc/apt/preferences.d/libsdl
#install
apt-get update
apt-get -y --force-yes install libsdl1.2debian/wheezy

Running the above code should fix the issue and the touchscreen will now work with Pygame. So let's go ahead and create our own test script that sets a background, creates a button, and checks for touch input to perform an action.

Below is some test code called pygame_test.py

import pygame 
import os 
from time import sleep 
import random 
os.environ['SDL_FBDEV']= '/dev/fb1' 
os.environ["SDL_MOUSEDEV"] = '/dev/input/touchscreen' 
os.environ['SDL_MOUSEDRV'] = 'TSLIB' 
pygame.init() 
lcd = pygame.display.set_mode((320,240)) 
lcd.fill((255,0,0)) 
def make_button(text, xpo, ypo, color): 
       font=pygame.font.Font(None,24) 
       label=font.render(str(text),1,(color)) 
       lcd.blit(label,(xpo,ypo)) 
       pygame.draw.rect(lcd, cream, (xpo-5,ypo-5,110,35),1) 
def random_color(): 
   rgbl=[255,0,0] 
   random.shuffle(rgbl) 
   return tuple(rgbl) 
blue = 26, 0, 255 
white = 255, 255, 255 
cream = 254, 255, 250 
lcd.fill(blue) 
pygame.mouse.set_visible(False) 
while 1: 
       make_button("Menu item 1", 20, 20, white) 
       for event in pygame.event.get(): 
               if (event.type == pygame.MOUSEBUTTONDOWN): 
                       print "screen pressed" 
                       lcd.fill(random_color()) 
                       pos = (pygame.mouse.get_pos()[0],pygame.mouse.get_pos()[1]) 
                       print pos 
       pygame.display.update() 

Although I'm using Youtube in this tutorial, the concept should also work with other streaming services like Twitch.tv and FacebookLive. Most of these streaming services use a protocol called Real-Time Messaging Protocol (RTMP). So in order to stream to Youtube, we'll need the RTMP URL as well as a private key for our specific stream.

To get your key and URL, go to your Youtube Dashboard and select "Live Streaming" and select either "Stream now" or "Events" and create a new event. Following those steps will allow you to generate a new RTMP URL and private key.

With our URL and key, we can now turn back to the Raspberry Pi to make a streaming program using Python. When it comes to streaming from a Raspberry Pi, there are actually several different methods. One of the easier ways is by installing avconv (part of the libav-tools package) and using raspivid to pipe the stream to the RTMP URL. Here's a sample of how that would work from the command line:

sudo apt-get install libav-tools 
raspivid -o - -t 0 -vf -hf -fps 30 -b 6000000 | avconv -re -ar 44100 -ac 2 -acodec pcm_s16le -f s16le -ac 2 -i /dev/zero -f h264 -i - -vcodec copy -acodec aac -ab 128k -g 50 -strict experimental -f flv rtmp://a.rtmp.youtube.com/live2/[your-secret-key-here] 

This method might work alright through the command line, but whenever I tried to incorporate it into python code, it wouldn't work. There seems to be an issue with avconv and Youtube.

The program that worked the best for me was FFMpeg. Most of you may know that avconv is a 99% compatible replacement for FFMpeg, but Youtube streaming seems to fall within that 1%. Those of you that have worked with FFMpeg on the Raspberry Pi before know how difficult (and how long) it can be to install. I clocked it at just slightly over an hour from beginning to end using a Raspberry Pi 3 using the steps below (based on these steps https://github.com/tgogos/rpi_ffmpeg).

sudo sh -c 'echo "deb http://www.deb-multimedia.org jessie main non-free" >> /etc/apt/sources.list.d/deb-multimedia.list' 
sudo sh -c 'echo "deb-src http://www.deb-multimedia.org jessie main non-free" >> /etc/apt/sources.list.d/deb-multimedia.list' 
sudo apt-get update  
sudo apt-get install deb-multimedia-keyring 
sudo apt-get update  
sudo apt-get install build-essential libmp3lame-dev libvorbis-dev libtheora-dev libspeex-dev yasm libopenjpeg-dev libx264-dev libogg-dev
cd ~
sudo git clone https://git.videolan.org/x264 
cd x264/ 
sudo ./configure --host=arm-unknown-linux-gnueabi --enable-static --disable-opencl 
sudo make -j4
sudo make install 
cd ~
sudo git clone https://github.com/FFmpeg/FFmpeg.git 
cd FFmpeg/ 
sudo ./configure --arch=armel --target-os=linux --enable-gpl --enable-libx264 --enable-nonfree 
sudo make -j4 
sudo make install

Now that you're back from a nice break after letting all this install, we can finally write a simple streaming script to test it out. Basically we can use the subprocess PIPE command to emulate entering the command in a terminal and then "pipe" the camera stream through FFMpeg to Youtube.

Getting the FFMpeg command just right is kinda tricky, but the one I used below seems to work well for me. The value that you might need to adjust is the itsoffset value. It's what helps sync the audio with the video. It basically "offsets" the video by a number in seconds. In my example, the offset is for 5.5 seconds. So if your audio and video or out of sync, you can try adjusting this number.

NOTE*** If you start getting a lot of "Alsa X Buffer" errors, then that means your itsoffest is probably too high. Adjusting it to lower will fix this issue.

Depending on the type of microphone your using, you may also need to change the hw value in the script. Mine is listed as card 1, so my hw value is 1,0 below. You can find what your audio device is listed as by typing the command:

arecord -l

This will list all of your recording devices and tell you what card number they're listed as.

Below is some sample code for streaming stream_test.py

#!/usr/bin/env python3 
import subprocess 
import picamera 
import time 
YOUTUBE="rtmp://a.rtmp.youtube.com/live2/" 
KEY= #ENTER PRIVATE KEY HERE# 
stream_cmd = 'ffmpeg -f h264 -r 25 -i - -itsoffset 5.5 -fflags nobuffer -f alsa -ac 1 -i hw:1,0 -vcodec copy -acodec aac -ac 1 -ar 8000 -ab 32k -map 0:0 -map 1:0 -strict experimental -f flv ' + YOUTUBE + KEY 
stream_pipe = subprocess.Popen(stream_cmd, shell=True, stdin=subprocess.PIPE) 
camera = picamera.PiCamera(resolution=(640, 480), framerate=25) 
try: 
  now = time.strftime("%Y-%m-%d-%H:%M:%S") 
  camera.framerate = 25 
  camera.vflip = True 
  camera.hflip = True 
  camera.start_recording(stream.stdin, format='h264', bitrate = 2000000) 
  while True: 
     camera.wait_recording(1) 
except KeyboardInterrupt: 
     camera.stop_recording() 
finally: 
  camera.close() 
  stream.stdin.close() 
  stream.wait() 
  print("Camera safely shut down") 
  print("Good bye") 

With the touchscreen, camera, and Youtube streaming all working, all we need now is a simple interface to control it. My vision is to have it so that you can preview the camera, using the touchscreen as a view finder, and then click a button to stream it to Youtube.

The vision I have is very similar to the Adafruit Pi Camera project. I like it because it uses the camera feed as the background of the touchscreen. It basically uses the io.BytesIO library to stream the buffer of camera images to use as background images on the touchscreen.

We want to have 3 buttons on the touchscreen:

  • Stream - Pressing this will stream the camera and audio to Youtube
  • Preview - Pressing this will show a preview of the camera on the LCD screen
  • Power - Pressing this will shutdown the Operating System

So combining that with our streaming code, and our touchscreen code, I came up with something like this:

Sample Python Code - youtube_stream.py

#!/usr/bin/env python 
import os 
import time 
import io 
import pygame 
import picamera 
import subprocess 
os.environ['SDL_VIDEODRIVER'] = 'fbcon' 
os.environ['SDL_FBDEV'] = '/dev/fb1' 
os.environ['SDL_MOUSEDEV'] = '/dev/input/touchscreen' 
os.environ['SDL_MOUSEDRV'] = 'TSLIB' 
pygame.init() 
lcd = pygame.display.set_mode((0,0), pygame.FULLSCREEN) 
pygame.mouse.set_visible(False) 
img_bg = pygame.image.load('/home/pi/camera_bg.jpg') 
preview_toggle = 0 
stream_toggle = 0 
blue = 26, 0, 255 
white = 255, 255, 255 
cream = 254, 255, 250 
YOUTUBE="rtmp://a.rtmp.youtube.com/live2/"		 
KEY= #ENTER PRIVATE KEY HERE#				 
stream_cmd = 'ffmpeg -f h264 -r 25 -i - -itsoffset 5.5 -fflags nobuffer -f alsa -ac 1 -i hw:1,0 -vcodec copy -acodec aac -ac 1 -ar 8000 -ab 32k -map 0:0 -map 1:0 -strict experimental -f flv ' + YOUTUBE + KEY
stream_pipe = subprocess.Popen(stream_cmd, shell=True, stdin=subprocess.PIPE) 
camera = picamera.PiCamera() 
camera.resolution = (1080, 720) 
camera.rotation   = 180	 
camera.crop       = (0.0, 0.0, 1.0, 1.0) 
camera.framerate  = 25 
rgb = bytearray(camera.resolution[0] * camera.resolution[1] * 3) 
def make_button(text, xpo, ypo, color): 
       font=pygame.font.Font(None,24)	 
       label=font.render(str(text),1,(color)) 
       lcd.blit(label,(xpo,ypo)) 
       pygame.draw.rect(lcd, cream, (xpo-5,ypo-5,150,35),1) 
def stream(): 
	camera.wait_recording(1) 
def shutdown_pi(): 
       os.system("sudo shutdown -h now") 
def preview(): 
	stream = io.BytesIO() 
	camera.vflip = True
	camera.hflip = True 
	camera.capture(stream, use_video_port=True, format='rgb', resize=(320, 240)) 
	stream.seek(0) 
	stream.readinto(rgb) 
	stream.close() 
	img = pygame.image.frombuffer(rgb[0:(320 * 240 * 3)], (320, 240), 'RGB') 
	lcd.blit(img, (0,0)) 
	make_button("STOP", 175,200, white) 
	pygame.display.update() 
try: 
	while True: 
		if stream_toggle == 1: 
			stream() 
		elif preview_toggle == 1: 
			preview() 
		else: 
			click_count = 0		 
			lcd.fill(blue) 
			lcd.blit(img_bg,(0,0)) 
			make_button("STREAM", 5, 200, white) 
			make_button("PREVIEW",175,200, white) 
                        make_button("POWER", 200, 5, white) 
			pygame.display.update() 
		for event in pygame.event.get(): 
			if (event.type == pygame.MOUSEBUTTONDOWN): 
				pos = pygame.mouse.get_pos() 
			if (event.type == pygame.MOUSEBUTTONUP): 
				pos = pygame.mouse.get_pos() 
				print pos 
				x,y = pos 
				if y > 100: 
					if x < 200: 
						print "stream pressed" 
						if stream_toggle == 0 and preview_toggle == 0: 
							stream_toggle = 1 
							lcd.fill(blue) 
							lcd.blit(img_bg,(0,0)) 
							make_button("STOP", 20, 200, white) 
							pygame.display.update() 
							camera.vflip=True 
							camera.hflip = True 
							camera.start_recording(stream_pipe.stdin, format='h264', bitrate = 2000000) 
						elif preview_toggle == 1: 
							preview_toggle = 0 
							lcd.fill(blue) 
							lcd.blit(img_bg,(0,0)) 
							make_button("STREAM", 5, 200, white) 
							make_button("PREVIEW",175,200, white) 
							pygame.display.update() 
						else: 
							stream_toggle = 0 
							lcd.fill(blue) 
							make_button("STREAM", 5, 200, white) 
							make_button("PREVIEW",175,200, white) 
							pygame.display.update() 
							camera.stop_recording() 
					elif x > 225: 
						print "preview pressed" 
						if preview_toggle == 0 and stream_toggle == 0: 
							preview_toggle = 1 
							lcd.fill(blue) 
							make_button("STOP", 175,200, white) 
							pygame.display.update() 
						elif stream_toggle == 1: 
							stream_toggle = 0 
							lcd.fill(blue) 
							make_button("STREAM", 5, 200, white) 
							make_button("PREVIEW",175,200, white) 
							pygame.display.update() 
							camera.stop_recording() 
						else: 
							preview_toggle = 0 
							lcd.fill(blue) 
							make_button("STREAM", 5, 200, white) 
							make_button("PREVIEW",175,200, white) 
							pygame.display.update() 
except KeyboardInterrupt: 
	camera.stop_recording() 
	print ' Exit Key Pressed' 
finally: 
	camera.close() 
	stream_pipe.stdin.close() 
	stream_pipe.wait() 
	print("Camera safely shut down") 
	print("Good bye")

Customize the script the way you want to make it work for you, and then test it out. Just run it using

sudo python youtube_stream.py

Then click on the "Preview" button to preview your camera. Press "Stop" to go back to the main menu. Then click the "Stream" button to start streaming to Youtube. Now you should be able to go to your Youtube Live dashboard and preview your live stream!

Once you're happy with the results, the last thing to do is to make the script executable and add it to the Pi's rc.local file so that it auto-launches whenever the Pi boots up

sudo chmod +x youtube_stream.py
sudo nano /etc/rc.local

Towards the bottom of the rc.local file, right before "exit 0", add this line:

sudo python /home/pi/youtube_stream.py &

Save it and reboot the Pi. Once it's through rebooting, you should see your new touchscreen interface!

Obviously we want to make this camera portable, so we're going to need a battery. I like to use "emergency cell phone chargers" because they can last for a while, they can be recharged, and a lot of them even have a built in USB cable that can plug directly into the Pi! However, you are still welcome to buy a Lithium Polymer battery and a charging board if you don't want to go that route. You can find the parts to do that listed in the "what you will need" section.

The only thing one of these battery banks doesn't have is an an/off switch. There are a couple ways to go about this: You could either solder a switch onto the battery bank yourself, or you could buy a premade micro-usb on/off switch, which is a much safer way to go than soldering near a lithium polymer battery.

This will take care of turning the power on and off, but it's generally not a good idea to cut the power to a Pi while it's running software. This can corrupt the software and/or the Pi itself. You eagle eyed code monkeys may have noticed that the code in the previous step generates a touch screen "Power" button that can shut down the Raspberry Pi OS.

So you can switch the USB cable "on" to power on the Pi, and then touch the "Power" touchscreen button to turn off the Raspberry Pi OS before switching the USB cable to "off".

The finishing touch for this project is 3D printing an epic case to contain everything! There are plenty of great CAD programs out there, but for simplicities sake, I'm using Tinkercad. You'll want to 3D print the enclosure using supports.

raspberry_pi_step04.jpg

raspberry_pi_step05.jpg

raspberry_pi_step01.jpg

raspberry_pi_step02.jpg

The LCD, Raspberry Pi and Camera should basically snap into place, as long as you're using the same parts as me. The LiPo battery will sit in between the camera and the Pi, but you won't be able to plug the battery directly into the USB port of the Pi because there isn't enough room in the case. As an alternative, we can strip the wires coming from the battery and solder them to the PP2 and PP5 ports on the back of the Raspberry Pi Micro USB port.

raspberry_pi_step06.jpg

raspberry_pi_step07.jpg

raspberry_pi_step09.jpg

I used hot glue to hold everything into place within the camera case, but DO NOT USE HOT GLUE DIRECTLY ON THE LIPO BATTERY!!! Once all the pieces are together, you can test it out. If it works, you can hot glue the two halves of the case together!

raspberry_pi_step10.jpg

raspberry_pi_step12.jpg

raspberry_pi_IMG_0593_sm.jpg

raspberry_pi_IMG_0597_sm.jpg

Finally, you can add a little bit of white paint to make it look more like a Youtube logo, and then start streaming!

This guide was first published on Sep 15, 2017. It was last updated on Mar 08, 2024.