Streaming Raspberry Camera Feed through Android WebView [All codes]

Streaming Raspberry Camera Feed through Android WebView [All codes]

I was cracking my brain on how to stream camera feed from my raspberry pi through an Android app. It was quite easy with Android application developed with ionic framework but when it came down to using native Android Java, it surely wasn’t an easy one because I was trying to avoid all third party libraries.

See also:

So in this post, we are going to stream a camera feed from Raspberry Pi camera through an Android app developed with Java programming language. It sounds fairly easy but it was not. Lets get down to business, below are the list of materials we will be needing to accomplish this task:

  •  PC or laptop
  • Raspberry Pi 3
  • Raspberry Pi Camera
  • Android phone for debugging and testing
  • wireless connection or any means of connecting everything together

 

Setting up your Raspberry Pi

Our previous post on setting up raspberry pi in headless mode will help you through:

  • Start up the Pi.
  • Open the Raspberry Pi Configuration Tool from the main menu or by typing sudo raspi-config in terminal(ctrl+alt+t).
  • Enable Camera and I2C
  • Install Flask using:
sudo apt-get install python3-flask

Flask is a micro framework for Python.Flask depends on two external libraries, Jinja2 template engine and the Werkzeug WSGI toolkit.

 

  • Create a new folder named camserver in your /home/pi/
  • Create templates folder in your /home/pi/camserver folder which will contain your html files
  • Create two python scripts: appCam.py and camera_pi.py from 
  • Copy and paste these lines of codes inside your camera_pi.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  camera_pi.py
#  
#  
#  
import time
import io
import threading
import picamera


class Camera(object):
    thread = None  # background thread that reads frames from camera
    frame = None  # current frame is stored here by background thread
    last_access = 0  # time of last client access to the camera

    def initialize(self):
        if Camera.thread is None:
            # start background frame thread
            Camera.thread = threading.Thread(target=self._thread)
            Camera.thread.start()

            # wait until frames start to be available
            while self.frame is None:
                time.sleep(0)

    def get_frame(self):
        Camera.last_access = time.time()
        self.initialize()
        return self.frame

    @classmethod
    def _thread(cls):
        with picamera.PiCamera() as camera:
            # camera setup
            camera.resolution = (320, 240)
            camera.hflip = True
            camera.vflip = False

            # let camera warm up
            camera.start_preview()
            time.sleep(2)

            stream = io.BytesIO()
            for foo in camera.capture_continuous(stream, 'jpeg',
                                                 use_video_port=True):
                # store frame
                stream.seek(0)
                cls.frame = stream.read()

                # reset stream for next frame
                stream.seek(0)
                stream.truncate()

                # if there hasn't been any clients asking for frames in
                # the last 10 seconds stop the thread
                if time.time() - cls.last_access > 10:
                    break
        cls.thread = None

 

  • copy and paste these lines of codes inside your appCam.py:

 

 

from flask import Flask, render_template, Response
# Raspberry Pi camera module (requires picamera package, developed by Miguel Grinberg)
from camera_pi import Camera
app = Flask(__name__)
@app.route('/')
def index():
    """Video streaming home page."""
    return render_template('index.html')
def gen(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.route('/video_feed')
def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(Camera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/view')
def androidview():
    """Video streaming android home page."""
    return render_template('html5video.html')
if __name__ == '__main__':
    app.run(host='0.0.0.0', port =80, debug=True, threaded=True)

 

  • Inside your templates folder, create two html files: index.html and html5video.html. Open your index.html and paste this lines of codes:
<html>
  <head>
    
    <link rel="stylesheet" href='../static/style.css'/>
  </head>
  <body>
    
    <h3><img src="{{ url_for('video_feed') }}" width="90%"></h3>
    <hr>
    <p> Credit to MJRoBot.org</p>
  </body>
</html>

 

  • Open your html5video.html and paste these lines of codes from

    , I had to modify it abit to suit my case:

<!DOCTYPE HTML>
<html>
	<head>
		<meta http-equiv="content-type" content="text/html; charset=">
		<title></title>
	</head>
	<body>
		<img id="video" autobuffer controls height="100%" width="100%" src="http://192.168.43.241/video_feed">

	

		<div id="msg"></div>

<script type="text/javascript">
	// array of the events we want to track
	var events=new Array("abort","canplay","canplaythrough","durationchange","emptied","ended","error","loadeddata","loadedmetadata","loadstart","pause","play","playing","progress","ratechange","seeked","seeking","stalled","suspend","timeupdate","volumechange","waiting");
	var vid=document.getElementById('video');
	var msg = document.getElementById('msg');
	// add event listeners to the video
	for (var i in events) {
		vid.addEventListener(events[i], showEvent, false);
	}
function showEvent(e) {
	var addMsg = "";
	if (e.type == "durationchange") {
		addMsg = e.type + "[" + vid.duration + "]";
	} else if (e.type == "seeked") {
		addMsg = e.type + "[" + vid.currentTime + "]";
	} else if (e.type == "timeupdate") {
		// do nothing as there are a lot of these
	} else if (e.type == "volumechange") {
		addMsg = "volume " + (vid.muted ? "muted" : vid.volume);
	} else {
		addMsg = e.type;
	}
	if (addMsg != "") {
		msg.innerHTML = addMsg + ((msg.innerHTML == "") ? "":", ") + msg.innerHTML;
	}
}
</script>

	</body>
</html>

 

  • Start up the server by running this command from the camserver folder: sudo python appCam.py. Open your browser, both the addresses http://192.168.43.241/video_feed and  http://192.168.43.241/view will output a camera feed on the browser.

    The application has two routes. The / route serves the main page, which is defined in the index.html

    The /video_feed route returns the streaming response. Because this stream returns the images that are to be displayed in the web page, the URL to this route is in the src attribute of the image tag. The browser will automatically keep the image element updated by displaying the stream of JPEG images in it.

    Lets move over to the android part .

  • Go to any browser in your network and enter with http://your-pi-ip-address (for example, in my case: 192.168.43.241)NOTE: If you are not sure about your RPi Ip address, run on your terminal:
    ifconfig

    at wlan0: section you will find it.

 

Setting up the Android part

  • Create a new Android Java application/Project either with Android studio or intellij IDE
  • Modify your activity_main.xml file to these:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.lsteinacoz.projectgarden.MainActivity">


    <WebView
                    android:layout_height="match_parent"
                    android:id="@+id/webView" android:layout_width="match_parent"/>

</RelativeLayout>

 

  • Modify your MainActivity.java to these:
package com.lsteinacoz.projectgarden;

import android.app.Activity;
import android.net.Uri;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;


import java.util.Arrays;

public class MainActivity extends Activity  {

    private WebView webSurface;
    String vidAddress = "http://192.168.43.241/view"; //take note of dis line




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);



        webSurface = (WebView) findViewById(R.id.webView);
        webSurface.setWebChromeClient(new WebChromeClient());
        webSurface.getSettings().setPluginState(WebSettings.PluginState.ON_DEMAND);
        webSurface.getSettings().setJavaScriptEnabled(true);
        webSurface.loadUrl(vidAddress);

       }


}





 

Take note of this line:

String vidAddress = "http://192.168.43.241/view"; //take note of dis line

 

We are rendering the  html5video.html from our python server on the webview. Android webview is not friendly at all with bare videos so we have to load it fro a  html file. You can now upload to your device and check it out.

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *