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
compileSdk36 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.