Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The following RTVI transports are available in this repository:
Add the following dependency to your `build.gradle` file:

```
implementation "ai.pipecat:daily-transport:1.0.3"
implementation "ai.pipecat:daily-transport:1.1.0"
```

Instantiate from your code:
Expand All @@ -32,7 +32,7 @@ val options = PipecatClientOptions(callbacks = callbacks)

val client: PipecatClientDaily = PipecatClient(DailyTransport(context), options)

client.startBotAndConnect(startBotParams).withCallback {
client.startBotAndConnect(apiRequest).withCallback {
// ...
}
```
Expand Down Expand Up @@ -125,23 +125,26 @@ client.connect().withCallback {
Add the following dependency to your `build.gradle` file:

```
implementation "ai.pipecat:small-webrtc-transport:0.3.7"
implementation "ai.pipecat:small-webrtc-transport:1.1.0"
```

Instantiate from your code:

```kotlin
val options = RTVIClientOptions(
params = RTVIClientParams(baseUrl = null),
enableMic = true,
enableCam = true
)
val callbacks = object : PipecatEventCallbacks() {

val connectionUrl = "http://localhost:7860/api/offer"
override fun onBackendError(message: String) {
Log.e(TAG, "Error from backend: $message")
}

val client = RTVIClient(SmallWebRTCTransport.Factory(context, connectionUrl), callbacks, options)
// ...
}

client.connect().withCallback {
val options = PipecatClientOptions(callbacks = callbacks)

val client: PipecatClientSmallWebRTC = PipecatClient(SmallWebRTCTransport(context), options)

client.startBotAndConnect(apiRequest).withCallback {
// ...
}
```
```
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dokka = "1.9.20"
androidxTest = "1.6.1"
ktor = "2.3.5"
okhttp = "4.12.0"
pipecatClient = "1.0.2"
pipecatClient = "1.1.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand Down
2 changes: 1 addition & 1 deletion pipecat-client-android-daily/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
val libraryVersion = "1.0.3"
val libraryVersion = "1.1.0"

plugins {
alias(libs.plugins.android.library)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ai.pipecat.client.transport.MsgClientToServer
import ai.pipecat.client.transport.MsgServerToClient
import ai.pipecat.client.transport.Transport
import ai.pipecat.client.transport.TransportContext
import ai.pipecat.client.types.APIRequest
import ai.pipecat.client.types.MediaDeviceId
import ai.pipecat.client.types.MediaDeviceInfo
import ai.pipecat.client.types.Participant
Expand Down Expand Up @@ -238,7 +239,10 @@ class DailyTransport(
thread = ctx.thread
}

override fun deserializeConnectParams(json: String): DailyTransportConnectParams {
override fun deserializeConnectParams(
json: String,
startBotRequest: APIRequest
): DailyTransportConnectParams {
val authBundle: DailyTransportAuthBundle = JSON_INSTANCE.decodeFromString(json)

val room = authBundle.actualRoom() ?: throw Exception("dailyRoom not set")
Expand Down
20 changes: 13 additions & 7 deletions pipecat-client-android-small-webrtc-transport/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
val libraryVersion = "1.1.0"

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
Expand All @@ -9,11 +11,12 @@ plugins {

android {
namespace = "ai.pipecat.client.small_webrtc_transport"
compileSdk = 34
compileSdk = 35

defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "VERSION_NAME", "\"$libraryVersion\"")
}

buildTypes {
Expand All @@ -27,16 +30,20 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

lint {
targetSdk = 35
}

kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}

buildFeatures {
buildConfig = true
}

sourceSets {
Expand All @@ -56,8 +63,7 @@ dependencies {
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)

// Temporary override until transport is updated to 1.0.0
api("ai.pipecat:client:0.3.4")
api(libs.pipecat.client)

androidTestImplementation(libs.androidx.runner)
androidTestImplementation(libs.androidx.rules)
Expand All @@ -76,7 +82,7 @@ publishing {
register<MavenPublication>("release") {
groupId = "ai.pipecat"
artifactId = "small-webrtc-transport"
version = "0.3.7"
version = libraryVersion

pom {
name.set("Small WebRTC Transport")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ai.pipecat.client.small_webrtc_transport

import ai.pipecat.client.types.Value
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

Expand All @@ -10,5 +11,7 @@ internal data class OfferRequestBody(
@SerialName("pc_id")
val pcId: String?,
@SerialName("restart_pc")
val restartPc: Boolean
val restartPc: Boolean,
@SerialName("request_data")
val requestData: Value
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ai.pipecat.client.small_webrtc_transport

import ai.pipecat.client.PipecatClient

typealias PipecatClientSmallWebRTC = PipecatClient<SmallWebRTCTransport, SmallWebRTCTransportConnectParams>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ai.pipecat.client.small_webrtc_transport

import ai.pipecat.client.types.Value
import kotlinx.serialization.Serializable

@Serializable
internal data class SmallWebRTCStartBotResult(
val sessionId: String,
val iceConfig: Value = Value.Null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This iceConfig maybe it would be nice to create a specific type for it. For example this is the one for Web:

export type IceConfig = {
  iceServers?: RTCIceServer[];
};

Where RTCIceServer is this: https://udn.realityripple.com/docs/Web/API/RTCIceServer

)
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import ai.pipecat.client.result.RTVIError
import ai.pipecat.client.result.resolvedPromiseErr
import ai.pipecat.client.result.resolvedPromiseOk
import ai.pipecat.client.result.withPromise
import ai.pipecat.client.transport.AuthBundle
import ai.pipecat.client.transport.MsgClientToServer
import ai.pipecat.client.transport.MsgServerToClient
import ai.pipecat.client.transport.Transport
import ai.pipecat.client.transport.TransportContext
import ai.pipecat.client.transport.TransportFactory
import ai.pipecat.client.types.APIRequest
import ai.pipecat.client.types.MediaDeviceId
import ai.pipecat.client.types.MediaDeviceInfo
import ai.pipecat.client.types.Participant
import ai.pipecat.client.types.ParticipantId
import ai.pipecat.client.types.TransportState
import ai.pipecat.client.types.Value
import ai.pipecat.client.utils.ThreadRef
import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioManager
Expand All @@ -37,11 +38,9 @@ private val LOCAL_PARTICIPANT = Participant(
local = true
)

class SmallWebRTCTransport internal constructor(
private val transportContext: TransportContext,
androidContext: Context,
private val serverUrl: String,
) : Transport() {
class SmallWebRTCTransport(
context: Context,
) : Transport<SmallWebRTCTransportConnectParams>() {

companion object {
private const val TAG = "SmallWebRTCTransport"
Expand All @@ -64,30 +63,30 @@ class SmallWebRTCTransport internal constructor(
val Rear = MediaDeviceInfo(id = MediaDeviceId("cam-rear"), name = "Rear Camera")
}

class Factory(
private val androidContext: Context,
private val serverUrl: String
) : TransportFactory {
override fun createTransport(context: TransportContext): Transport {
return SmallWebRTCTransport(context, androidContext, serverUrl)
}
}
private lateinit var transportContext: TransportContext
private lateinit var thread: ThreadRef

private var state = TransportState.Disconnected

private val appContext = androidContext.applicationContext
private val thread = transportContext.thread
private val appContext = context.applicationContext

private var client: WebRTCClient? = null
private var selectedCam = CameraMode.Front

override fun initialize(ctx: TransportContext) {
transportContext = ctx
thread = ctx.thread
}

override fun initDevices(): Future<Unit, RTVIError> = resolvedPromiseOk(thread, Unit)

@SuppressLint("MissingPermission")
override fun connect(authBundle: AuthBundle?): Future<Unit, RTVIError> =
override fun connect(
transportParams: SmallWebRTCTransportConnectParams
): Future<Unit, RTVIError> =
thread.runOnThreadReturningFuture {

Log.i(TAG, "connect(${authBundle})")
Log.i(TAG, "connect(${transportParams})")

if (client != null) {
return@runOnThreadReturningFuture resolvedPromiseErr(
Expand All @@ -114,7 +113,7 @@ class SmallWebRTCTransport internal constructor(
disconnect()
}

"renegotiate" -> negotiate()
"renegotiate" -> negotiate(transportParams)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any reason this should use connectParams instead? i.e. is it possible for the connectParams to change between now and when a renegotiation occurs and therefore should use the updated params?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like there's any way for this to change in the meantime -- the type is immutable and we don't change the connectParams field. If nothing else using transportParams avoids an unnecessary "non-null" assertion.

}

} else {
Expand All @@ -139,7 +138,8 @@ class SmallWebRTCTransport internal constructor(
} else {
null
},
initialMicEnabled = transportContext.options.enableMic
initialMicEnabled = transportContext.options.enableMic,
rtviProtocolVersion = transportContext.protocolVersion
)
} catch (e: Exception) {
return@runOnThreadReturningFuture resolvedPromiseErr(
Expand All @@ -148,18 +148,17 @@ class SmallWebRTCTransport internal constructor(
)
}

negotiate()
negotiate(transportParams)
}

private fun negotiate() = withPromise<Unit, RTVIError>(thread) { promise ->
private fun negotiate(
connectParams: SmallWebRTCTransportConnectParams
) = withPromise<Unit, RTVIError>(thread) { promise ->

MainScope().launch {

try {
client?.negotiateConnection(
url = serverUrl,
restartPc = false
)
client?.negotiateConnection(connectParams)

val cb = transportContext.callbacks
setState(TransportState.Connected)
Expand All @@ -174,6 +173,24 @@ class SmallWebRTCTransport internal constructor(
}
}

override fun deserializeConnectParams(
json: String,
startBotRequest: APIRequest
): SmallWebRTCTransportConnectParams {
val startBotResult = JSON_INSTANCE.decodeFromString<SmallWebRTCStartBotResult>(json)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should also add a check to make sure the json isn't already of type SmallWebRTCTransportConnectParams.


return SmallWebRTCTransportConnectParams(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SmallWebRTCTransportConnectionParams should also be able to receive the iceConfig from the startBotResult. So we can use it when connecting.

webrtcRequestParams = APIRequest(
endpoint = startBotRequest.endpoint.replace(
"/start",
"/sessions/${startBotResult.sessionId}/api/offer"
),
requestData = Value.Object(),
headers = startBotRequest.headers
)
)
}

override fun disconnect(): Future<Unit, RTVIError> = thread.runOnThreadReturningFuture {
withPromise(thread) { promise ->

Expand All @@ -185,6 +202,7 @@ class SmallWebRTCTransport internal constructor(
if (clientRef != null) {
clientRef.dispose()
setState(TransportState.Disconnected)
transportContext.onConnectionEnd()
transportContext.callbacks.onDisconnected()
}
promise.resolveOk(Unit)
Expand Down Expand Up @@ -256,8 +274,6 @@ class SmallWebRTCTransport internal constructor(
override fun enableMic(enable: Boolean): Future<Unit, RTVIError> = client?.setMicEnabled(enable)
?: resolvedPromiseErr(thread, RTVIError.TransportNotInitialized)

override fun expiry() = null

override fun enableCam(enable: Boolean): Future<Unit, RTVIError> =
client?.setCamMode(if (enable) selectedCam else null)
?: resolvedPromiseErr(thread, RTVIError.TransportNotInitialized)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ai.pipecat.client.small_webrtc_transport

import ai.pipecat.client.types.APIRequest

data class SmallWebRTCTransportConnectParams(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add the iceConfig here.

val webrtcRequestParams: APIRequest
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package ai.pipecat.client.small_webrtc_transport

val SMALL_WEBRTC_TRANSPORT_VERSION: String = ai.pipecat.client.small_webrtc_transport.BuildConfig.VERSION_NAME
Loading
Loading