Skip to content

Getting started

Set up the SDK and make your first call. Takes about five minutes.

Requirements

  • Android 8.0 (API 26) or newer on the device
  • compileSdk 36 or higher, JDK 17
  • Kotlin 2.2 or newer (the SDK itself is built with 2.2.20)
  • Core library desugaring enabled, if you use net.bunny:player
  • A Bunny Stream video library, and its id (Bunny dashboard: Stream > your library)
  • The library's API key (Stream > your library > API), only if the app manages the library: lists, uploads, edits or broadcasts. Playback does not need it.

The last three are enforced by the build rather than documented politely. A compileSdk below 36 fails with checkAarMetadata; an older Kotlin cannot read the SDK's metadata and reports the binary version as incompatible - 2.1 works because Kotlin reads metadata one version ahead, 2.0 and below do not; and without desugaring the player's media3 dependency is rejected. Desugaring looks like this:

// build.gradle.kts
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}

1. Add the dependencies

The SDK ships on Maven Central as three artifacts. Pick what you need:

// build.gradle.kts
dependencies {
    implementation("net.bunny:api:latest.release")        // video and live stream management, uploads
    implementation("net.bunny:player:latest.release")     // video and live playback (pulls in :api)
    implementation("net.bunny:recording:latest.release")  // camera recording and go-live (pulls in :api)
}

Replace latest.release with a concrete version for reproducible builds.

2. Declare the INTERNET permission

The api and player artifacts do not declare it for you (the recording artifact does):

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />

Camera capture needs two more permissions, covered in Go live from the camera.

3. Initialize

Call initialize once, before anything else from the SDK. Application.onCreate is the usual place.

Apps that only play

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        BunnyStreamApi.initialize(context = this, libraryId = 12345L)
    }
}

That is all a player needs. Videos and live streams play through Bunny's public play endpoints, which take the library id, the video or stream id and, when the library has token authentication on, a signed token (see Secure playback). The management calls (listing, creating, editing, uploading, broadcasting) fail with BunnyError.InvalidState on such an instance, without reaching the network.

Apps that manage the library

BunnyStreamApi.initialize(
    context = this,
    accessKey = BuildConfig.BUNNY_API_KEY, // your library API key
    libraryId = 12345L,                    // your library id
)

Keep the API key out of source control. Read it from local.properties, an environment variable or your secrets tooling, and remember that a key baked into a shipped APK can be extracted. Ship it only in apps that need it, such as an internal publishing tool, and keep viewer-facing apps on the playback-only setup above.

This registers a default instance that the rest of the SDK reaches through getInstance(), and that the player and camera views fall back to when you do not give them one. A blank key or a library id of 0 is rejected here rather than failing later with a 401: pass null (or leave the key out) for playback only.

The most common integration mistake is skipping the call: the player renders a black view and logs an error instead of crashing, and the camera view refuses to start. See Troubleshooting.

More than one library

initialize is a convenience for the common case. Every instance owns its credentials, its HTTP client and its uploads, so you can hold as many as you have libraries:

val marketing = BunnyStreamApi.create(
    context = this,
    config = BunnyStreamConfig(accessKey = marketingKey, libraryId = 12345L),
)
val training = BunnyStreamApi.create(
    context = this,
    config = BunnyStreamConfig(accessKey = trainingKey, libraryId = 67890L),
)

Nothing is registered globally, so hold on to the handles. Point a view at one with its bunny property (BunnyStreamPlayer, BunnyStreamCameraUpload) or the bunny parameter (BunnyLiveStreamPlayer); leave it unset and the view uses the default instance. Call release() on an instance when you are done with it — that stops its in-flight uploads and leaves every other instance running.

4. Make a call

// List the videos in your library. Repository calls are suspend - call them from a coroutine.
val result = BunnyStreamApi.getInstance().videoRepository.listVideos(libraryId = 12345L)

result.fold(
    onOk = { page -> render(page.items) },
    onErr = { error -> showError(error.message) },
)

Every management call answers with a BunnyResult rather than throwing — see Handle errors.

Next steps

The demo app in app/ exercises every feature and is the quickest way to see working code.