How to implement a WebRTC HTTP Playback Protocol (WHPP) client for native Android applications
Tags: webrtc, android, kotlin, whpp
Published: 2022-09-22 09:04:17

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](https://groups.google.com/g/discuss-webrtc/c/Ozvbd0p7Q1Y). 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
<?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"
...
```


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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jl24x2e047iby5qxbacw.png)

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
<?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>
```
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](https://www.npmjs.com/package/@eyevinn/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: 

```html
<!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>
```

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.

```kotlin
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
    }
}
```

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: 

```kotlin
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
    }
}
```

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

![WHPP client running on Android](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iyrwz07zn223lkrgyqvy.png)

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

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()](https://developer.android.com/reference/android/webkit/WebView#public-methods_1) 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](https://eyevinntechnology.medium.com/webrtc-based-distribution-with-whip-and-whpp-running-on-aws-fargate-b217e4c6e00) has you covered.

## Links
* WHPP client (TypeScripe/JavaScript) https://github.com/Eyevinn/whpp-client
* WebRTC HTTP Playback Protocol (WHPP): https://dev.to/video/webrtc-http-playback-protocol-whpp-p66
* WHPP protocol proposal: https://github.com/Eyevinn/webrtc-http-playback-protocol

## 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!