DEV Community

Albin Larsson for Eyevinn Video Dev-Team Blog

Posted on

How to implement a WebRTC HTTP Playback Protocol (WHPP) client for native Android applications

The Android platform has no direct support for WebRTC. If you have searched for how to use WebRTC on Android you probably ended up on the WebRTC Android SDK page. This page is horribly outdated, with dead links and references to prebuilt libraries which were discontinued years ago. This SDK is also very hard to build and use, and very hard to find guides or documentation for.

Luckily this is not the only way. You can also use WebRTC from a WebView. This is supported on Android 5.0 and later, and is very performant and and easy to use. This might not be fully "Native" to some, but you'll be free to implement the rest of your UI and code (like the media buttons) outside of the WebView.

To start off, open your project in Android Studio, or create a new "Empty Activity" if you want to start from an empty project.

We need to give the app permissions. And since we are making a WHPP client, we only need the internet permission.

In app/manifests/AndroidManifest.xml, add the android.permission.INTERNET permission:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
...
Enter fullscreen mode Exit fullscreen mode

Now you need to add the WebView to your layout file available in app/res/layout/. It should be named something like activity_main.xml. If you open it, you will see a graphical editor.

Android Studio layout file graphical editor

If you want to use this editor, search the Palette (1) for "WebView" and drag it to the view. Then select and edit the Layout attributes under Attributes (2), setting the width and height to "match_parent". Also remember to set the id. For this example I will set it to playerWebView.

Alternatively you can switch to the code view (3), and add the WebView there. It should look something like this afterwards:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

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

</androidx.constraintlayout.widget.ConstraintLayout>
Enter fullscreen mode Exit fullscreen mode

Now we will create the html file for the web view.

If you don't have an "assets" folder (you won't have one if you just created the project), you can create one with File -> New -> Folder -> Assets Folder

This is where you need to create your whpp client. We will use the example implementation directly from this library, with some slight modifications. To make it easy, create just one html file "whpp.html" with this content:

<!DOCTYPE html>
<html>
<head>
    <title>WHPP client web example</title>
    <style> video { width: 100%; height: 100%; }
</style>
</head>
<body>
<video autoplay controls></video>
<script type="module">
      import { WHPPClient } from "https://cdn.jsdelivr.net/npm/@eyevinn/whpp-client@0.1.2/dist/whpp-client.m.js";
      window.addEventListener("DOMContentLoaded", async () => {
            const whppUrl = new URL("https://broadcaster.lab.sto.eyevinn.technology:8443/broadcaster/channel/sthlm");

            const video = document.querySelector("video");
            const peer = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] });

            peer.ontrack = (ev) => {
                if (ev.streams && ev.streams[0]) {
                    video.srcObject = ev.streams[0];
                }
            };
            const client = new WHPPClient(peer, whppUrl, { debug: true });
            await client.connect();
      });
    </script>
</body>
</html>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now there's only one more file to edit, and it's MainActivity.kt, located in app/java/[name-of-your-package]. I created a new method named setupWebView() that has all the code you need to load the html asset into the WebView.

package se.eyevinntechnology.android_whpp_player

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.webkit.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setupWebView()
    }

    private fun setupWebView() {
        val webView = findViewById<WebView>(R.id.playerWebView)
        webView.loadUrl("file:android_asset/whpp.html")
        webView.settings.javaScriptEnabled = true
        // This is only needed if you want to autoplay with audio without muting
        webView.settings.mediaPlaybackRequiresUserGesture = false
    }
}
Enter fullscreen mode Exit fullscreen mode

I would recommend to also add a bridge between the WebViews javascript console and the Andriod apps log, so you can get debug output. In the setupWebView() method just created, add:

webView.webChromeClient = object : WebChromeClient() {
    override fun onConsoleMessage(message: ConsoleMessage): Boolean {
        val priority = when (message.messageLevel().name) {
            "DEBUG" -> Log.DEBUG
            "LOG" -> Log.INFO
            "WARNING" -> Log.WARN
            "ERROR" -> Log.ERROR
            else -> Log.INFO
        }
        Log.println(priority, "Webview", "${message.message()} -- From line ${message.lineNumber()} of ${message.sourceId()}")
        return true
    }
}
Enter fullscreen mode Exit fullscreen mode

Now you should be able to run your app in the Device Manager/emulator or an actual device:

WHPP client running on Android

It's a landscape video stream displayed in portrait mode, but it's working. 🎉

If you run into issues with the emulator, try API 31 (Android 12) or higher. I found that on my OS, the older Android API builds had issues with WebRTC, while actual physical devices on the same API did not have those issues.

This is the basics to get it running. The WHPP protocol and javascript client makes it easy to set up. You will be wanting to refine it a little, saving the whpp-client as a local javascript file in the assets, handling the screen orientation properly, and removing the media buttons from the html video element and adding them to your Android view. For the latter you will probably want to use WebView.addJavascriptInterface() to add an interface you can call from the JavaScript code in the WebView. But that's outside of the scope of this tutorial.

If you want to see how we set up the server for this, so you can set up your own. Then this post has you covered.

Links

About Eyevinn Technology

Eyevinn Technology is an independent consultant firm specialized in video and streaming. Independent in a way that we are not commercially tied to any platform or technology vendor.

At Eyevinn, every software developer consultant has a dedicated budget reserved for open source development and contribution to the open source community. This give us room for innovation, team building and personal competence development. And also gives us as a company a way to contribute back to the open source community.

Want to know more about Eyevinn and how it is to work here. Contact us at work@eyevinn.se!

Latest comments (3)

Collapse
 
selvan profile image
morsetree

Android ExoPlayer handles video playback for other protocols like RTSP, RTMP, HLS, DASH. Looks like WHEP/WHPP would be an another good addition to ExoPlayer.

Collapse
 
selvan profile image
morsetree

Created a feature request for WHEP with ExoPlayer - github.com/google/ExoPlayer/issues...

Collapse
 
dhruvjoshi9 profile image
Dhruv Joshi

Nicely written and knowledgeable!