In this tutorial, we are going to use Raspberry Pi camera to stream video to Android device via WIFI. The real-time video can be viewed by many Android devices simultaneously as long as they are connected to the same WIFI source.
You need a Raspberry Pi 4B + Raspberry Pi CSI camera module + Android smartphone.
Step 1: Insert the Pi CSI camera to Raspberry Pi 4B.
Step 2: Open "Raspberry Pi Configuration". Click "Interface" to enable Camera, SSH, I2C and SPI. Then reboot the Raspberry Pi.
Step 3: Find your own IP address by typing "hostname -I". You should see 192.168.xx.xxx
Step 4: Write python code in Thonny and save it as app.py . Focus on the yellow sentences carefully. Port 8000 is chosen.
import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server
PAGE="""\
<html>
<head>
<title>Raspberry Pi - Surveillance Camera</title>
</head>
<body>
<center><h1>Raspberry Pi </h1></center>
<center><img src="stream.mjpg" width="640" height="480"></center>
</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='640x480', framerate=24) as camera:
output = StreamingOutput()
#Uncomment the next line if you want to take a picture when the camera is activated
#camera.capture("picture.png")
#Uncomment the next line if you need to change the orientation of camera
#camera.rotation = 90
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8000)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()
Step 5: Write android app
The code is simple but you have to download and install Android Studio first.
After the android project is created, search if MainActivity.Java, activity_main.xml and AndroidManifest.xml exist.
Edit activity_main.xml as follows
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="30dp">
<ProgressBar
android:id="@+id/myProgressBar"
android:layout_weight="0.1"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/myImageView"
android:src="@mipmap/ic_launcher"
android:layout_weight="0.9"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<WebView
android:id="@+id/myWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Edit MainActivity.Java as follows. As port 8000 is chosen in step 4, you must write :8000 after your IP address: 192.168.xx.xxx
package com.example.browser;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageView;
import android.widget.ProgressBar;
public class MainActivity extends AppCompatActivity
{
ProgressBar superProgressBar;
ImageView superImageView;
WebView superWebView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
superProgressBar=findViewById(R.id.myProgressBar);
superImageView=findViewById(R.id.myImageView);
superWebView=findViewById(R.id.myWebView);
superProgressBar.setMax(100);
superWebView.loadUrl("http://192.168.xx.xxx:8000");
superWebView.getSettings().setJavaScriptEnabled(true);
superWebView.getSettings().setLoadWithOverviewMode(true);
superWebView.getSettings().setUseWideViewPort(true);
superWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
superWebView.setScrollbarFadingEnabled(true);
superWebView.setWebViewClient(new WebViewClient());
superWebView.setWebChromeClient(new WebChromeClient()
{
@Override
public void onProgressChanged(WebView view, int newProgress)
{
super.onProgressChanged(view, newProgress);
superProgressBar.setProgress(newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title)
{
super.onReceivedTitle(view, title);
getSupportActionBar().setTitle(title);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon)
{
super.onReceivedIcon(view, icon);
superImageView.setImageBitmap(icon);
}
});
}
@Override
public void onBackPressed()
{
if(superWebView.canGoBack())
{
superWebView.goBack();
}
else {
finish();
}
}
}
Edit AndroidManifest.xml as follows
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.browser">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Step 6: Build apk
Click "Build" and find "Generate Signed Bundle or APK". Click "Next"
Create key store and fill your information. Then click "Next"
Click "release" and install the apk file in your Android device.
Choose V2 if the Android device is running Android version 5.0 or above. Otherwise please choose V1. If your old Android device was upgraded to version 5.0 or above by yourself, you should choose V1 too.
Step 7: Run the python code and the Android app.
Let's stream multiple devices in parallel. The screen on L.H.S is Window device. The screen on R.H.S is Linux device. The mini screen at the middle is Android device.
*The above code are modified from open source platform
How would I change the code to work with a USB camera inside of a pi camera?
ReplyDelete