⚠️  Sunset Notice: This service will be discontinued as of September 30th, 2023.  Learn more »

Did you come here for Live Video Shopping?

This is documentation for Bambuser Live Streaming SDK.

If you're looking for documentation regarding Live Video Shopping (opens new window) , see these pages (opens new window)

How to create a live broadcasting app in Kotlin using Android Studio

This guide is based on Android Studio 3.4.1.

This guide focuses on the bare minimum required to start broadcasting. We're using Android Studio, which you can find on developer.android.com/studio (opens new window). Make sure to familiarize yourself with the example apps in the SDK bundles for a more complete overview.

Create a new Application

  • Open Android Studio
  • Choose File -> New -> New project...
  • Choose the Empty Activity template.
  • Enter a suitable application name and your company domain.
  • Set the Minimum SDK to at least API 21: Android 5.0, which is the oldest API supported by the Broadcaster (opens new window).
  • Choose Kotlin as programming language (See our Java guide if you prefer Java)

Add the broadcast SDK

  • Log in to the Bambuser site and download the latest SDK bundle for Android from the Developer (opens new window) page.

    • Open the downloaded zip file and extract it.
  • In Android Studio, in the project tree on the left-hand-side, right click on your app module and choose Open Module Settings.

  • Click the + button in the modules drawer

  • Choose Import .JAR/.AAR Package.
  • Navigate to the SDK files you extracted and import the .aar file.

  • Optionally remove the version number in the suggested subproject name in case you upgrade the module in place later, etc. Below we will assume you used libbambuser

  • You may now need to close and reopen the Project Structure screen, before the library module shows up in the list. Some recent versions of Android Studio fail to add new modules to the settings.gradle file. If this happens, add include ':libbambuser' manually at the end of the file and sync the project, then reopen the Project Structure screen.

  • Once more, in the project tree on the left-hand-side, right click on your app module and choose Open Module Settings again.

  • Choose Dependencies on the lefthand side, keep the app module selected in the second column, click the + button in the third column and choose Module Dependency.

  • In the second popup window, check libbambuser and click OK.

  • Click OK again to return from the Project Structure screen.

Configure Gradle to build for supported architectures

The Bambuser SDK contains native code built for the armeabi-v7a, arm64-v8a, x86 and x86_64 ABIs (opens new window). If you don't want to bundle all of them, or if other libraries contain native code for other architectures, an ABI filter is needed to ensure that the generated APK contains the greatest common denominator.

The armeabi-v7a and arm64-v8a ABIs (opens new window) are compatible with all modern devices and ARM emulator images. The x86 and x86_64 ABIs are in practice only necessary when developing on x86 emulator images, as real x86 devices can translate ARM machine code.

  • Open the build.gradle file for the app module.

  • Add an NDK ABI filter to the android defaultConfig block. For example:

android {
    defaultConfig {
        // ...
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
    }
}

Add required Android app permissions and features

The Bambuser broadcasting library for Android requires at least the following permissions for basic functionality: CAMERA (opens new window), RECORD_AUDIO (opens new window) and INTERNET (opens new window). We also recommend adding the ACCESS_NETWORK_STATE (opens new window) and WAKE_LOCK (opens new window) permissions at this point.

Additionally, to achieve suitable filtering (opens new window) of your app in the Google Play store, you should declare <uses-feature /> tags relevant for apps that rely on the camera.

  • Open the manifests/AndroidManifest.xml file in the Project tree on the left.

  • Add the following tags for permissions and features, before the <application /> tag:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-feature android:name="android.hardware.camera.any" android:required="true" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />

Since Android 6.0, the above is not enough. Certain permissions must be approved (opens new window) by the end-user at runtime. In the generated MainActivity.kt, check for and request any missing permissions:

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity

// ...

class MainActivity : AppCompatActivity() {
    public override fun onResume() {
        super.onResume()
        if (!hasPermission(Manifest.permission.CAMERA) && !hasPermission(Manifest.permission.RECORD_AUDIO))
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), 1)
        else if (!hasPermission(Manifest.permission.RECORD_AUDIO))
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), 1)
        else if (!hasPermission(Manifest.permission.CAMERA))
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
    }

    // ...

    private fun hasPermission(permission: String): Boolean {
        return ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
    }
}

This is enough for our minimal example. In practice, you should also at least implement onRequestPermissionsResult() in case the user rejects any of the requested permissions.

Add the viewfinder

In res/layout/activity_main.xml, replace the auto-generated TextView with a SurfaceViewWithAutoAR (opens new window).

<com.bambuser.broadcaster.SurfaceViewWithAutoAR
    android:id="@+id/previewSurface"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

Android Studio presents a visualization of the component tree by default. By switching from the Design tab to the Text tab near the bottom, you can access the XML representation and paste the snippet above.

Authentication

To be able to broadcast, your app needs to identify itself to Bambuser. Head over to the Developer (opens new window) page on the Bambuser site again and get the Sandbox applicationId, which we'll use when constructing the Broadcaster (opens new window).

WARNING

Remember to replace the Sandbox id with a Production applicationId before you release your app!

Bootstrap the SDK

Prepare MainActivity.kt for integration of the Broadcaster (opens new window) by importing the related classes and adding an implementation of the Broadcaster.Observer (opens new window) interface:

import android.util.Log
// ...
import com.bambuser.broadcaster.BroadcastStatus;
import com.bambuser.broadcaster.Broadcaster;
import com.bambuser.broadcaster.CameraError;
import com.bambuser.broadcaster.ConnectionError;
// ...
class MainActivity : AppCompatActivity() {
    // ...
    private val broadcasterObserver = object : Broadcaster.Observer {
        override fun onConnectionStatusChange(broadcastStatus: BroadcastStatus) {
            Log.i("Mybroadcastingapp", "Received status change: $broadcastStatus")
        }
        override fun onStreamHealthUpdate(i: Int) {}
        override fun onConnectionError(connectionError: ConnectionError, s: String?) {
            Log.i("Mybroadcastingapp","Received connection error: $connectionError, $s")
        }
        override fun onCameraError(cameraError: CameraError) {
            Log.i("Mybroadcastingapp","Received camera error: $cameraError")
        }
        override fun onChatMessage(s: String) {
            Log.i("Mybroadcastingapp","Received chat messsage: $s")
        }
        override fun onResolutionsScanned() {}
        override fun onCameraPreviewStateChanged() {}
        override fun onBroadcastInfoAvailable(s: String, s1: String) {
            Log.i("Mybroadcastingapp","Received broadcast info: $s, $s1")
        }
        override fun onBroadcastIdAvailable(id: String) {
            Log.i("Mybroadcastingapp","Received broadcast id: $id")
        }
    }
    // ...
}

In the main activity, lazily create an instance of the Broadcaster (opens new window) class. provide a reference to the activity as well as the applicationId. Also forward the Activity (opens new window) lifecycle events necessary for init and release of the camera.

Also import the viewfinder we set up previously, and attach it in onResume().

import kotlinx.android.synthetic.main.activity_main.previewSurface
// ...
class MainActivity : AppCompatActivity() {
    // ...
    private val APPLICATION_ID = "PLEASE INSERT YOUR APPLICATION SPECIFIC ID PROVIDED BY BAMBUSER"

    private val broadcaster by lazy {
        Broadcaster(this, APPLICATION_ID, broadcasterObserver)
    }

    override fun onPause() {
        super.onPause()
        broadcaster.onActivityPause()
    }

    public override fun onResume() {
        super.onResume()
        // ... permission checks, see above
        broadcaster.setCameraSurface(previewSurface)
        broadcaster.onActivityResume()
    }

    override fun onDestroy() {
        super.onDestroy()
        broadcaster.onActivityDestroy()
    }
}

Screen rotation and keep-awake

To rotate the camera preview and live video according to the rotation of the device, forward the display rotation to the Broadcaster (opens new window) after creation and in onResume().

broadcaster.setRotation(this.windowManager.defaultDisplay.rotation)

To prevent the screen from going to sleep, set FLAG_KEEP_SCREEN_ON (opens new window) when a broadcast starts and clear it when the broadcast ends.

Import the WindowManager (opens new window) class where the flag is found, then add an implementation to the empty onConnectionStatusChange(BroadcastStatus broadcastStatus) callback in the Broadcaster.Observer (opens new window) you created earlier:

import android.view.WindowManager;
// ...
class MainActivity : AppCompatActivity() {
    // ...
    private val activity = this

    private val broadcasterObserver = object : Broadcaster.Observer {
        override fun onConnectionStatusChange(broadcastStatus: BroadcastStatus) {
            Log.i("Mybroadcastingapp", "Received status change: $broadcastStatus")
            if (broadcastStatus == BroadcastStatus.STARTING) {
                activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
            }
            if (broadcastStatus == BroadcastStatus.IDLE) {
                activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
            }
        }
        // ...

Add a broadcast button

Add a simple Button (opens new window) in the activity_main.xml layout:

<Button android:id="@+id/BroadcastButton"
    android:text="Broadcast"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

...and make it respond to clicks in MainActivity.kt:

import kotlinx.android.synthetic.main.activity_main.broadcastButton
// ...
class MainActivity : AppCompatActivity() {
    // ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        broadcastButton.setOnClickListener {
            if (broadcaster.canStartBroadcasting()) {
                broadcaster.startBroadcast()
            } else {
                broadcaster.stopBroadcast()
            }
        }
    }
// ...

Update the button label depending on broadcast state:


override fun onConnectionStatusChange(broadcastStatus: BroadcastStatus) {
    // ...
    broadcastButton.setText(if (broadcastStatus == BroadcastStatus.IDLE) "Broadcast" else "Disconnect")
}

Running the app

Connect your mobile device to your PC and follow the Android Developers guide for running your app (opens new window).

If everything is set up correctly, you should be able to start your first broadcast from your app by tapping the button we added earlier.

Check the Content page (opens new window) when logged in on the Bambuser site to view the broadcast when you go live in Sandbox mode.

What's next