Committed by
GitHub
Updates for 2.0.0 (#353)
* Prefix webrtc to avoid collisions with other libraries * Prepare for v2.0.0 * Move test webrtc files as well * Revert java target to Java 1.8 * Remove LiveKit.connect method * Rename participant's track variables to trackPublication to accurately reflect type * update protocol submodule * Change remoteParticipants to identity keys Also add in the Participant Sid/Identity value classes to prevent mistakes * change publishData to take in Identity type * Move to android-prefixed webrtc library * Make inline value classes serializable * Fix publishdata send to identity destination * Support sync stream id * Remap LivekitModels.VideoQuality to VideoQuality enum The new VideoQuality enum exposes only the ones that should be used by consumers * Suspend function for getSid * spotless * fix compile * Add documentation and internal visibility for internal classes
正在显示
100 个修改的文件
包含
933 行增加
和
683 行删除
| @@ -57,7 +57,11 @@ subprojects { | @@ -57,7 +57,11 @@ subprojects { | ||
| 57 | } | 57 | } |
| 58 | kotlin { | 58 | kotlin { |
| 59 | target("src/*/java/**/*.kt") | 59 | target("src/*/java/**/*.kt") |
| 60 | - targetExclude("src/*/java/**/ReentrantMutex.kt") // Different license | 60 | + targetExclude( |
| 61 | + "src/*/java/**/ReentrantMutex.kt", // Different license | ||
| 62 | + "src/*/java/**/TextureViewRenderer.kt", // Different license | ||
| 63 | + "src/*/java/**/FlowDelegate.kt", // Different license | ||
| 64 | + ) | ||
| 61 | ktlint("0.50.0") | 65 | ktlint("0.50.0") |
| 62 | .setEditorConfigPath("$rootDir/.editorconfig") | 66 | .setEditorConfigPath("$rootDir/.editorconfig") |
| 63 | licenseHeaderFile(rootProject.file("LicenseHeaderFile.txt")) | 67 | licenseHeaderFile(rootProject.file("LicenseHeaderFile.txt")) |
| @@ -6,66 +6,66 @@ ext { | @@ -6,66 +6,66 @@ ext { | ||
| 6 | java_version = JavaVersion.VERSION_1_8 | 6 | java_version = JavaVersion.VERSION_1_8 |
| 7 | dokka_version = '1.5.0' | 7 | dokka_version = '1.5.0' |
| 8 | androidSdk = [ | 8 | androidSdk = [ |
| 9 | - compileVersion: 33, | ||
| 10 | - targetVersion : 33, | ||
| 11 | - minVersion : 21, | 9 | + compileVersion: 33, |
| 10 | + targetVersion : 33, | ||
| 11 | + minVersion : 21, | ||
| 12 | ] | 12 | ] |
| 13 | versions = [ | 13 | versions = [ |
| 14 | - androidx_core : "1.10.1", | ||
| 15 | - androidx_lifecycle: "2.5.1", | ||
| 16 | - autoService : '1.0.1', | ||
| 17 | - coroutines : "1.6.0", | ||
| 18 | - dagger : "2.46", | ||
| 19 | - groupie : "2.9.0", | ||
| 20 | - junit : "4.13.2", | ||
| 21 | - junitJupiter : "5.5.0", | ||
| 22 | - lint : "30.0.1", | ||
| 23 | - serialization : "1.5.0", | ||
| 24 | - protobuf : "3.22.0", | 14 | + androidx_core : "1.10.1", |
| 15 | + androidx_lifecycle: "2.5.1", | ||
| 16 | + autoService : '1.0.1', | ||
| 17 | + coroutines : "1.6.0", | ||
| 18 | + dagger : "2.46", | ||
| 19 | + groupie : "2.9.0", | ||
| 20 | + junit : "4.13.2", | ||
| 21 | + junitJupiter : "5.5.0", | ||
| 22 | + lint : "30.0.1", | ||
| 23 | + serialization : "1.5.0", | ||
| 24 | + protobuf : "3.22.0", | ||
| 25 | ] | 25 | ] |
| 26 | generated = [ | 26 | generated = [ |
| 27 | - protoSrc: "$projectDir/protocol", | 27 | + protoSrc: "$projectDir/protocol", |
| 28 | ] | 28 | ] |
| 29 | deps = [ | 29 | deps = [ |
| 30 | - androidx : [ | ||
| 31 | - 'annotation' : 'androidx.annotation:annotation:1.6.0', | ||
| 32 | - 'activity_compose' : 'androidx.activity:activity-compose:1.7.1', | ||
| 33 | - 'constraintlayout_compose': "androidx.constraintlayout:constraintlayout-compose:1.0.1", | ||
| 34 | - ], | ||
| 35 | - auto : [ | ||
| 36 | - 'service' : "com.google.auto.service:auto-service:${versions.autoService}", | ||
| 37 | - 'serviceAnnotations': "com.google.auto.service:auto-service-annotations:${versions.autoService}", | ||
| 38 | - ], | ||
| 39 | - coroutines : [ | ||
| 40 | - "lib" : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}", | ||
| 41 | - "test": "org.jetbrains.kotlinx:kotlinx-coroutines-test: ${versions.coroutines}", | ||
| 42 | - ], | ||
| 43 | - compose : [ | ||
| 44 | - "bom": "androidx.compose:compose-bom:2023.04.01", | ||
| 45 | - ], | ||
| 46 | - timber : "com.github.ajalt:timberkt:1.5.1", | 30 | + androidx : [ |
| 31 | + 'annotation' : 'androidx.annotation:annotation:1.6.0', | ||
| 32 | + 'activity_compose' : 'androidx.activity:activity-compose:1.7.1', | ||
| 33 | + 'constraintlayout_compose': "androidx.constraintlayout:constraintlayout-compose:1.0.1", | ||
| 34 | + ], | ||
| 35 | + auto : [ | ||
| 36 | + 'service' : "com.google.auto.service:auto-service:${versions.autoService}", | ||
| 37 | + 'serviceAnnotations': "com.google.auto.service:auto-service-annotations:${versions.autoService}", | ||
| 38 | + ], | ||
| 39 | + coroutines : [ | ||
| 40 | + "lib" : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}", | ||
| 41 | + "test": "org.jetbrains.kotlinx:kotlinx-coroutines-test: ${versions.coroutines}", | ||
| 42 | + ], | ||
| 43 | + compose : [ | ||
| 44 | + "bom": "androidx.compose:compose-bom:2023.04.01", | ||
| 45 | + ], | ||
| 46 | + timber : "com.github.ajalt:timberkt:1.5.1", | ||
| 47 | 47 | ||
| 48 | - // lint | ||
| 49 | - lint : "com.android.tools.lint:lint:${versions.lint}", | ||
| 50 | - lintApi : "com.android.tools.lint:lint-api:${versions.lint}", | ||
| 51 | - lintChecks : "com.android.tools.lint:lint-checks:${versions.lint}", | ||
| 52 | - lintTests : "com.android.tools.lint:lint-tests:${versions.lint}", | ||
| 53 | - | ||
| 54 | - // tests | ||
| 55 | - androidx_test : [ | ||
| 56 | - "core" : 'androidx.test:core:1.5.0', | ||
| 57 | - "junit": "androidx.test.ext:junit:1.1.5", | ||
| 58 | - ], | ||
| 59 | - espresso : 'androidx.test.espresso:espresso-core:3.5.1', | ||
| 60 | - junit : "junit:junit:${versions.junit}", | ||
| 61 | - junitJupiterApi : "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}", | ||
| 62 | - junitJupiterEngine: "org.junit.jupiter:junit-jupiter-engine:${versions.junitJupiter}", | ||
| 63 | - mockito : [ | ||
| 64 | - "core" : 'org.mockito:mockito-core:4.0.0', | ||
| 65 | - "kotlin": "org.mockito.kotlin:mockito-kotlin:4.0.0", | ||
| 66 | - ], | ||
| 67 | - robolectric : 'org.robolectric:robolectric:4.10.2', | 48 | + // lint |
| 49 | + lint : "com.android.tools.lint:lint:${versions.lint}", | ||
| 50 | + lintApi : "com.android.tools.lint:lint-api:${versions.lint}", | ||
| 51 | + lintChecks : "com.android.tools.lint:lint-checks:${versions.lint}", | ||
| 52 | + lintTests : "com.android.tools.lint:lint-tests:${versions.lint}", | ||
| 68 | 53 | ||
| 54 | + // tests | ||
| 55 | + androidx_test : [ | ||
| 56 | + "core" : 'androidx.test:core:1.5.0', | ||
| 57 | + "junit": "androidx.test.ext:junit:1.1.5", | ||
| 58 | + ], | ||
| 59 | + espresso : 'androidx.test.espresso:espresso-core:3.5.1', | ||
| 60 | + junit : "junit:junit:${versions.junit}", | ||
| 61 | + junitJupiterApi : "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}", | ||
| 62 | + junitJupiterEngine: "org.junit.jupiter:junit-jupiter-engine:${versions.junitJupiter}", | ||
| 63 | + mockito : [ | ||
| 64 | + "core" : 'org.mockito:mockito-core:4.0.0', | ||
| 65 | + "kotlin": "org.mockito.kotlin:mockito-kotlin:4.0.0", | ||
| 66 | + ], | ||
| 67 | + robolectric : 'org.robolectric:robolectric:4.10.2', | ||
| 68 | + turbine : 'app.cash.turbine:turbine:1.0.0', | ||
| 69 | ] | 69 | ] |
| 70 | annotations = [ | 70 | annotations = [ |
| 71 | ] | 71 | ] |
| @@ -23,7 +23,8 @@ kotlin.code.style=official | @@ -23,7 +23,8 @@ kotlin.code.style=official | ||
| 23 | ############################################################### | 23 | ############################################################### |
| 24 | 24 | ||
| 25 | GROUP=io.livekit | 25 | GROUP=io.livekit |
| 26 | -VERSION_NAME=1.6.0 | 26 | +VERSION_NAME=2.0.0-SNAPSHOT |
| 27 | + | ||
| 27 | 28 | ||
| 28 | POM_DESCRIPTION=LiveKit Android SDK, WebRTC Rooms | 29 | POM_DESCRIPTION=LiveKit Android SDK, WebRTC Rooms |
| 29 | 30 |
| @@ -147,7 +147,7 @@ dependencies { | @@ -147,7 +147,7 @@ dependencies { | ||
| 147 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | 147 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
| 148 | implementation deps.coroutines.lib | 148 | implementation deps.coroutines.lib |
| 149 | implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:${versions.serialization}" | 149 | implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:${versions.serialization}" |
| 150 | - api 'io.github.webrtc-sdk:android:114.5735.05' | 150 | + api 'io.github.webrtc-sdk:android-prefixed:114.5735.07' |
| 151 | api "com.squareup.okhttp3:okhttp:4.12.0" | 151 | api "com.squareup.okhttp3:okhttp:4.12.0" |
| 152 | api 'com.github.davidliu:audioswitch:89582c47c9a04c62f90aa5e57251af4800a62c9a' | 152 | api 'com.github.davidliu:audioswitch:89582c47c9a04c62f90aa5e57251af4800a62c9a' |
| 153 | implementation deps.androidx.annotation | 153 | implementation deps.androidx.annotation |
| @@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
| 22 | 22 | ||
| 23 | # WebRTC | 23 | # WebRTC |
| 24 | ######################################### | 24 | ######################################### |
| 25 | --keep class org.webrtc.** { *; } | 25 | +-keep class livekit.org.webrtc.** { *; } |
| 26 | 26 | ||
| 27 | # NIST sdp parser | 27 | # NIST sdp parser |
| 28 | ######################################### | 28 | ######################################### |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,8 +17,12 @@ | @@ -17,8 +17,12 @@ | ||
| 17 | package io.livekit.android | 17 | package io.livekit.android |
| 18 | 18 | ||
| 19 | import io.livekit.android.room.ProtocolVersion | 19 | import io.livekit.android.room.ProtocolVersion |
| 20 | -import org.webrtc.PeerConnection | 20 | +import io.livekit.android.room.Room |
| 21 | +import livekit.org.webrtc.PeerConnection | ||
| 21 | 22 | ||
| 23 | +/** | ||
| 24 | + * Options for using with [Room.connect]. | ||
| 25 | + */ | ||
| 22 | data class ConnectOptions( | 26 | data class ConnectOptions( |
| 23 | /** Auto subscribe to room tracks upon connect, defaults to true */ | 27 | /** Auto subscribe to room tracks upon connect, defaults to true */ |
| 24 | val autoSubscribe: Boolean = true, | 28 | val autoSubscribe: Boolean = true, |
| @@ -48,7 +52,7 @@ data class ConnectOptions( | @@ -48,7 +52,7 @@ data class ConnectOptions( | ||
| 48 | /** | 52 | /** |
| 49 | * the protocol version to use with the server. | 53 | * the protocol version to use with the server. |
| 50 | */ | 54 | */ |
| 51 | - val protocolVersion: ProtocolVersion = ProtocolVersion.v9 | 55 | + val protocolVersion: ProtocolVersion = ProtocolVersion.v12, |
| 52 | ) { | 56 | ) { |
| 53 | internal var reconnect: Boolean = false | 57 | internal var reconnect: Boolean = false |
| 54 | internal var participantSid: String? = null | 58 | internal var participantSid: String? = null |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,119 +21,89 @@ import android.content.Context | @@ -21,119 +21,89 @@ import android.content.Context | ||
| 21 | import io.livekit.android.dagger.DaggerLiveKitComponent | 21 | import io.livekit.android.dagger.DaggerLiveKitComponent |
| 22 | import io.livekit.android.dagger.RTCModule | 22 | import io.livekit.android.dagger.RTCModule |
| 23 | import io.livekit.android.dagger.create | 23 | import io.livekit.android.dagger.create |
| 24 | -import io.livekit.android.room.ProtocolVersion | ||
| 25 | import io.livekit.android.room.Room | 24 | import io.livekit.android.room.Room |
| 26 | -import io.livekit.android.room.RoomListener | ||
| 27 | import io.livekit.android.util.LKLog | 25 | import io.livekit.android.util.LKLog |
| 28 | import io.livekit.android.util.LoggingLevel | 26 | import io.livekit.android.util.LoggingLevel |
| 29 | import timber.log.Timber | 27 | import timber.log.Timber |
| 30 | 28 | ||
| 31 | -class LiveKit { | ||
| 32 | - companion object { | ||
| 33 | - /** | ||
| 34 | - * [LoggingLevel] to use for Livekit logs. Set to [LoggingLevel.OFF] to turn off logs. | ||
| 35 | - * | ||
| 36 | - * Defaults to [LoggingLevel.OFF] | ||
| 37 | - */ | ||
| 38 | - @JvmStatic | ||
| 39 | - var loggingLevel: LoggingLevel | ||
| 40 | - get() = LKLog.loggingLevel | ||
| 41 | - set(value) { | ||
| 42 | - LKLog.loggingLevel = value | ||
| 43 | - | ||
| 44 | - // Plant debug tree if needed. | ||
| 45 | - if (value != LoggingLevel.OFF) { | ||
| 46 | - val forest = Timber.forest() | ||
| 47 | - val needsPlanting = forest.none { it is Timber.DebugTree } | ||
| 48 | - | ||
| 49 | - if (needsPlanting) { | ||
| 50 | - Timber.plant(Timber.DebugTree()) | ||
| 51 | - } | 29 | +object LiveKit { |
| 30 | + /** | ||
| 31 | + * [LoggingLevel] to use for Livekit logs. Set to [LoggingLevel.OFF] to turn off logs. | ||
| 32 | + * | ||
| 33 | + * Defaults to [LoggingLevel.OFF] | ||
| 34 | + */ | ||
| 35 | + @JvmStatic | ||
| 36 | + var loggingLevel: LoggingLevel | ||
| 37 | + get() = LKLog.loggingLevel | ||
| 38 | + set(value) { | ||
| 39 | + LKLog.loggingLevel = value | ||
| 40 | + | ||
| 41 | + // Plant debug tree if needed. | ||
| 42 | + if (value != LoggingLevel.OFF) { | ||
| 43 | + val forest = Timber.forest() | ||
| 44 | + val needsPlanting = forest.none { it is Timber.DebugTree } | ||
| 45 | + | ||
| 46 | + if (needsPlanting) { | ||
| 47 | + Timber.plant(Timber.DebugTree()) | ||
| 52 | } | 48 | } |
| 53 | } | 49 | } |
| 54 | - | ||
| 55 | - /** | ||
| 56 | - * Enables logs for the underlying WebRTC sdk logging. Used in conjunction with [loggingLevel]. | ||
| 57 | - * | ||
| 58 | - * Note: WebRTC logging is very noisy and should only be used to diagnose native WebRTC issues. | ||
| 59 | - */ | ||
| 60 | - @JvmStatic | ||
| 61 | - var enableWebRTCLogging: Boolean = false | ||
| 62 | - | ||
| 63 | - /** | ||
| 64 | - * Certain WebRTC classes need to be initialized prior to use. | ||
| 65 | - * | ||
| 66 | - * This does not need to be called under normal circumstances, as [LiveKit.create] | ||
| 67 | - * will handle this for you. | ||
| 68 | - */ | ||
| 69 | - fun init(appContext: Context) { | ||
| 70 | - RTCModule.libWebrtcInitialization(appContext) | ||
| 71 | } | 50 | } |
| 72 | 51 | ||
| 73 | - /** | ||
| 74 | - * Create a Room object. | ||
| 75 | - */ | ||
| 76 | - fun create( | ||
| 77 | - appContext: Context, | ||
| 78 | - options: RoomOptions = RoomOptions(), | ||
| 79 | - overrides: LiveKitOverrides = LiveKitOverrides(), | ||
| 80 | - ): Room { | ||
| 81 | - val ctx = appContext.applicationContext | ||
| 82 | - | ||
| 83 | - if (ctx !is Application) { | ||
| 84 | - LKLog.w { "Application context was not found, this may cause memory leaks." } | ||
| 85 | - } | ||
| 86 | - | ||
| 87 | - val component = DaggerLiveKitComponent | ||
| 88 | - .factory() | ||
| 89 | - .create(ctx, overrides) | ||
| 90 | - | ||
| 91 | - val room = component.roomFactory().create(ctx) | ||
| 92 | - | ||
| 93 | - options.audioTrackCaptureDefaults?.let { | ||
| 94 | - room.audioTrackCaptureDefaults = it | ||
| 95 | - } | ||
| 96 | - options.videoTrackCaptureDefaults?.let { | ||
| 97 | - room.videoTrackCaptureDefaults = it | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - options.audioTrackPublishDefaults?.let { | ||
| 101 | - room.audioTrackPublishDefaults = it | ||
| 102 | - } | ||
| 103 | - options.videoTrackPublishDefaults?.let { | ||
| 104 | - room.videoTrackPublishDefaults = it | ||
| 105 | - } | ||
| 106 | - room.adaptiveStream = options.adaptiveStream | ||
| 107 | - room.dynacast = options.dynacast | ||
| 108 | - room.e2eeOptions = options.e2eeOptions | 52 | + /** |
| 53 | + * Enables logs for the underlying WebRTC sdk logging. Used in conjunction with [loggingLevel]. | ||
| 54 | + * | ||
| 55 | + * Note: WebRTC logging is very noisy and should only be used to diagnose native WebRTC issues. | ||
| 56 | + */ | ||
| 57 | + @JvmStatic | ||
| 58 | + var enableWebRTCLogging: Boolean = false | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Certain WebRTC classes need to be initialized prior to use. | ||
| 62 | + * | ||
| 63 | + * This does not need to be called under normal circumstances, as [LiveKit.create] | ||
| 64 | + * will handle this for you. | ||
| 65 | + */ | ||
| 66 | + fun init(appContext: Context) { | ||
| 67 | + RTCModule.libWebrtcInitialization(appContext) | ||
| 68 | + } | ||
| 109 | 69 | ||
| 110 | - return room | 70 | + /** |
| 71 | + * Create a Room object. | ||
| 72 | + */ | ||
| 73 | + fun create( | ||
| 74 | + appContext: Context, | ||
| 75 | + options: RoomOptions = RoomOptions(), | ||
| 76 | + overrides: LiveKitOverrides = LiveKitOverrides(), | ||
| 77 | + ): Room { | ||
| 78 | + val ctx = appContext.applicationContext | ||
| 79 | + | ||
| 80 | + if (ctx !is Application) { | ||
| 81 | + LKLog.w { "Application context was not found, this may cause memory leaks." } | ||
| 111 | } | 82 | } |
| 112 | 83 | ||
| 113 | - /** | ||
| 114 | - * Connect to a LiveKit room | ||
| 115 | - * @param url URL to LiveKit server (i.e. ws://mylivekitdeploy.io) | ||
| 116 | - * @param listener Listener to Room events. LiveKit interactions take place with these callbacks | ||
| 117 | - */ | ||
| 118 | - @Deprecated("Use LiveKit.create and Room.connect instead. This is limited to max protocol 7.") | ||
| 119 | - suspend fun connect( | ||
| 120 | - appContext: Context, | ||
| 121 | - url: String, | ||
| 122 | - token: String, | ||
| 123 | - options: ConnectOptions = ConnectOptions(), | ||
| 124 | - roomOptions: RoomOptions = RoomOptions(), | ||
| 125 | - listener: RoomListener? = null, | ||
| 126 | - overrides: LiveKitOverrides = LiveKitOverrides(), | ||
| 127 | - ): Room { | ||
| 128 | - val room = create(appContext, roomOptions, overrides) | 84 | + val component = DaggerLiveKitComponent |
| 85 | + .factory() | ||
| 86 | + .create(ctx, overrides) | ||
| 129 | 87 | ||
| 130 | - room.listener = listener | 88 | + val room = component.roomFactory().create(ctx) |
| 131 | 89 | ||
| 132 | - val protocolVersion = maxOf(options.protocolVersion, ProtocolVersion.v7) | ||
| 133 | - val connectOptions = options.copy(protocolVersion = protocolVersion) | 90 | + options.audioTrackCaptureDefaults?.let { |
| 91 | + room.audioTrackCaptureDefaults = it | ||
| 92 | + } | ||
| 93 | + options.videoTrackCaptureDefaults?.let { | ||
| 94 | + room.videoTrackCaptureDefaults = it | ||
| 95 | + } | ||
| 134 | 96 | ||
| 135 | - room.connect(url, token, connectOptions) | ||
| 136 | - return room | 97 | + options.audioTrackPublishDefaults?.let { |
| 98 | + room.audioTrackPublishDefaults = it | ||
| 137 | } | 99 | } |
| 100 | + options.videoTrackPublishDefaults?.let { | ||
| 101 | + room.videoTrackPublishDefaults = it | ||
| 102 | + } | ||
| 103 | + room.adaptiveStream = options.adaptiveStream | ||
| 104 | + room.dynacast = options.dynacast | ||
| 105 | + room.e2eeOptions = options.e2eeOptions | ||
| 106 | + | ||
| 107 | + return room | ||
| 138 | } | 108 | } |
| 139 | } | 109 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -23,12 +23,12 @@ import io.livekit.android.audio.AudioHandler | @@ -23,12 +23,12 @@ import io.livekit.android.audio.AudioHandler | ||
| 23 | import io.livekit.android.audio.AudioSwitchHandler | 23 | import io.livekit.android.audio.AudioSwitchHandler |
| 24 | import io.livekit.android.audio.NoAudioHandler | 24 | import io.livekit.android.audio.NoAudioHandler |
| 25 | import io.livekit.android.room.Room | 25 | import io.livekit.android.room.Room |
| 26 | +import livekit.org.webrtc.EglBase | ||
| 27 | +import livekit.org.webrtc.VideoDecoderFactory | ||
| 28 | +import livekit.org.webrtc.VideoEncoderFactory | ||
| 29 | +import livekit.org.webrtc.audio.AudioDeviceModule | ||
| 30 | +import livekit.org.webrtc.audio.JavaAudioDeviceModule | ||
| 26 | import okhttp3.OkHttpClient | 31 | import okhttp3.OkHttpClient |
| 27 | -import org.webrtc.EglBase | ||
| 28 | -import org.webrtc.VideoDecoderFactory | ||
| 29 | -import org.webrtc.VideoEncoderFactory | ||
| 30 | -import org.webrtc.audio.AudioDeviceModule | ||
| 31 | -import org.webrtc.audio.JavaAudioDeviceModule | ||
| 32 | 32 | ||
| 33 | /** | 33 | /** |
| 34 | * Overrides to replace LiveKit internally used components with custom implementations. | 34 | * Overrides to replace LiveKit internally used components with custom implementations. |
| @@ -65,6 +65,9 @@ data class LiveKitOverrides( | @@ -65,6 +65,9 @@ data class LiveKitOverrides( | ||
| 65 | val eglBase: EglBase? = null, | 65 | val eglBase: EglBase? = null, |
| 66 | ) | 66 | ) |
| 67 | 67 | ||
| 68 | +/** | ||
| 69 | + * Options for customizing the audio settings of LiveKit. | ||
| 70 | + */ | ||
| 68 | class AudioOptions( | 71 | class AudioOptions( |
| 69 | /** | 72 | /** |
| 70 | * Override the default output [AudioType]. | 73 | * Override the default output [AudioType]. |
| @@ -103,6 +106,9 @@ class AudioOptions( | @@ -103,6 +106,9 @@ class AudioOptions( | ||
| 103 | val javaAudioDeviceModuleCustomizer: ((builder: JavaAudioDeviceModule.Builder) -> Unit)? = null, | 106 | val javaAudioDeviceModuleCustomizer: ((builder: JavaAudioDeviceModule.Builder) -> Unit)? = null, |
| 104 | ) | 107 | ) |
| 105 | 108 | ||
| 109 | +/** | ||
| 110 | + * Audio types for customizing the audio of LiveKit. | ||
| 111 | + */ | ||
| 106 | sealed class AudioType( | 112 | sealed class AudioType( |
| 107 | val audioMode: Int, | 113 | val audioMode: Int, |
| 108 | val audioAttributes: AudioAttributes, | 114 | val audioAttributes: AudioAttributes, |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,6 +16,12 @@ | @@ -16,6 +16,12 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android | 17 | package io.livekit.android |
| 18 | 18 | ||
| 19 | +/** | ||
| 20 | + * Version information about LiveKit | ||
| 21 | + */ | ||
| 19 | object Version { | 22 | object Version { |
| 23 | + /** | ||
| 24 | + * The current LiveKit SDK version. | ||
| 25 | + */ | ||
| 20 | const val CLIENT_VERSION = BuildConfig.VERSION_NAME | 26 | const val CLIENT_VERSION = BuildConfig.VERSION_NAME |
| 21 | } | 27 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -76,6 +76,9 @@ constructor(context: Context) : AudioHandler { | @@ -76,6 +76,9 @@ constructor(context: Context) : AudioHandler { | ||
| 76 | onAudioFocusChangeListener?.onAudioFocusChange(it) | 76 | onAudioFocusChangeListener?.onAudioFocusChange(it) |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | + /** | ||
| 80 | + * Set this to listen to audio focus changes. | ||
| 81 | + */ | ||
| 79 | var onAudioFocusChangeListener: AudioManager.OnAudioFocusChangeListener? = null | 82 | var onAudioFocusChangeListener: AudioManager.OnAudioFocusChangeListener? = null |
| 80 | 83 | ||
| 81 | override fun start() { | 84 | override fun start() { |
| @@ -98,6 +101,16 @@ constructor(context: Context) : AudioHandler { | @@ -98,6 +101,16 @@ constructor(context: Context) : AudioHandler { | ||
| 98 | } | 101 | } |
| 99 | } | 102 | } |
| 100 | 103 | ||
| 104 | + /** | ||
| 105 | + * Creates the request used when requesting audio focus. | ||
| 106 | + * | ||
| 107 | + * The default implementation creates an audio focus request based on the | ||
| 108 | + * settings of this object. | ||
| 109 | + * | ||
| 110 | + * Only used on Android O and upwards. On lower platforms, | ||
| 111 | + * the request will be made using the [audioStreamType] and [focusMode] | ||
| 112 | + * settings. | ||
| 113 | + */ | ||
| 101 | @RequiresApi(Build.VERSION_CODES.O) | 114 | @RequiresApi(Build.VERSION_CODES.O) |
| 102 | open fun createAudioRequest(): AudioFocusRequest { | 115 | open fun createAudioRequest(): AudioFocusRequest { |
| 103 | return AudioFocusRequest.Builder(focusMode) | 116 | return AudioFocusRequest.Builder(focusMode) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -183,12 +183,21 @@ constructor(private val context: Context) : AudioHandler { | @@ -183,12 +183,21 @@ constructor(private val context: Context) : AudioHandler { | ||
| 183 | } | 183 | } |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | + /** | ||
| 187 | + * The currently selected audio device, or null if none (or this handler is not started). | ||
| 188 | + */ | ||
| 186 | val selectedAudioDevice: AudioDevice? | 189 | val selectedAudioDevice: AudioDevice? |
| 187 | get() = audioSwitch?.selectedAudioDevice | 190 | get() = audioSwitch?.selectedAudioDevice |
| 188 | 191 | ||
| 192 | + /** | ||
| 193 | + * The available audio devices. This requires calling [start] before it is populated. | ||
| 194 | + */ | ||
| 189 | val availableAudioDevices: List<AudioDevice> | 195 | val availableAudioDevices: List<AudioDevice> |
| 190 | get() = audioSwitch?.availableAudioDevices ?: listOf() | 196 | get() = audioSwitch?.availableAudioDevices ?: listOf() |
| 191 | 197 | ||
| 198 | + /** | ||
| 199 | + * Select a specific audio device. | ||
| 200 | + */ | ||
| 192 | fun selectDevice(audioDevice: AudioDevice?) { | 201 | fun selectDevice(audioDevice: AudioDevice?) { |
| 193 | if (Looper.myLooper() == Looper.getMainLooper()) { | 202 | if (Looper.myLooper() == Looper.getMainLooper()) { |
| 194 | audioSwitch?.selectDevice(audioDevice) | 203 | audioSwitch?.selectDevice(audioDevice) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -30,7 +30,7 @@ import javax.inject.Singleton | @@ -30,7 +30,7 @@ import javax.inject.Singleton | ||
| 30 | * @suppress | 30 | * @suppress |
| 31 | */ | 31 | */ |
| 32 | @Module | 32 | @Module |
| 33 | -object AudioHandlerModule { | 33 | +internal object AudioHandlerModule { |
| 34 | 34 | ||
| 35 | @Provides | 35 | @Provides |
| 36 | fun audioOutputType( | 36 | fun audioOutputType( |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -25,7 +25,7 @@ import javax.inject.Named | @@ -25,7 +25,7 @@ import javax.inject.Named | ||
| 25 | * @suppress | 25 | * @suppress |
| 26 | */ | 26 | */ |
| 27 | @Module | 27 | @Module |
| 28 | -object CoroutinesModule { | 28 | +internal object CoroutinesModule { |
| 29 | @Provides | 29 | @Provides |
| 30 | @Named(InjectionNames.DISPATCHER_DEFAULT) | 30 | @Named(InjectionNames.DISPATCHER_DEFAULT) |
| 31 | fun defaultDispatcher() = Dispatchers.Default | 31 | fun defaultDispatcher() = Dispatchers.Default |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,10 +16,7 @@ | @@ -16,10 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.dagger | 17 | package io.livekit.android.dagger |
| 18 | 18 | ||
| 19 | -/** | ||
| 20 | - * @suppress | ||
| 21 | - */ | ||
| 22 | -object InjectionNames { | 19 | +internal object InjectionNames { |
| 23 | 20 | ||
| 24 | /** | 21 | /** |
| 25 | * @see [kotlinx.coroutines.Dispatchers.Default] | 22 | * @see [kotlinx.coroutines.Dispatchers.Default] |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,11 +21,8 @@ import dagger.Provides | @@ -21,11 +21,8 @@ import dagger.Provides | ||
| 21 | import dagger.Reusable | 21 | import dagger.Reusable |
| 22 | import kotlinx.serialization.json.Json | 22 | import kotlinx.serialization.json.Json |
| 23 | 23 | ||
| 24 | -/** | ||
| 25 | - * @suppress | ||
| 26 | - */ | ||
| 27 | @Module | 24 | @Module |
| 28 | -object JsonFormatModule { | 25 | +internal object JsonFormatModule { |
| 29 | @Provides | 26 | @Provides |
| 30 | @Reusable | 27 | @Reusable |
| 31 | fun kotlinSerializationJson(): Json = | 28 | fun kotlinSerializationJson(): Json = |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,8 +21,8 @@ import dagger.BindsInstance | @@ -21,8 +21,8 @@ import dagger.BindsInstance | ||
| 21 | import dagger.Component | 21 | import dagger.Component |
| 22 | import io.livekit.android.LiveKitOverrides | 22 | import io.livekit.android.LiveKitOverrides |
| 23 | import io.livekit.android.room.Room | 23 | import io.livekit.android.room.Room |
| 24 | -import org.webrtc.EglBase | ||
| 25 | -import org.webrtc.PeerConnectionFactory | 24 | +import livekit.org.webrtc.EglBase |
| 25 | +import livekit.org.webrtc.PeerConnectionFactory | ||
| 26 | import javax.inject.Singleton | 26 | import javax.inject.Singleton |
| 27 | 27 | ||
| 28 | @Singleton | 28 | @Singleton |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,11 +21,8 @@ import dagger.Provides | @@ -21,11 +21,8 @@ import dagger.Provides | ||
| 21 | import io.livekit.android.memory.CloseableManager | 21 | import io.livekit.android.memory.CloseableManager |
| 22 | import javax.inject.Singleton | 22 | import javax.inject.Singleton |
| 23 | 23 | ||
| 24 | -/** | ||
| 25 | - * @suppress | ||
| 26 | - */ | ||
| 27 | @Module | 24 | @Module |
| 28 | -object MemoryModule { | 25 | +internal object MemoryModule { |
| 29 | 26 | ||
| 30 | @Singleton | 27 | @Singleton |
| 31 | @Provides | 28 | @Provides |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -24,11 +24,8 @@ import io.livekit.android.LiveKitOverrides | @@ -24,11 +24,8 @@ import io.livekit.android.LiveKitOverrides | ||
| 24 | import javax.inject.Named | 24 | import javax.inject.Named |
| 25 | 25 | ||
| 26 | @SuppressLint("KotlinNullnessAnnotation") | 26 | @SuppressLint("KotlinNullnessAnnotation") |
| 27 | -/** | ||
| 28 | - * @suppress | ||
| 29 | - */ | ||
| 30 | @Module | 27 | @Module |
| 31 | -class OverridesModule(private val overrides: LiveKitOverrides) { | 28 | +internal class OverridesModule(private val overrides: LiveKitOverrides) { |
| 32 | 29 | ||
| 33 | @Provides | 30 | @Provides |
| 34 | @Named(InjectionNames.OVERRIDE_OKHTTP) | 31 | @Named(InjectionNames.OVERRIDE_OKHTTP) |
| @@ -30,20 +30,17 @@ import io.livekit.android.util.LKLog | @@ -30,20 +30,17 @@ import io.livekit.android.util.LKLog | ||
| 30 | import io.livekit.android.util.LoggingLevel | 30 | import io.livekit.android.util.LoggingLevel |
| 31 | import io.livekit.android.webrtc.CustomVideoDecoderFactory | 31 | import io.livekit.android.webrtc.CustomVideoDecoderFactory |
| 32 | import io.livekit.android.webrtc.CustomVideoEncoderFactory | 32 | import io.livekit.android.webrtc.CustomVideoEncoderFactory |
| 33 | -import org.webrtc.* | ||
| 34 | -import org.webrtc.audio.AudioDeviceModule | ||
| 35 | -import org.webrtc.audio.JavaAudioDeviceModule | 33 | +import livekit.org.webrtc.* |
| 34 | +import livekit.org.webrtc.audio.AudioDeviceModule | ||
| 35 | +import livekit.org.webrtc.audio.JavaAudioDeviceModule | ||
| 36 | import timber.log.Timber | 36 | import timber.log.Timber |
| 37 | import javax.inject.Named | 37 | import javax.inject.Named |
| 38 | import javax.inject.Singleton | 38 | import javax.inject.Singleton |
| 39 | 39 | ||
| 40 | -typealias CapabilitiesGetter = @JvmSuppressWildcards (MediaStreamTrack.MediaType) -> RtpCapabilities | 40 | +internal typealias CapabilitiesGetter = @JvmSuppressWildcards (MediaStreamTrack.MediaType) -> RtpCapabilities |
| 41 | 41 | ||
| 42 | -/** | ||
| 43 | - * @suppress | ||
| 44 | - */ | ||
| 45 | @Module | 42 | @Module |
| 46 | -object RTCModule { | 43 | +internal object RTCModule { |
| 47 | 44 | ||
| 48 | /** | 45 | /** |
| 49 | * Certain classes require libwebrtc to be initialized prior to use. | 46 | * Certain classes require libwebrtc to be initialized prior to use. |
| @@ -55,6 +52,7 @@ object RTCModule { | @@ -55,6 +52,7 @@ object RTCModule { | ||
| 55 | PeerConnectionFactory.initialize( | 52 | PeerConnectionFactory.initialize( |
| 56 | PeerConnectionFactory.InitializationOptions | 53 | PeerConnectionFactory.InitializationOptions |
| 57 | .builder(appContext) | 54 | .builder(appContext) |
| 55 | + .setNativeLibraryName("lkjingle_peerconnection_so") | ||
| 58 | .setInjectableLogger( | 56 | .setInjectableLogger( |
| 59 | { s, severity, s2 -> | 57 | { s, severity, s2 -> |
| 60 | if (!LiveKit.enableWebRTCLogging) { | 58 | if (!LiveKit.enableWebRTCLogging) { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -28,11 +28,8 @@ import okhttp3.WebSocket | @@ -28,11 +28,8 @@ import okhttp3.WebSocket | ||
| 28 | import javax.inject.Named | 28 | import javax.inject.Named |
| 29 | import javax.inject.Singleton | 29 | import javax.inject.Singleton |
| 30 | 30 | ||
| 31 | -/** | ||
| 32 | - * @suppress | ||
| 33 | - */ | ||
| 34 | @Module | 31 | @Module |
| 35 | -object WebModule { | 32 | +internal object WebModule { |
| 36 | @Provides | 33 | @Provides |
| 37 | @Singleton | 34 | @Singleton |
| 38 | fun okHttpClient( | 35 | fun okHttpClient( |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -22,6 +22,7 @@ import dagger.assisted.AssistedInject | @@ -22,6 +22,7 @@ import dagger.assisted.AssistedInject | ||
| 22 | import io.livekit.android.events.RoomEvent | 22 | import io.livekit.android.events.RoomEvent |
| 23 | import io.livekit.android.room.Room | 23 | import io.livekit.android.room.Room |
| 24 | import io.livekit.android.room.participant.LocalParticipant | 24 | import io.livekit.android.room.participant.LocalParticipant |
| 25 | +import io.livekit.android.room.participant.Participant | ||
| 25 | import io.livekit.android.room.participant.RemoteParticipant | 26 | import io.livekit.android.room.participant.RemoteParticipant |
| 26 | import io.livekit.android.room.track.LocalAudioTrack | 27 | import io.livekit.android.room.track.LocalAudioTrack |
| 27 | import io.livekit.android.room.track.LocalVideoTrack | 28 | import io.livekit.android.room.track.LocalVideoTrack |
| @@ -30,13 +31,13 @@ import io.livekit.android.room.track.RemoteVideoTrack | @@ -30,13 +31,13 @@ import io.livekit.android.room.track.RemoteVideoTrack | ||
| 30 | import io.livekit.android.room.track.Track | 31 | import io.livekit.android.room.track.Track |
| 31 | import io.livekit.android.room.track.TrackPublication | 32 | import io.livekit.android.room.track.TrackPublication |
| 32 | import io.livekit.android.util.LKLog | 33 | import io.livekit.android.util.LKLog |
| 33 | -import org.webrtc.FrameCryptor | ||
| 34 | -import org.webrtc.FrameCryptor.FrameCryptionState | ||
| 35 | -import org.webrtc.FrameCryptorAlgorithm | ||
| 36 | -import org.webrtc.FrameCryptorFactory | ||
| 37 | -import org.webrtc.PeerConnectionFactory | ||
| 38 | -import org.webrtc.RtpReceiver | ||
| 39 | -import org.webrtc.RtpSender | 34 | +import livekit.org.webrtc.FrameCryptor |
| 35 | +import livekit.org.webrtc.FrameCryptor.FrameCryptionState | ||
| 36 | +import livekit.org.webrtc.FrameCryptorAlgorithm | ||
| 37 | +import livekit.org.webrtc.FrameCryptorFactory | ||
| 38 | +import livekit.org.webrtc.PeerConnectionFactory | ||
| 39 | +import livekit.org.webrtc.RtpReceiver | ||
| 40 | +import livekit.org.webrtc.RtpSender | ||
| 40 | 41 | ||
| 41 | class E2EEManager | 42 | class E2EEManager |
| 42 | @AssistedInject | 43 | @AssistedInject |
| @@ -47,7 +48,7 @@ constructor( | @@ -47,7 +48,7 @@ constructor( | ||
| 47 | private var room: Room? = null | 48 | private var room: Room? = null |
| 48 | private var keyProvider: KeyProvider | 49 | private var keyProvider: KeyProvider |
| 49 | private var peerConnectionFactory: PeerConnectionFactory | 50 | private var peerConnectionFactory: PeerConnectionFactory |
| 50 | - private var frameCryptors = mutableMapOf<Pair<String, String>, FrameCryptor>() | 51 | + private var frameCryptors = mutableMapOf<Pair<String, Participant.Identity>, FrameCryptor>() |
| 51 | private var algorithm: FrameCryptorAlgorithm = FrameCryptorAlgorithm.AES_GCM | 52 | private var algorithm: FrameCryptorAlgorithm = FrameCryptorAlgorithm.AES_GCM |
| 52 | private lateinit var emitEvent: (roomEvent: RoomEvent) -> Unit? | 53 | private lateinit var emitEvent: (roomEvent: RoomEvent) -> Unit? |
| 53 | var enabled: Boolean = false | 54 | var enabled: Boolean = false |
| @@ -69,7 +70,7 @@ constructor( | @@ -69,7 +70,7 @@ constructor( | ||
| 69 | this.enabled = true | 70 | this.enabled = true |
| 70 | this.room = room | 71 | this.room = room |
| 71 | this.emitEvent = emitEvent | 72 | this.emitEvent = emitEvent |
| 72 | - this.room?.localParticipant?.tracks?.forEach() { item -> | 73 | + this.room?.localParticipant?.trackPublications?.forEach() { item -> |
| 73 | var participant = this.room!!.localParticipant | 74 | var participant = this.room!!.localParticipant |
| 74 | var publication = item.value | 75 | var publication = item.value |
| 75 | if (publication.track != null) { | 76 | if (publication.track != null) { |
| @@ -78,7 +79,7 @@ constructor( | @@ -78,7 +79,7 @@ constructor( | ||
| 78 | } | 79 | } |
| 79 | this.room?.remoteParticipants?.forEach() { item -> | 80 | this.room?.remoteParticipants?.forEach() { item -> |
| 80 | var participant = item.value | 81 | var participant = item.value |
| 81 | - participant.tracks.forEach() { item -> | 82 | + participant.trackPublications.forEach() { item -> |
| 82 | var publication = item.value | 83 | var publication = item.value |
| 83 | if (publication.track != null) { | 84 | if (publication.track != null) { |
| 84 | addSubscribedTrack(publication.track!!, publication, participant, room) | 85 | addSubscribedTrack(publication.track!!, publication, participant, room) |
| @@ -169,11 +170,11 @@ constructor( | @@ -169,11 +170,11 @@ constructor( | ||
| 169 | } | 170 | } |
| 170 | } | 171 | } |
| 171 | 172 | ||
| 172 | - private fun addRtpSender(sender: RtpSender, participantId: String, trackId: String, kind: String): FrameCryptor { | 173 | + private fun addRtpSender(sender: RtpSender, participantId: Participant.Identity, trackId: String, kind: String): FrameCryptor { |
| 173 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpSender( | 174 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpSender( |
| 174 | peerConnectionFactory, | 175 | peerConnectionFactory, |
| 175 | sender, | 176 | sender, |
| 176 | - participantId, | 177 | + participantId.value, |
| 177 | algorithm, | 178 | algorithm, |
| 178 | keyProvider.rtcKeyProvider, | 179 | keyProvider.rtcKeyProvider, |
| 179 | ) | 180 | ) |
| @@ -183,11 +184,11 @@ constructor( | @@ -183,11 +184,11 @@ constructor( | ||
| 183 | return frameCryptor | 184 | return frameCryptor |
| 184 | } | 185 | } |
| 185 | 186 | ||
| 186 | - private fun addRtpReceiver(receiver: RtpReceiver, participantId: String, trackId: String, kind: String): FrameCryptor { | 187 | + private fun addRtpReceiver(receiver: RtpReceiver, participantId: Participant.Identity, trackId: String, kind: String): FrameCryptor { |
| 187 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpReceiver( | 188 | var frameCryptor = FrameCryptorFactory.createFrameCryptorForRtpReceiver( |
| 188 | peerConnectionFactory, | 189 | peerConnectionFactory, |
| 189 | receiver, | 190 | receiver, |
| 190 | - participantId, | 191 | + participantId.value, |
| 191 | algorithm, | 192 | algorithm, |
| 192 | keyProvider.rtcKeyProvider, | 193 | keyProvider.rtcKeyProvider, |
| 193 | ) | 194 | ) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,8 +17,8 @@ | @@ -17,8 +17,8 @@ | ||
| 17 | package io.livekit.android.e2ee | 17 | package io.livekit.android.e2ee |
| 18 | 18 | ||
| 19 | import io.livekit.android.util.LKLog | 19 | import io.livekit.android.util.LKLog |
| 20 | -import org.webrtc.FrameCryptorFactory | ||
| 21 | -import org.webrtc.FrameCryptorKeyProvider | 20 | +import livekit.org.webrtc.FrameCryptorFactory |
| 21 | +import livekit.org.webrtc.FrameCryptorKeyProvider | ||
| 22 | 22 | ||
| 23 | class KeyInfo | 23 | class KeyInfo |
| 24 | constructor(var participantId: String, var keyIndex: Int, var key: String) { | 24 | constructor(var participantId: String, var keyIndex: Int, var key: String) { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,12 +16,16 @@ | @@ -16,12 +16,16 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.events | 17 | package io.livekit.android.events |
| 18 | 18 | ||
| 19 | +import kotlinx.coroutines.flow.Flow | ||
| 19 | import kotlinx.coroutines.flow.SharedFlow | 20 | import kotlinx.coroutines.flow.SharedFlow |
| 20 | 21 | ||
| 21 | interface EventListenable<out T> { | 22 | interface EventListenable<out T> { |
| 22 | val events: SharedFlow<T> | 23 | val events: SharedFlow<T> |
| 23 | } | 24 | } |
| 24 | 25 | ||
| 26 | +/** | ||
| 27 | + * @see [Flow.collect] | ||
| 28 | + */ | ||
| 25 | suspend inline fun <T> EventListenable<T>.collect(crossinline action: suspend (value: T) -> Unit) { | 29 | suspend inline fun <T> EventListenable<T>.collect(crossinline action: suspend (value: T) -> Unit) { |
| 26 | events.collect { value -> action(value) } | 30 | events.collect { value -> action(value) } |
| 27 | } | 31 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -232,7 +232,7 @@ enum class DisconnectReason { | @@ -232,7 +232,7 @@ enum class DisconnectReason { | ||
| 232 | JOIN_FAILURE, | 232 | JOIN_FAILURE, |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | -fun LivekitModels.DisconnectReason?.convert(): DisconnectReason { | 235 | +internal fun LivekitModels.DisconnectReason?.convert(): DisconnectReason { |
| 236 | return when (this) { | 236 | return when (this) { |
| 237 | LivekitModels.DisconnectReason.CLIENT_INITIATED -> DisconnectReason.CLIENT_INITIATED | 237 | LivekitModels.DisconnectReason.CLIENT_INITIATED -> DisconnectReason.CLIENT_INITIATED |
| 238 | LivekitModels.DisconnectReason.DUPLICATE_IDENTITY -> DisconnectReason.DUPLICATE_IDENTITY | 238 | LivekitModels.DisconnectReason.DUPLICATE_IDENTITY -> DisconnectReason.DUPLICATE_IDENTITY |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -19,7 +19,7 @@ package io.livekit.android.memory | @@ -19,7 +19,7 @@ package io.livekit.android.memory | ||
| 19 | import java.io.Closeable | 19 | import java.io.Closeable |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | - * @hide | 22 | + * @suppress |
| 23 | */ | 23 | */ |
| 24 | class CloseableManager : Closeable { | 24 | class CloseableManager : Closeable { |
| 25 | 25 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.memory | 17 | package io.livekit.android.memory |
| 18 | 18 | ||
| 19 | -import org.webrtc.SurfaceTextureHelper | 19 | +import livekit.org.webrtc.SurfaceTextureHelper |
| 20 | import java.io.Closeable | 20 | import java.io.Closeable |
| 21 | 21 | ||
| 22 | internal class SurfaceTextureHelperCloser(private val surfaceTextureHelper: SurfaceTextureHelper) : Closeable { | 22 | internal class SurfaceTextureHelperCloser(private val surfaceTextureHelper: SurfaceTextureHelper) : Closeable { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -20,7 +20,7 @@ import android.content.Context | @@ -20,7 +20,7 @@ import android.content.Context | ||
| 20 | import android.util.AttributeSet | 20 | import android.util.AttributeSet |
| 21 | import android.view.View | 21 | import android.view.View |
| 22 | import io.livekit.android.room.track.video.ViewVisibility | 22 | import io.livekit.android.room.track.video.ViewVisibility |
| 23 | -import org.webrtc.SurfaceViewRenderer | 23 | +import livekit.org.webrtc.SurfaceViewRenderer |
| 24 | 24 | ||
| 25 | open class SurfaceViewRenderer : SurfaceViewRenderer, ViewVisibility.Notifier { | 25 | open class SurfaceViewRenderer : SurfaceViewRenderer, ViewVisibility.Notifier { |
| 26 | constructor(context: Context) : super(context) | 26 | constructor(context: Context) : super(context) |
| @@ -19,8 +19,8 @@ import android.view.SurfaceHolder | @@ -19,8 +19,8 @@ import android.view.SurfaceHolder | ||
| 19 | import android.view.TextureView | 19 | import android.view.TextureView |
| 20 | import android.view.View | 20 | import android.view.View |
| 21 | import io.livekit.android.room.track.video.ViewVisibility | 21 | import io.livekit.android.room.track.video.ViewVisibility |
| 22 | -import org.webrtc.* | ||
| 23 | -import org.webrtc.RendererCommon.* | 22 | +import livekit.org.webrtc.* |
| 23 | +import livekit.org.webrtc.RendererCommon.* | ||
| 24 | import java.util.concurrent.CountDownLatch | 24 | import java.util.concurrent.CountDownLatch |
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -20,8 +20,8 @@ import android.content.Context | @@ -20,8 +20,8 @@ import android.content.Context | ||
| 20 | import android.hardware.camera2.CameraManager | 20 | import android.hardware.camera2.CameraManager |
| 21 | import android.os.Handler | 21 | import android.os.Handler |
| 22 | import android.os.Looper | 22 | import android.os.Looper |
| 23 | -import org.webrtc.Camera1Enumerator | ||
| 24 | -import org.webrtc.Camera2Enumerator | 23 | +import livekit.org.webrtc.Camera1Enumerator |
| 24 | +import livekit.org.webrtc.Camera2Enumerator | ||
| 25 | 25 | ||
| 26 | object DeviceManager { | 26 | object DeviceManager { |
| 27 | 27 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -18,8 +18,5 @@ package io.livekit.android.room | @@ -18,8 +18,5 @@ package io.livekit.android.room | ||
| 18 | 18 | ||
| 19 | import kotlinx.serialization.Serializable | 19 | import kotlinx.serialization.Serializable |
| 20 | 20 | ||
| 21 | -/** | ||
| 22 | - * @suppress | ||
| 23 | - */ | ||
| 24 | @Serializable | 21 | @Serializable |
| 25 | -data class IceCandidateJSON(val candidate: String, val sdpMLineIndex: Int, val sdpMid: String?) | 22 | +internal data class IceCandidateJSON(val candidate: String, val sdpMLineIndex: Int, val sdpMid: String?) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -40,9 +40,9 @@ import kotlinx.coroutines.CoroutineDispatcher | @@ -40,9 +40,9 @@ import kotlinx.coroutines.CoroutineDispatcher | ||
| 40 | import kotlinx.coroutines.CoroutineScope | 40 | import kotlinx.coroutines.CoroutineScope |
| 41 | import kotlinx.coroutines.SupervisorJob | 41 | import kotlinx.coroutines.SupervisorJob |
| 42 | import kotlinx.coroutines.runBlocking | 42 | import kotlinx.coroutines.runBlocking |
| 43 | -import org.webrtc.* | ||
| 44 | -import org.webrtc.PeerConnection.RTCConfiguration | ||
| 45 | -import org.webrtc.PeerConnection.SignalingState | 43 | +import livekit.org.webrtc.* |
| 44 | +import livekit.org.webrtc.PeerConnection.RTCConfiguration | ||
| 45 | +import livekit.org.webrtc.PeerConnection.SignalingState | ||
| 46 | import java.util.concurrent.atomic.AtomicBoolean | 46 | import java.util.concurrent.atomic.AtomicBoolean |
| 47 | import javax.inject.Named | 47 | import javax.inject.Named |
| 48 | import kotlin.contracts.ExperimentalContracts | 48 | import kotlin.contracts.ExperimentalContracts |
| @@ -459,7 +459,7 @@ internal data class TrackBitrateInfo( | @@ -459,7 +459,7 @@ internal data class TrackBitrateInfo( | ||
| 459 | val maxBitrate: Long, | 459 | val maxBitrate: Long, |
| 460 | ) | 460 | ) |
| 461 | 461 | ||
| 462 | -sealed class TrackBitrateInfoKey { | 462 | +internal sealed class TrackBitrateInfoKey { |
| 463 | data class Cid(val value: String) : TrackBitrateInfoKey() | 463 | data class Cid(val value: String) : TrackBitrateInfoKey() |
| 464 | data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey() | 464 | data class Transceiver(val value: RtpTransceiver) : TrackBitrateInfoKey() |
| 465 | } | 465 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -19,19 +19,16 @@ package io.livekit.android.room | @@ -19,19 +19,16 @@ package io.livekit.android.room | ||
| 19 | import io.livekit.android.util.LKLog | 19 | import io.livekit.android.util.LKLog |
| 20 | import io.livekit.android.webrtc.peerconnection.executeOnRTCThread | 20 | import io.livekit.android.webrtc.peerconnection.executeOnRTCThread |
| 21 | import livekit.LivekitRtc | 21 | import livekit.LivekitRtc |
| 22 | -import org.webrtc.CandidatePairChangeEvent | ||
| 23 | -import org.webrtc.DataChannel | ||
| 24 | -import org.webrtc.IceCandidate | ||
| 25 | -import org.webrtc.MediaStream | ||
| 26 | -import org.webrtc.PeerConnection | ||
| 27 | -import org.webrtc.RtpReceiver | ||
| 28 | -import org.webrtc.RtpTransceiver | ||
| 29 | -import org.webrtc.SessionDescription | ||
| 30 | - | ||
| 31 | -/** | ||
| 32 | - * @suppress | ||
| 33 | - */ | ||
| 34 | -class PublisherTransportObserver( | 22 | +import livekit.org.webrtc.CandidatePairChangeEvent |
| 23 | +import livekit.org.webrtc.DataChannel | ||
| 24 | +import livekit.org.webrtc.IceCandidate | ||
| 25 | +import livekit.org.webrtc.MediaStream | ||
| 26 | +import livekit.org.webrtc.PeerConnection | ||
| 27 | +import livekit.org.webrtc.RtpReceiver | ||
| 28 | +import livekit.org.webrtc.RtpTransceiver | ||
| 29 | +import livekit.org.webrtc.SessionDescription | ||
| 30 | + | ||
| 31 | +internal class PublisherTransportObserver( | ||
| 35 | private val engine: RTCEngine, | 32 | private val engine: RTCEngine, |
| 36 | private val client: SignalClient, | 33 | private val client: SignalClient, |
| 37 | ) : PeerConnection.Observer, PeerConnectionTransport.Listener { | 34 | ) : PeerConnection.Observer, PeerConnectionTransport.Listener { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -44,9 +44,9 @@ import livekit.LivekitModels | @@ -44,9 +44,9 @@ import livekit.LivekitModels | ||
| 44 | import livekit.LivekitRtc | 44 | import livekit.LivekitRtc |
| 45 | import livekit.LivekitRtc.JoinResponse | 45 | import livekit.LivekitRtc.JoinResponse |
| 46 | import livekit.LivekitRtc.ReconnectResponse | 46 | import livekit.LivekitRtc.ReconnectResponse |
| 47 | -import org.webrtc.* | ||
| 48 | -import org.webrtc.PeerConnection.RTCConfiguration | ||
| 49 | -import org.webrtc.RtpTransceiver.RtpTransceiverInit | 47 | +import livekit.org.webrtc.* |
| 48 | +import livekit.org.webrtc.PeerConnection.RTCConfiguration | ||
| 49 | +import livekit.org.webrtc.RtpTransceiver.RtpTransceiverInit | ||
| 50 | import java.net.ConnectException | 50 | import java.net.ConnectException |
| 51 | import java.nio.ByteBuffer | 51 | import java.nio.ByteBuffer |
| 52 | import javax.inject.Inject | 52 | import javax.inject.Inject |
| @@ -266,6 +266,7 @@ internal constructor( | @@ -266,6 +266,7 @@ internal constructor( | ||
| 266 | cid: String, | 266 | cid: String, |
| 267 | name: String, | 267 | name: String, |
| 268 | kind: LivekitModels.TrackType, | 268 | kind: LivekitModels.TrackType, |
| 269 | + stream: String?, | ||
| 269 | builder: LivekitRtc.AddTrackRequest.Builder = LivekitRtc.AddTrackRequest.newBuilder(), | 270 | builder: LivekitRtc.AddTrackRequest.Builder = LivekitRtc.AddTrackRequest.newBuilder(), |
| 270 | ): LivekitModels.TrackInfo { | 271 | ): LivekitModels.TrackInfo { |
| 271 | if (pendingTrackResolvers[cid] != null) { | 272 | if (pendingTrackResolvers[cid] != null) { |
| @@ -274,7 +275,13 @@ internal constructor( | @@ -274,7 +275,13 @@ internal constructor( | ||
| 274 | // Suspend until signal client receives message confirming track publication. | 275 | // Suspend until signal client receives message confirming track publication. |
| 275 | return suspendCoroutine { cont -> | 276 | return suspendCoroutine { cont -> |
| 276 | pendingTrackResolvers[cid] = cont | 277 | pendingTrackResolvers[cid] = cont |
| 277 | - client.sendAddTrack(cid, name, kind, builder) | 278 | + client.sendAddTrack( |
| 279 | + cid = cid, | ||
| 280 | + name = name, | ||
| 281 | + type = kind, | ||
| 282 | + stream = stream, | ||
| 283 | + builder = builder, | ||
| 284 | + ) | ||
| 278 | } | 285 | } |
| 279 | } | 286 | } |
| 280 | 287 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -41,14 +41,19 @@ import io.livekit.android.room.participant.* | @@ -41,14 +41,19 @@ import io.livekit.android.room.participant.* | ||
| 41 | import io.livekit.android.room.track.* | 41 | import io.livekit.android.room.track.* |
| 42 | import io.livekit.android.util.FlowObservable | 42 | import io.livekit.android.util.FlowObservable |
| 43 | import io.livekit.android.util.LKLog | 43 | import io.livekit.android.util.LKLog |
| 44 | +import io.livekit.android.util.flow | ||
| 44 | import io.livekit.android.util.flowDelegate | 45 | import io.livekit.android.util.flowDelegate |
| 45 | import io.livekit.android.util.invoke | 46 | import io.livekit.android.util.invoke |
| 46 | import io.livekit.android.webrtc.getFilteredStats | 47 | import io.livekit.android.webrtc.getFilteredStats |
| 47 | import kotlinx.coroutines.* | 48 | import kotlinx.coroutines.* |
| 49 | +import kotlinx.coroutines.flow.filterNotNull | ||
| 50 | +import kotlinx.coroutines.flow.first | ||
| 51 | +import kotlinx.serialization.Serializable | ||
| 48 | import livekit.LivekitModels | 52 | import livekit.LivekitModels |
| 49 | import livekit.LivekitRtc | 53 | import livekit.LivekitRtc |
| 50 | -import org.webrtc.* | 54 | +import livekit.org.webrtc.* |
| 51 | import javax.inject.Named | 55 | import javax.inject.Named |
| 56 | +import kotlin.jvm.Throws | ||
| 52 | 57 | ||
| 53 | class Room | 58 | class Room |
| 54 | @AssistedInject | 59 | @AssistedInject |
| @@ -92,15 +97,35 @@ constructor( | @@ -92,15 +97,35 @@ constructor( | ||
| 92 | SERVER_LEAVE, | 97 | SERVER_LEAVE, |
| 93 | } | 98 | } |
| 94 | 99 | ||
| 100 | + @Serializable | ||
| 95 | @JvmInline | 101 | @JvmInline |
| 96 | value class Sid(val sid: String) | 102 | value class Sid(val sid: String) |
| 97 | 103 | ||
| 98 | @Deprecated("Use events instead.") | 104 | @Deprecated("Use events instead.") |
| 99 | var listener: RoomListener? = null | 105 | var listener: RoomListener? = null |
| 100 | 106 | ||
| 107 | + /** | ||
| 108 | + * The session id of the room. | ||
| 109 | + * | ||
| 110 | + * Note: the sid may not be populated immediately upon [connect], | ||
| 111 | + * so using the suspend function [getSid] or listening to the flow | ||
| 112 | + * `room::sid.flow` is highly advised. | ||
| 113 | + */ | ||
| 101 | @FlowObservable | 114 | @FlowObservable |
| 102 | @get:FlowObservable | 115 | @get:FlowObservable |
| 103 | var sid: Sid? by flowDelegate(null) | 116 | var sid: Sid? by flowDelegate(null) |
| 117 | + private set | ||
| 118 | + | ||
| 119 | + /** | ||
| 120 | + * Gets the sid of the room. | ||
| 121 | + * | ||
| 122 | + * If the sid is not yet available, will suspend until received. | ||
| 123 | + */ | ||
| 124 | + suspend fun getSid(): Sid { | ||
| 125 | + return this@Room::sid.flow | ||
| 126 | + .filterNotNull() | ||
| 127 | + .first() | ||
| 128 | + } | ||
| 104 | 129 | ||
| 105 | @FlowObservable | 130 | @FlowObservable |
| 106 | @get:FlowObservable | 131 | @get:FlowObservable |
| @@ -193,13 +218,15 @@ constructor( | @@ -193,13 +218,15 @@ constructor( | ||
| 193 | internalListener = this@Room | 218 | internalListener = this@Room |
| 194 | } | 219 | } |
| 195 | 220 | ||
| 196 | - private var mutableRemoteParticipants by flowDelegate(emptyMap<String, RemoteParticipant>()) | 221 | + private var mutableRemoteParticipants by flowDelegate(emptyMap<Participant.Identity, RemoteParticipant>()) |
| 197 | 222 | ||
| 198 | @FlowObservable | 223 | @FlowObservable |
| 199 | @get:FlowObservable | 224 | @get:FlowObservable |
| 200 | - val remoteParticipants: Map<String, RemoteParticipant> | 225 | + val remoteParticipants: Map<Participant.Identity, RemoteParticipant> |
| 201 | get() = mutableRemoteParticipants | 226 | get() = mutableRemoteParticipants |
| 202 | 227 | ||
| 228 | + private var sidToIdentity = mutableMapOf<Participant.Sid, Participant.Identity>() | ||
| 229 | + | ||
| 203 | private var mutableActiveSpeakers by flowDelegate(emptyList<Participant>()) | 230 | private var mutableActiveSpeakers by flowDelegate(emptyList<Participant>()) |
| 204 | 231 | ||
| 205 | @FlowObservable | 232 | @FlowObservable |
| @@ -221,6 +248,14 @@ constructor( | @@ -221,6 +248,14 @@ constructor( | ||
| 221 | e2eeOptions = e2eeOptions, | 248 | e2eeOptions = e2eeOptions, |
| 222 | ) | 249 | ) |
| 223 | 250 | ||
| 251 | + /** | ||
| 252 | + * Connect to a LiveKit Room. | ||
| 253 | + * | ||
| 254 | + * @param url | ||
| 255 | + * @param token | ||
| 256 | + * @param options | ||
| 257 | + */ | ||
| 258 | + @Throws(Exception::class) | ||
| 224 | suspend fun connect(url: String, token: String, options: ConnectOptions = ConnectOptions()) { | 259 | suspend fun connect(url: String, token: String, options: ConnectOptions = ConnectOptions()) { |
| 225 | if (this::coroutineScope.isInitialized) { | 260 | if (this::coroutineScope.isInitialized) { |
| 226 | coroutineScope.cancel() | 261 | coroutineScope.cancel() |
| @@ -333,7 +368,11 @@ constructor( | @@ -333,7 +368,11 @@ constructor( | ||
| 333 | override fun onJoinResponse(response: LivekitRtc.JoinResponse) { | 368 | override fun onJoinResponse(response: LivekitRtc.JoinResponse) { |
| 334 | LKLog.i { "Connected to server, server version: ${response.serverVersion}, client version: ${Version.CLIENT_VERSION}" } | 369 | LKLog.i { "Connected to server, server version: ${response.serverVersion}, client version: ${Version.CLIENT_VERSION}" } |
| 335 | 370 | ||
| 336 | - sid = Sid(response.room.sid) | 371 | + if (response.room.sid != null) { |
| 372 | + sid = Sid(response.room.sid) | ||
| 373 | + } else { | ||
| 374 | + sid = null | ||
| 375 | + } | ||
| 337 | name = response.room.name | 376 | name = response.room.name |
| 338 | metadata = response.room.metadata | 377 | metadata = response.room.metadata |
| 339 | 378 | ||
| @@ -354,16 +393,16 @@ constructor( | @@ -354,16 +393,16 @@ constructor( | ||
| 354 | localParticipant.updateFromInfo(response.participant) | 393 | localParticipant.updateFromInfo(response.participant) |
| 355 | 394 | ||
| 356 | if (response.otherParticipantsList.isNotEmpty()) { | 395 | if (response.otherParticipantsList.isNotEmpty()) { |
| 357 | - response.otherParticipantsList.forEach { | ||
| 358 | - getOrCreateRemoteParticipant(it.sid, it) | 396 | + response.otherParticipantsList.forEach { info -> |
| 397 | + getOrCreateRemoteParticipant(Participant.Identity(info.identity), info) | ||
| 359 | } | 398 | } |
| 360 | } | 399 | } |
| 361 | } | 400 | } |
| 362 | 401 | ||
| 363 | - private fun handleParticipantDisconnect(sid: String) { | 402 | + private fun handleParticipantDisconnect(identity: Participant.Identity) { |
| 364 | val newParticipants = mutableRemoteParticipants.toMutableMap() | 403 | val newParticipants = mutableRemoteParticipants.toMutableMap() |
| 365 | - val removedParticipant = newParticipants.remove(sid) ?: return | ||
| 366 | - removedParticipant.tracks.values.toList().forEach { publication -> | 404 | + val removedParticipant = newParticipants.remove(identity) ?: return |
| 405 | + removedParticipant.trackPublications.values.toList().forEach { publication -> | ||
| 367 | removedParticipant.unpublishTrack(publication.sid, true) | 406 | removedParticipant.unpublishTrack(publication.sid, true) |
| 368 | } | 407 | } |
| 369 | 408 | ||
| @@ -372,29 +411,41 @@ constructor( | @@ -372,29 +411,41 @@ constructor( | ||
| 372 | eventBus.postEvent(RoomEvent.ParticipantDisconnected(this, removedParticipant), coroutineScope) | 411 | eventBus.postEvent(RoomEvent.ParticipantDisconnected(this, removedParticipant), coroutineScope) |
| 373 | } | 412 | } |
| 374 | 413 | ||
| 375 | - fun getParticipant(sid: String): Participant? { | 414 | + fun getParticipantBySid(sid: String): Participant? { |
| 415 | + return getParticipantBySid(Participant.Sid(sid)) | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + fun getParticipantBySid(sid: Participant.Sid): Participant? { | ||
| 376 | if (sid == localParticipant.sid) { | 419 | if (sid == localParticipant.sid) { |
| 377 | return localParticipant | 420 | return localParticipant |
| 378 | } else { | 421 | } else { |
| 379 | - return remoteParticipants[sid] | 422 | + return remoteParticipants[sidToIdentity[sid]] |
| 423 | + } | ||
| 424 | + } | ||
| 425 | + | ||
| 426 | + fun getParticipantByIdentity(identity: String): Participant? { | ||
| 427 | + return getParticipantByIdentity(Participant.Identity(identity)) | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + fun getParticipantByIdentity(identity: Participant.Identity): Participant? { | ||
| 431 | + if (identity == localParticipant.identity) { | ||
| 432 | + return localParticipant | ||
| 433 | + } else { | ||
| 434 | + return remoteParticipants[identity] | ||
| 380 | } | 435 | } |
| 381 | } | 436 | } |
| 382 | 437 | ||
| 383 | @Synchronized | 438 | @Synchronized |
| 384 | private fun getOrCreateRemoteParticipant( | 439 | private fun getOrCreateRemoteParticipant( |
| 385 | - sid: String, | ||
| 386 | - info: LivekitModels.ParticipantInfo? = null, | 440 | + identity: Participant.Identity, |
| 441 | + info: LivekitModels.ParticipantInfo, | ||
| 387 | ): RemoteParticipant { | 442 | ): RemoteParticipant { |
| 388 | - var participant = remoteParticipants[sid] | 443 | + var participant = remoteParticipants[identity] |
| 389 | if (participant != null) { | 444 | if (participant != null) { |
| 390 | return participant | 445 | return participant |
| 391 | } | 446 | } |
| 392 | 447 | ||
| 393 | - participant = if (info != null) { | ||
| 394 | - RemoteParticipant(info, engine.client, ioDispatcher, defaultDispatcher) | ||
| 395 | - } else { | ||
| 396 | - RemoteParticipant(sid, null, engine.client, ioDispatcher, defaultDispatcher) | ||
| 397 | - } | 448 | + participant = RemoteParticipant(info, engine.client, ioDispatcher, defaultDispatcher) |
| 398 | participant.internalListener = this | 449 | participant.internalListener = this |
| 399 | 450 | ||
| 400 | coroutineScope.launch { | 451 | coroutineScope.launch { |
| @@ -466,26 +517,25 @@ constructor( | @@ -466,26 +517,25 @@ constructor( | ||
| 466 | } | 517 | } |
| 467 | } | 518 | } |
| 468 | 519 | ||
| 469 | - if (info != null) { | ||
| 470 | - participant.updateFromInfo(info) | ||
| 471 | - } | 520 | + participant.updateFromInfo(info) |
| 472 | 521 | ||
| 473 | val newRemoteParticipants = mutableRemoteParticipants.toMutableMap() | 522 | val newRemoteParticipants = mutableRemoteParticipants.toMutableMap() |
| 474 | - newRemoteParticipants[sid] = participant | 523 | + newRemoteParticipants[identity] = participant |
| 475 | mutableRemoteParticipants = newRemoteParticipants | 524 | mutableRemoteParticipants = newRemoteParticipants |
| 525 | + sidToIdentity[participant.sid] = identity | ||
| 476 | 526 | ||
| 477 | return participant | 527 | return participant |
| 478 | } | 528 | } |
| 479 | 529 | ||
| 480 | private fun handleActiveSpeakersUpdate(speakerInfos: List<LivekitModels.SpeakerInfo>) { | 530 | private fun handleActiveSpeakersUpdate(speakerInfos: List<LivekitModels.SpeakerInfo>) { |
| 481 | val speakers = mutableListOf<Participant>() | 531 | val speakers = mutableListOf<Participant>() |
| 482 | - val seenSids = mutableSetOf<String>() | 532 | + val seenSids = mutableSetOf<Participant.Sid>() |
| 483 | val localParticipant = localParticipant | 533 | val localParticipant = localParticipant |
| 484 | speakerInfos.forEach { speakerInfo -> | 534 | speakerInfos.forEach { speakerInfo -> |
| 485 | - val speakerSid = speakerInfo.sid!! | 535 | + val speakerSid = Participant.Sid(speakerInfo.sid) |
| 486 | seenSids.add(speakerSid) | 536 | seenSids.add(speakerSid) |
| 487 | 537 | ||
| 488 | - val participant = getParticipant(speakerSid) ?: return@forEach | 538 | + val participant = getParticipantBySid(speakerSid) ?: return@forEach |
| 489 | participant.audioLevel = speakerInfo.level | 539 | participant.audioLevel = speakerInfo.level |
| 490 | participant.isSpeaking = true | 540 | participant.isSpeaking = true |
| 491 | speakers.add(participant) | 541 | speakers.add(participant) |
| @@ -508,21 +558,22 @@ constructor( | @@ -508,21 +558,22 @@ constructor( | ||
| 508 | } | 558 | } |
| 509 | 559 | ||
| 510 | private fun handleSpeakersChanged(speakerInfos: List<LivekitModels.SpeakerInfo>) { | 560 | private fun handleSpeakersChanged(speakerInfos: List<LivekitModels.SpeakerInfo>) { |
| 511 | - val updatedSpeakers = mutableMapOf<String, Participant>() | ||
| 512 | - activeSpeakers.forEach { | ||
| 513 | - updatedSpeakers[it.sid] = it | 561 | + val updatedSpeakers = mutableMapOf<Participant.Sid, Participant>() |
| 562 | + activeSpeakers.forEach { participant -> | ||
| 563 | + updatedSpeakers[participant.sid] = participant | ||
| 514 | } | 564 | } |
| 515 | 565 | ||
| 516 | speakerInfos.forEach { speaker -> | 566 | speakerInfos.forEach { speaker -> |
| 517 | - val participant = getParticipant(speaker.sid) ?: return@forEach | 567 | + val speakerSid = Participant.Sid(speaker.sid) |
| 568 | + val participant = getParticipantBySid(speakerSid) ?: return@forEach | ||
| 518 | 569 | ||
| 519 | participant.audioLevel = speaker.level | 570 | participant.audioLevel = speaker.level |
| 520 | participant.isSpeaking = speaker.active | 571 | participant.isSpeaking = speaker.active |
| 521 | 572 | ||
| 522 | if (speaker.active) { | 573 | if (speaker.active) { |
| 523 | - updatedSpeakers[speaker.sid] = participant | 574 | + updatedSpeakers[speakerSid] = participant |
| 524 | } else { | 575 | } else { |
| 525 | - updatedSpeakers.remove(speaker.sid) | 576 | + updatedSpeakers.remove(speakerSid) |
| 526 | } | 577 | } |
| 527 | } | 578 | } |
| 528 | 579 | ||
| @@ -555,6 +606,7 @@ constructor( | @@ -555,6 +606,7 @@ constructor( | ||
| 555 | metadata = null | 606 | metadata = null |
| 556 | name = null | 607 | name = null |
| 557 | isRecording = false | 608 | isRecording = false |
| 609 | + sidToIdentity.clear() | ||
| 558 | } | 610 | } |
| 559 | 611 | ||
| 560 | private fun handleDisconnect(reason: DisconnectReason) { | 612 | private fun handleDisconnect(reason: DisconnectReason) { |
| @@ -590,8 +642,8 @@ constructor( | @@ -590,8 +642,8 @@ constructor( | ||
| 590 | val participantTracksList = mutableListOf<LivekitModels.ParticipantTracks>() | 642 | val participantTracksList = mutableListOf<LivekitModels.ParticipantTracks>() |
| 591 | for (participant in remoteParticipants.values) { | 643 | for (participant in remoteParticipants.values) { |
| 592 | val builder = LivekitModels.ParticipantTracks.newBuilder() | 644 | val builder = LivekitModels.ParticipantTracks.newBuilder() |
| 593 | - builder.participantSid = participant.sid | ||
| 594 | - for (trackPub in participant.tracks.values) { | 645 | + builder.participantSid = participant.sid.value |
| 646 | + for (trackPub in participant.trackPublications.values) { | ||
| 595 | val remoteTrackPub = (trackPub as? RemoteTrackPublication) ?: continue | 647 | val remoteTrackPub = (trackPub as? RemoteTrackPublication) ?: continue |
| 596 | if (remoteTrackPub.subscribed != sendUnsub) { | 648 | if (remoteTrackPub.subscribed != sendUnsub) { |
| 597 | builder.addTrackSids(remoteTrackPub.sid) | 649 | builder.addTrackSids(remoteTrackPub.sid) |
| @@ -712,11 +764,19 @@ constructor( | @@ -712,11 +764,19 @@ constructor( | ||
| 712 | return | 764 | return |
| 713 | } | 765 | } |
| 714 | 766 | ||
| 715 | - var (participantSid, trackSid) = unpackStreamId(streams.first().id) | ||
| 716 | - if (trackSid == null) { | ||
| 717 | - trackSid = track.id() | 767 | + var (participantSid, streamId) = unpackStreamId(streams.first().id) |
| 768 | + var trackSid = track.id() | ||
| 769 | + | ||
| 770 | + if (streamId != null && streamId.startsWith("TR")) { | ||
| 771 | + trackSid = streamId | ||
| 718 | } | 772 | } |
| 719 | - val participant = getOrCreateRemoteParticipant(participantSid) | 773 | + val participant = getParticipantBySid(participantSid) as? RemoteParticipant |
| 774 | + | ||
| 775 | + if (participant == null) { | ||
| 776 | + LKLog.e { "Tried to add a track for a participant that is not present. sid: $participantSid" } | ||
| 777 | + return | ||
| 778 | + } | ||
| 779 | + | ||
| 720 | val statsGetter = engine.createStatsGetter(receiver) | 780 | val statsGetter = engine.createStatsGetter(receiver) |
| 721 | participant.addSubscribedMediaTrack( | 781 | participant.addSubscribedMediaTrack( |
| 722 | track, | 782 | track, |
| @@ -732,24 +792,37 @@ constructor( | @@ -732,24 +792,37 @@ constructor( | ||
| 732 | */ | 792 | */ |
| 733 | override fun onUpdateParticipants(updates: List<LivekitModels.ParticipantInfo>) { | 793 | override fun onUpdateParticipants(updates: List<LivekitModels.ParticipantInfo>) { |
| 734 | for (info in updates) { | 794 | for (info in updates) { |
| 735 | - val participantSid = info.sid | 795 | + val participantSid = Participant.Sid(info.sid) |
| 796 | + // LiveKit server doesn't send identity info prior to version 1.5.2 in disconnect updates | ||
| 797 | + // so we try to map an empty identity to an already known sID manually | ||
| 798 | + | ||
| 799 | + @Suppress("NAME_SHADOWING") var info = info | ||
| 800 | + if (info.identity.isNullOrBlank()) { | ||
| 801 | + info = with(info.toBuilder()) { | ||
| 802 | + identity = sidToIdentity[participantSid]?.value ?: "" | ||
| 803 | + build() | ||
| 804 | + } | ||
| 805 | + } | ||
| 806 | + | ||
| 807 | + val participantIdentity = Participant.Identity(info.identity) | ||
| 736 | 808 | ||
| 737 | - if (localParticipant.sid == participantSid) { | 809 | + if (localParticipant.identity == participantIdentity) { |
| 738 | localParticipant.updateFromInfo(info) | 810 | localParticipant.updateFromInfo(info) |
| 739 | continue | 811 | continue |
| 740 | } | 812 | } |
| 741 | 813 | ||
| 742 | - val isNewParticipant = !remoteParticipants.contains(participantSid) | 814 | + val isNewParticipant = !remoteParticipants.contains(participantIdentity) |
| 743 | 815 | ||
| 744 | if (info.state == LivekitModels.ParticipantInfo.State.DISCONNECTED) { | 816 | if (info.state == LivekitModels.ParticipantInfo.State.DISCONNECTED) { |
| 745 | - handleParticipantDisconnect(participantSid) | 817 | + handleParticipantDisconnect(participantIdentity) |
| 746 | } else { | 818 | } else { |
| 747 | - val participant = getOrCreateRemoteParticipant(participantSid, info) | 819 | + val participant = getOrCreateRemoteParticipant(participantIdentity, info) |
| 748 | if (isNewParticipant) { | 820 | if (isNewParticipant) { |
| 749 | listener?.onParticipantConnected(this, participant) | 821 | listener?.onParticipantConnected(this, participant) |
| 750 | eventBus.postEvent(RoomEvent.ParticipantConnected(this, participant), coroutineScope) | 822 | eventBus.postEvent(RoomEvent.ParticipantConnected(this, participant), coroutineScope) |
| 751 | } else { | 823 | } else { |
| 752 | participant.updateFromInfo(info) | 824 | participant.updateFromInfo(info) |
| 825 | + sidToIdentity[participantSid] = participantIdentity | ||
| 753 | } | 826 | } |
| 754 | } | 827 | } |
| 755 | } | 828 | } |
| @@ -773,6 +846,9 @@ constructor( | @@ -773,6 +846,9 @@ constructor( | ||
| 773 | * @suppress | 846 | * @suppress |
| 774 | */ | 847 | */ |
| 775 | override fun onRoomUpdate(update: LivekitModels.Room) { | 848 | override fun onRoomUpdate(update: LivekitModels.Room) { |
| 849 | + if (update.sid != null) { | ||
| 850 | + sid = Sid(update.sid) | ||
| 851 | + } | ||
| 776 | val oldMetadata = metadata | 852 | val oldMetadata = metadata |
| 777 | metadata = update.metadata | 853 | metadata = update.metadata |
| 778 | 854 | ||
| @@ -794,7 +870,7 @@ constructor( | @@ -794,7 +870,7 @@ constructor( | ||
| 794 | override fun onConnectionQuality(updates: List<LivekitRtc.ConnectionQualityInfo>) { | 870 | override fun onConnectionQuality(updates: List<LivekitRtc.ConnectionQualityInfo>) { |
| 795 | updates.forEach { info -> | 871 | updates.forEach { info -> |
| 796 | val quality = ConnectionQuality.fromProto(info.quality) | 872 | val quality = ConnectionQuality.fromProto(info.quality) |
| 797 | - val participant = getParticipant(info.participantSid) ?: return | 873 | + val participant = getParticipantBySid(info.participantSid) ?: return |
| 798 | participant.connectionQuality = quality | 874 | participant.connectionQuality = quality |
| 799 | listener?.onConnectionQualityChanged(participant, quality) | 875 | listener?.onConnectionQualityChanged(participant, quality) |
| 800 | eventBus.postEvent(RoomEvent.ConnectionQualityChanged(this, participant, quality), coroutineScope) | 876 | eventBus.postEvent(RoomEvent.ConnectionQualityChanged(this, participant, quality), coroutineScope) |
| @@ -812,7 +888,7 @@ constructor( | @@ -812,7 +888,7 @@ constructor( | ||
| 812 | * @suppress | 888 | * @suppress |
| 813 | */ | 889 | */ |
| 814 | override fun onUserPacket(packet: LivekitModels.UserPacket, kind: LivekitModels.DataPacket.Kind) { | 890 | override fun onUserPacket(packet: LivekitModels.UserPacket, kind: LivekitModels.DataPacket.Kind) { |
| 815 | - val participant = remoteParticipants[packet.participantSid] | 891 | + val participant = getParticipantBySid(packet.participantSid) as? RemoteParticipant |
| 816 | val data = packet.payload.toByteArray() | 892 | val data = packet.payload.toByteArray() |
| 817 | val topic = if (packet.hasTopic()) { | 893 | val topic = if (packet.hasTopic()) { |
| 818 | packet.topic | 894 | packet.topic |
| @@ -830,8 +906,8 @@ constructor( | @@ -830,8 +906,8 @@ constructor( | ||
| 830 | */ | 906 | */ |
| 831 | override fun onStreamStateUpdate(streamStates: List<LivekitRtc.StreamStateInfo>) { | 907 | override fun onStreamStateUpdate(streamStates: List<LivekitRtc.StreamStateInfo>) { |
| 832 | for (streamState in streamStates) { | 908 | for (streamState in streamStates) { |
| 833 | - val participant = getParticipant(streamState.participantSid) ?: continue | ||
| 834 | - val track = participant.tracks[streamState.trackSid] ?: continue | 909 | + val participant = getParticipantBySid(streamState.participantSid) ?: continue |
| 910 | + val track = participant.trackPublications[streamState.trackSid] ?: continue | ||
| 835 | 911 | ||
| 836 | track.track?.streamState = Track.StreamState.fromProto(streamState.state) | 912 | track.track?.streamState = Track.StreamState.fromProto(streamState.state) |
| 837 | } | 913 | } |
| @@ -848,7 +924,7 @@ constructor( | @@ -848,7 +924,7 @@ constructor( | ||
| 848 | * @suppress | 924 | * @suppress |
| 849 | */ | 925 | */ |
| 850 | override fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) { | 926 | override fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) { |
| 851 | - val participant = getParticipant(subscriptionPermissionUpdate.participantSid) as? RemoteParticipant ?: return | 927 | + val participant = getParticipantBySid(subscriptionPermissionUpdate.participantSid) as? RemoteParticipant ?: return |
| 852 | participant.onSubscriptionPermissionUpdate(subscriptionPermissionUpdate) | 928 | participant.onSubscriptionPermissionUpdate(subscriptionPermissionUpdate) |
| 853 | } | 929 | } |
| 854 | 930 | ||
| @@ -885,7 +961,7 @@ constructor( | @@ -885,7 +961,7 @@ constructor( | ||
| 885 | override fun onFullReconnecting() { | 961 | override fun onFullReconnecting() { |
| 886 | localParticipant.prepareForFullReconnect() | 962 | localParticipant.prepareForFullReconnect() |
| 887 | remoteParticipants.keys.toMutableSet() // copy keys to avoid concurrent modifications. | 963 | remoteParticipants.keys.toMutableSet() // copy keys to avoid concurrent modifications. |
| 888 | - .forEach { sid -> handleParticipantDisconnect(sid) } | 964 | + .forEach { identity -> handleParticipantDisconnect(identity) } |
| 889 | } | 965 | } |
| 890 | 966 | ||
| 891 | /** | 967 | /** |
| @@ -897,7 +973,7 @@ constructor( | @@ -897,7 +973,7 @@ constructor( | ||
| 897 | } else { | 973 | } else { |
| 898 | val remoteParticipants = remoteParticipants.values.toList() | 974 | val remoteParticipants = remoteParticipants.values.toList() |
| 899 | for (participant in remoteParticipants) { | 975 | for (participant in remoteParticipants) { |
| 900 | - val pubs = participant.tracks.values.toList() | 976 | + val pubs = participant.trackPublications.values.toList() |
| 901 | for (pub in pubs) { | 977 | for (pub in pubs) { |
| 902 | val remotePub = pub as? RemoteTrackPublication ?: continue | 978 | val remotePub = pub as? RemoteTrackPublication ?: continue |
| 903 | if (remotePub.subscribed) { | 979 | if (remotePub.subscribed) { |
| @@ -1091,8 +1167,6 @@ interface RoomListener { | @@ -1091,8 +1167,6 @@ interface RoomListener { | ||
| 1091 | * Could not connect to the room | 1167 | * Could not connect to the room |
| 1092 | */ | 1168 | */ |
| 1093 | fun onFailedToConnect(room: Room, error: Throwable) {} | 1169 | fun onFailedToConnect(room: Room, error: Throwable) {} |
| 1094 | -// fun onReconnecting(room: Room, error: Exception) {} | ||
| 1095 | -// fun onReconnect(room: Room) {} | ||
| 1096 | 1170 | ||
| 1097 | /** | 1171 | /** |
| 1098 | * Active speakers changed. List of speakers are ordered by their audio level. loudest | 1172 | * Active speakers changed. List of speakers are ordered by their audio level. loudest |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -37,12 +37,12 @@ import livekit.LivekitModels | @@ -37,12 +37,12 @@ import livekit.LivekitModels | ||
| 37 | import livekit.LivekitRtc | 37 | import livekit.LivekitRtc |
| 38 | import livekit.LivekitRtc.JoinResponse | 38 | import livekit.LivekitRtc.JoinResponse |
| 39 | import livekit.LivekitRtc.ReconnectResponse | 39 | import livekit.LivekitRtc.ReconnectResponse |
| 40 | +import livekit.org.webrtc.IceCandidate | ||
| 41 | +import livekit.org.webrtc.PeerConnection | ||
| 42 | +import livekit.org.webrtc.SessionDescription | ||
| 40 | import okhttp3.* | 43 | import okhttp3.* |
| 41 | import okio.ByteString | 44 | import okio.ByteString |
| 42 | import okio.ByteString.Companion.toByteString | 45 | import okio.ByteString.Companion.toByteString |
| 43 | -import org.webrtc.IceCandidate | ||
| 44 | -import org.webrtc.PeerConnection | ||
| 45 | -import org.webrtc.SessionDescription | ||
| 46 | import java.util.* | 46 | import java.util.* |
| 47 | import javax.inject.Inject | 47 | import javax.inject.Inject |
| 48 | import javax.inject.Named | 48 | import javax.inject.Named |
| @@ -116,7 +116,7 @@ constructor( | @@ -116,7 +116,7 @@ constructor( | ||
| 116 | /** | 116 | /** |
| 117 | * @throws Exception if fails to connect. | 117 | * @throws Exception if fails to connect. |
| 118 | */ | 118 | */ |
| 119 | - suspend fun reconnect(url: String, token: String, participantSid: String?): Either<ReconnectResponse, Unit> { | 119 | + internal suspend fun reconnect(url: String, token: String, participantSid: String?): Either<ReconnectResponse, Unit> { |
| 120 | val reconnectResponse = connect( | 120 | val reconnectResponse = connect( |
| 121 | url, | 121 | url, |
| 122 | token, | 122 | token, |
| @@ -385,14 +385,21 @@ constructor( | @@ -385,14 +385,21 @@ constructor( | ||
| 385 | cid: String, | 385 | cid: String, |
| 386 | name: String, | 386 | name: String, |
| 387 | type: LivekitModels.TrackType, | 387 | type: LivekitModels.TrackType, |
| 388 | + stream: String?, | ||
| 388 | builder: LivekitRtc.AddTrackRequest.Builder = LivekitRtc.AddTrackRequest.newBuilder(), | 389 | builder: LivekitRtc.AddTrackRequest.Builder = LivekitRtc.AddTrackRequest.newBuilder(), |
| 389 | ) { | 390 | ) { |
| 390 | val encryptionType = lastRoomOptions?.e2eeOptions?.encryptionType ?: LivekitModels.Encryption.Type.NONE | 391 | val encryptionType = lastRoomOptions?.e2eeOptions?.encryptionType ?: LivekitModels.Encryption.Type.NONE |
| 391 | - val addTrackRequest = builder | ||
| 392 | - .setCid(cid) | ||
| 393 | - .setName(name) | ||
| 394 | - .setType(type) | ||
| 395 | - .setEncryption(encryptionType) | 392 | + val addTrackRequest = builder.apply { |
| 393 | + setCid(cid) | ||
| 394 | + setName(name) | ||
| 395 | + setType(type) | ||
| 396 | + if (stream != null) { | ||
| 397 | + setStream(stream) | ||
| 398 | + } else { | ||
| 399 | + clearStream() | ||
| 400 | + } | ||
| 401 | + encryption = encryptionType | ||
| 402 | + } | ||
| 396 | val request = LivekitRtc.SignalRequest.newBuilder() | 403 | val request = LivekitRtc.SignalRequest.newBuilder() |
| 397 | .setAddTrack(addTrackRequest) | 404 | .setAddTrack(addTrackRequest) |
| 398 | .build() | 405 | .build() |
| @@ -834,4 +841,7 @@ enum class ProtocolVersion(val value: Int) { | @@ -834,4 +841,7 @@ enum class ProtocolVersion(val value: Int) { | ||
| 834 | v7(7), | 841 | v7(7), |
| 835 | v8(8), | 842 | v8(8), |
| 836 | v9(9), | 843 | v9(9), |
| 844 | + v10(10), | ||
| 845 | + v11(11), | ||
| 846 | + v12(12), | ||
| 837 | } | 847 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -19,14 +19,14 @@ package io.livekit.android.room | @@ -19,14 +19,14 @@ package io.livekit.android.room | ||
| 19 | import io.livekit.android.util.LKLog | 19 | import io.livekit.android.util.LKLog |
| 20 | import io.livekit.android.webrtc.peerconnection.executeOnRTCThread | 20 | import io.livekit.android.webrtc.peerconnection.executeOnRTCThread |
| 21 | import livekit.LivekitRtc | 21 | import livekit.LivekitRtc |
| 22 | -import org.webrtc.CandidatePairChangeEvent | ||
| 23 | -import org.webrtc.DataChannel | ||
| 24 | -import org.webrtc.IceCandidate | ||
| 25 | -import org.webrtc.MediaStream | ||
| 26 | -import org.webrtc.MediaStreamTrack | ||
| 27 | -import org.webrtc.PeerConnection | ||
| 28 | -import org.webrtc.RtpReceiver | ||
| 29 | -import org.webrtc.RtpTransceiver | 22 | +import livekit.org.webrtc.CandidatePairChangeEvent |
| 23 | +import livekit.org.webrtc.DataChannel | ||
| 24 | +import livekit.org.webrtc.IceCandidate | ||
| 25 | +import livekit.org.webrtc.MediaStream | ||
| 26 | +import livekit.org.webrtc.MediaStreamTrack | ||
| 27 | +import livekit.org.webrtc.PeerConnection | ||
| 28 | +import livekit.org.webrtc.RtpReceiver | ||
| 29 | +import livekit.org.webrtc.RtpTransceiver | ||
| 30 | 30 | ||
| 31 | /** | 31 | /** |
| 32 | * @suppress | 32 | * @suppress |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -41,8 +41,8 @@ import livekit.LivekitModels | @@ -41,8 +41,8 @@ import livekit.LivekitModels | ||
| 41 | import livekit.LivekitRtc | 41 | import livekit.LivekitRtc |
| 42 | import livekit.LivekitRtc.AddTrackRequest | 42 | import livekit.LivekitRtc.AddTrackRequest |
| 43 | import livekit.LivekitRtc.SimulcastCodec | 43 | import livekit.LivekitRtc.SimulcastCodec |
| 44 | -import org.webrtc.* | ||
| 45 | -import org.webrtc.RtpTransceiver.RtpTransceiverInit | 44 | +import livekit.org.webrtc.* |
| 45 | +import livekit.org.webrtc.RtpTransceiver.RtpTransceiverInit | ||
| 46 | import javax.inject.Named | 46 | import javax.inject.Named |
| 47 | import kotlin.math.max | 47 | import kotlin.math.max |
| 48 | 48 | ||
| @@ -62,16 +62,16 @@ internal constructor( | @@ -62,16 +62,16 @@ internal constructor( | ||
| 62 | coroutineDispatcher: CoroutineDispatcher, | 62 | coroutineDispatcher: CoroutineDispatcher, |
| 63 | @Named(InjectionNames.SENDER) | 63 | @Named(InjectionNames.SENDER) |
| 64 | private val capabilitiesGetter: CapabilitiesGetter, | 64 | private val capabilitiesGetter: CapabilitiesGetter, |
| 65 | -) : Participant("", null, coroutineDispatcher) { | 65 | +) : Participant(Sid(""), null, coroutineDispatcher) { |
| 66 | 66 | ||
| 67 | var audioTrackCaptureDefaults: LocalAudioTrackOptions by defaultsManager::audioTrackCaptureDefaults | 67 | var audioTrackCaptureDefaults: LocalAudioTrackOptions by defaultsManager::audioTrackCaptureDefaults |
| 68 | var audioTrackPublishDefaults: AudioTrackPublishDefaults by defaultsManager::audioTrackPublishDefaults | 68 | var audioTrackPublishDefaults: AudioTrackPublishDefaults by defaultsManager::audioTrackPublishDefaults |
| 69 | var videoTrackCaptureDefaults: LocalVideoTrackOptions by defaultsManager::videoTrackCaptureDefaults | 69 | var videoTrackCaptureDefaults: LocalVideoTrackOptions by defaultsManager::videoTrackCaptureDefaults |
| 70 | var videoTrackPublishDefaults: VideoTrackPublishDefaults by defaultsManager::videoTrackPublishDefaults | 70 | var videoTrackPublishDefaults: VideoTrackPublishDefaults by defaultsManager::videoTrackPublishDefaults |
| 71 | 71 | ||
| 72 | - var republishes: List<LocalTrackPublication>? = null | 72 | + private var republishes: List<LocalTrackPublication>? = null |
| 73 | private val localTrackPublications | 73 | private val localTrackPublications |
| 74 | - get() = tracks.values | 74 | + get() = trackPublications.values |
| 75 | .mapNotNull { it as? LocalTrackPublication } | 75 | .mapNotNull { it as? LocalTrackPublication } |
| 76 | .toList() | 76 | .toList() |
| 77 | 77 | ||
| @@ -259,7 +259,7 @@ internal constructor( | @@ -259,7 +259,7 @@ internal constructor( | ||
| 259 | requestConfig = { | 259 | requestConfig = { |
| 260 | disableDtx = !options.dtx | 260 | disableDtx = !options.dtx |
| 261 | disableRed = !options.red | 261 | disableRed = !options.red |
| 262 | - source = LivekitModels.TrackSource.MICROPHONE | 262 | + source = options.source?.toProto() ?: LivekitModels.TrackSource.MICROPHONE |
| 263 | }, | 263 | }, |
| 264 | encodings = encodings, | 264 | encodings = encodings, |
| 265 | publishListener = publishListener, | 265 | publishListener = publishListener, |
| @@ -295,7 +295,7 @@ internal constructor( | @@ -295,7 +295,7 @@ internal constructor( | ||
| 295 | requestConfig = { | 295 | requestConfig = { |
| 296 | width = track.dimensions.width | 296 | width = track.dimensions.width |
| 297 | height = track.dimensions.height | 297 | height = track.dimensions.height |
| 298 | - source = if (track.options.isScreencast) { | 298 | + source = options.source?.toProto() ?: if (track.options.isScreencast) { |
| 299 | LivekitModels.TrackSource.SCREEN_SHARE | 299 | LivekitModels.TrackSource.SCREEN_SHARE |
| 300 | } else { | 300 | } else { |
| 301 | LivekitModels.TrackSource.CAMERA | 301 | LivekitModels.TrackSource.CAMERA |
| @@ -350,8 +350,9 @@ internal constructor( | @@ -350,8 +350,9 @@ internal constructor( | ||
| 350 | } | 350 | } |
| 351 | val trackInfo = engine.addTrack( | 351 | val trackInfo = engine.addTrack( |
| 352 | cid = cid, | 352 | cid = cid, |
| 353 | - name = track.name, | 353 | + name = options.name ?: track.name, |
| 354 | kind = track.kind.toProto(), | 354 | kind = track.kind.toProto(), |
| 355 | + stream = options.stream, | ||
| 355 | builder = builder, | 356 | builder = builder, |
| 356 | ) | 357 | ) |
| 357 | 358 | ||
| @@ -374,7 +375,7 @@ internal constructor( | @@ -374,7 +375,7 @@ internal constructor( | ||
| 374 | 375 | ||
| 375 | val transInit = RtpTransceiverInit( | 376 | val transInit = RtpTransceiverInit( |
| 376 | RtpTransceiver.RtpTransceiverDirection.SEND_ONLY, | 377 | RtpTransceiver.RtpTransceiverDirection.SEND_ONLY, |
| 377 | - listOf(this.sid), | 378 | + listOf(this.sid.value), |
| 378 | encodings, | 379 | encodings, |
| 379 | ) | 380 | ) |
| 380 | val transceiver = engine.createSenderTransceiver(track.rtcTrack, transInit) | 381 | val transceiver = engine.createSenderTransceiver(track.rtcTrack, transInit) |
| @@ -552,7 +553,7 @@ internal constructor( | @@ -552,7 +553,7 @@ internal constructor( | ||
| 552 | } | 553 | } |
| 553 | 554 | ||
| 554 | val sid = publication.sid | 555 | val sid = publication.sid |
| 555 | - tracks = tracks.toMutableMap().apply { remove(sid) } | 556 | + trackPublications = trackPublications.toMutableMap().apply { remove(sid) } |
| 556 | 557 | ||
| 557 | if (engine.connectionState == ConnectionState.CONNECTED) { | 558 | if (engine.connectionState == ConnectionState.CONNECTED) { |
| 558 | engine.removeTrack(track.rtcTrack) | 559 | engine.removeTrack(track.rtcTrack) |
| @@ -570,15 +571,15 @@ internal constructor( | @@ -570,15 +571,15 @@ internal constructor( | ||
| 570 | * | 571 | * |
| 571 | * @param data payload to send | 572 | * @param data payload to send |
| 572 | * @param reliability for delivery guarantee, use RELIABLE. for fastest delivery without guarantee, use LOSSY | 573 | * @param reliability for delivery guarantee, use RELIABLE. for fastest delivery without guarantee, use LOSSY |
| 573 | - * @param destination list of participant SIDs to deliver the payload, null to deliver to everyone | ||
| 574 | * @param topic the topic under which the message was published | 574 | * @param topic the topic under which the message was published |
| 575 | + * @param identities list of participant identities to deliver the payload, null to deliver to everyone | ||
| 575 | */ | 576 | */ |
| 576 | @Suppress("unused") | 577 | @Suppress("unused") |
| 577 | suspend fun publishData( | 578 | suspend fun publishData( |
| 578 | data: ByteArray, | 579 | data: ByteArray, |
| 579 | reliability: DataPublishReliability = DataPublishReliability.RELIABLE, | 580 | reliability: DataPublishReliability = DataPublishReliability.RELIABLE, |
| 580 | - destination: List<String>? = null, | ||
| 581 | topic: String? = null, | 581 | topic: String? = null, |
| 582 | + identities: List<Identity>? = null, | ||
| 582 | ) { | 583 | ) { |
| 583 | if (data.size > RTCEngine.MAX_DATA_PACKET_SIZE) { | 584 | if (data.size > RTCEngine.MAX_DATA_PACKET_SIZE) { |
| 584 | throw IllegalArgumentException("cannot publish data larger than " + RTCEngine.MAX_DATA_PACKET_SIZE) | 585 | throw IllegalArgumentException("cannot publish data larger than " + RTCEngine.MAX_DATA_PACKET_SIZE) |
| @@ -590,12 +591,12 @@ internal constructor( | @@ -590,12 +591,12 @@ internal constructor( | ||
| 590 | } | 591 | } |
| 591 | val packetBuilder = LivekitModels.UserPacket.newBuilder().apply { | 592 | val packetBuilder = LivekitModels.UserPacket.newBuilder().apply { |
| 592 | payload = ByteString.copyFrom(data) | 593 | payload = ByteString.copyFrom(data) |
| 593 | - participantSid = sid | 594 | + participantSid = sid.value |
| 594 | if (topic != null) { | 595 | if (topic != null) { |
| 595 | setTopic(topic) | 596 | setTopic(topic) |
| 596 | } | 597 | } |
| 597 | - if (destination != null) { | ||
| 598 | - addAllDestinationSids(destination) | 598 | + if (identities != null) { |
| 599 | + addAllDestinationIdentities(identities.map { it.value }) | ||
| 599 | } | 600 | } |
| 600 | } | 601 | } |
| 601 | val dataPacket = LivekitModels.DataPacket.newBuilder() | 602 | val dataPacket = LivekitModels.DataPacket.newBuilder() |
| @@ -611,10 +612,10 @@ internal constructor( | @@ -611,10 +612,10 @@ internal constructor( | ||
| 611 | 612 | ||
| 612 | // detect tracks that have mute status mismatched on server | 613 | // detect tracks that have mute status mismatched on server |
| 613 | for (ti in info.tracksList) { | 614 | for (ti in info.tracksList) { |
| 614 | - val publication = this.tracks[ti.sid] as? LocalTrackPublication ?: continue | 615 | + val publication = this.trackPublications[ti.sid] as? LocalTrackPublication ?: continue |
| 615 | val localMuted = publication.muted | 616 | val localMuted = publication.muted |
| 616 | if (ti.muted != localMuted) { | 617 | if (ti.muted != localMuted) { |
| 617 | - engine.updateMuteStatus(sid, localMuted) | 618 | + engine.updateMuteStatus(sid.value, localMuted) |
| 618 | } | 619 | } |
| 619 | } | 620 | } |
| 620 | } | 621 | } |
| @@ -640,7 +641,7 @@ internal constructor( | @@ -640,7 +641,7 @@ internal constructor( | ||
| 640 | } | 641 | } |
| 641 | 642 | ||
| 642 | internal fun onRemoteMuteChanged(trackSid: String, muted: Boolean) { | 643 | internal fun onRemoteMuteChanged(trackSid: String, muted: Boolean) { |
| 643 | - val pub = tracks[trackSid] | 644 | + val pub = trackPublications[trackSid] |
| 644 | pub?.muted = muted | 645 | pub?.muted = muted |
| 645 | } | 646 | } |
| 646 | 647 | ||
| @@ -652,7 +653,7 @@ internal constructor( | @@ -652,7 +653,7 @@ internal constructor( | ||
| 652 | val trackSid = subscribedQualityUpdate.trackSid | 653 | val trackSid = subscribedQualityUpdate.trackSid |
| 653 | val subscribedCodecs = subscribedQualityUpdate.subscribedCodecsList | 654 | val subscribedCodecs = subscribedQualityUpdate.subscribedCodecsList |
| 654 | val qualities = subscribedQualityUpdate.subscribedQualitiesList | 655 | val qualities = subscribedQualityUpdate.subscribedQualitiesList |
| 655 | - val pub = tracks[trackSid] as? LocalTrackPublication ?: return | 656 | + val pub = trackPublications[trackSid] as? LocalTrackPublication ?: return |
| 656 | val track = pub.track as? LocalVideoTrack ?: return | 657 | val track = pub.track as? LocalVideoTrack ?: return |
| 657 | val options = pub.options as? VideoTrackPublishOptions ?: return | 658 | val options = pub.options as? VideoTrackPublishOptions ?: return |
| 658 | 659 | ||
| @@ -671,7 +672,7 @@ internal constructor( | @@ -671,7 +672,7 @@ internal constructor( | ||
| 671 | } | 672 | } |
| 672 | 673 | ||
| 673 | private fun publishAdditionalCodecForTrack(track: LocalVideoTrack, codec: VideoCodec, options: VideoTrackPublishOptions) { | 674 | private fun publishAdditionalCodecForTrack(track: LocalVideoTrack, codec: VideoCodec, options: VideoTrackPublishOptions) { |
| 674 | - val existingPublication = tracks[track.sid] ?: run { | 675 | + val existingPublication = trackPublications[track.sid] ?: run { |
| 675 | LKLog.w { "attempting to publish additional codec for non-published track?!" } | 676 | LKLog.w { "attempting to publish additional codec for non-published track?!" } |
| 676 | return | 677 | return |
| 677 | } | 678 | } |
| @@ -685,7 +686,7 @@ internal constructor( | @@ -685,7 +686,7 @@ internal constructor( | ||
| 685 | 686 | ||
| 686 | val transceiverInit = RtpTransceiverInit( | 687 | val transceiverInit = RtpTransceiverInit( |
| 687 | RtpTransceiver.RtpTransceiverDirection.SEND_ONLY, | 688 | RtpTransceiver.RtpTransceiverDirection.SEND_ONLY, |
| 688 | - listOf(this.sid), | 689 | + listOf(this.sid.value), |
| 689 | newEncodings, | 690 | newEncodings, |
| 690 | ) | 691 | ) |
| 691 | 692 | ||
| @@ -725,6 +726,7 @@ internal constructor( | @@ -725,6 +726,7 @@ internal constructor( | ||
| 725 | cid = simulcastTrack.rtcTrack.id(), | 726 | cid = simulcastTrack.rtcTrack.id(), |
| 726 | name = existingPublication.name, | 727 | name = existingPublication.name, |
| 727 | kind = existingPublication.kind.toProto(), | 728 | kind = existingPublication.kind.toProto(), |
| 729 | + stream = options.stream, | ||
| 728 | builder = trackRequest, | 730 | builder = trackRequest, |
| 729 | ) | 731 | ) |
| 730 | 732 | ||
| @@ -735,7 +737,7 @@ internal constructor( | @@ -735,7 +737,7 @@ internal constructor( | ||
| 735 | } | 737 | } |
| 736 | 738 | ||
| 737 | internal fun handleLocalTrackUnpublished(unpublishedResponse: LivekitRtc.TrackUnpublishedResponse) { | 739 | internal fun handleLocalTrackUnpublished(unpublishedResponse: LivekitRtc.TrackUnpublishedResponse) { |
| 738 | - val pub = tracks[unpublishedResponse.trackSid] | 740 | + val pub = trackPublications[unpublishedResponse.trackSid] |
| 739 | val track = pub?.track | 741 | val track = pub?.track |
| 740 | if (track == null) { | 742 | if (track == null) { |
| 741 | LKLog.w { "Received unpublished track response for unknown or non-published track: ${unpublishedResponse.trackSid}" } | 743 | LKLog.w { "Received unpublished track response for unknown or non-published track: ${unpublishedResponse.trackSid}" } |
| @@ -753,7 +755,7 @@ internal constructor( | @@ -753,7 +755,7 @@ internal constructor( | ||
| 753 | republishes = pubs | 755 | republishes = pubs |
| 754 | } | 756 | } |
| 755 | 757 | ||
| 756 | - tracks = tracks.toMutableMap().apply { clear() } | 758 | + trackPublications = trackPublications.toMutableMap().apply { clear() } |
| 757 | 759 | ||
| 758 | for (publication in pubs) { | 760 | for (publication in pubs) { |
| 759 | internalListener?.onTrackUnpublished(publication, this) | 761 | internalListener?.onTrackUnpublished(publication, this) |
| @@ -780,7 +782,7 @@ internal constructor( | @@ -780,7 +782,7 @@ internal constructor( | ||
| 780 | } | 782 | } |
| 781 | 783 | ||
| 782 | fun cleanup() { | 784 | fun cleanup() { |
| 783 | - for (pub in tracks.values) { | 785 | + for (pub in trackPublications.values) { |
| 784 | val track = pub.track | 786 | val track = pub.track |
| 785 | 787 | ||
| 786 | if (track != null) { | 788 | if (track != null) { |
| @@ -810,7 +812,7 @@ internal constructor( | @@ -810,7 +812,7 @@ internal constructor( | ||
| 810 | } | 812 | } |
| 811 | 813 | ||
| 812 | internal fun LocalParticipant.publishTracksInfo(): List<LivekitRtc.TrackPublishedResponse> { | 814 | internal fun LocalParticipant.publishTracksInfo(): List<LivekitRtc.TrackPublishedResponse> { |
| 813 | - return tracks.values.mapNotNull { trackPub -> | 815 | + return trackPublications.values.mapNotNull { trackPub -> |
| 814 | val track = trackPub.track ?: return@mapNotNull null | 816 | val track = trackPub.track ?: return@mapNotNull null |
| 815 | 817 | ||
| 816 | LivekitRtc.TrackPublishedResponse.newBuilder() | 818 | LivekitRtc.TrackPublishedResponse.newBuilder() |
| @@ -821,7 +823,23 @@ internal fun LocalParticipant.publishTracksInfo(): List<LivekitRtc.TrackPublishe | @@ -821,7 +823,23 @@ internal fun LocalParticipant.publishTracksInfo(): List<LivekitRtc.TrackPublishe | ||
| 821 | } | 823 | } |
| 822 | 824 | ||
| 823 | interface TrackPublishOptions { | 825 | interface TrackPublishOptions { |
| 826 | + /** | ||
| 827 | + * The name of the track. | ||
| 828 | + */ | ||
| 824 | val name: String? | 829 | val name: String? |
| 830 | + | ||
| 831 | + /** | ||
| 832 | + * The source of a track, camera, microphone or screen. | ||
| 833 | + */ | ||
| 834 | + val source: Track.Source? | ||
| 835 | + | ||
| 836 | + /** | ||
| 837 | + * The stream name for the track. Audio and video tracks with the same stream | ||
| 838 | + * name will be placed in the same `MediaStream` and offer better synchronization. | ||
| 839 | + * | ||
| 840 | + * By default, camera and microphone will be placed in the same stream. | ||
| 841 | + */ | ||
| 842 | + val stream: String? | ||
| 825 | } | 843 | } |
| 826 | 844 | ||
| 827 | abstract class BaseVideoTrackPublishOptions { | 845 | abstract class BaseVideoTrackPublishOptions { |
| @@ -868,17 +886,23 @@ data class VideoTrackPublishOptions( | @@ -868,17 +886,23 @@ data class VideoTrackPublishOptions( | ||
| 868 | override val videoCodec: String = VideoCodec.VP8.codecName, | 886 | override val videoCodec: String = VideoCodec.VP8.codecName, |
| 869 | override val scalabilityMode: String? = null, | 887 | override val scalabilityMode: String? = null, |
| 870 | override val backupCodec: BackupVideoCodec? = null, | 888 | override val backupCodec: BackupVideoCodec? = null, |
| 889 | + override val source: Track.Source? = null, | ||
| 890 | + override val stream: String? = null, | ||
| 871 | ) : BaseVideoTrackPublishOptions(), TrackPublishOptions { | 891 | ) : BaseVideoTrackPublishOptions(), TrackPublishOptions { |
| 872 | constructor( | 892 | constructor( |
| 873 | name: String? = null, | 893 | name: String? = null, |
| 874 | base: BaseVideoTrackPublishOptions, | 894 | base: BaseVideoTrackPublishOptions, |
| 895 | + source: Track.Source? = null, | ||
| 896 | + stream: String? = null, | ||
| 875 | ) : this( | 897 | ) : this( |
| 876 | - name, | ||
| 877 | - base.videoEncoding, | ||
| 878 | - base.simulcast, | ||
| 879 | - base.videoCodec, | ||
| 880 | - base.scalabilityMode, | ||
| 881 | - base.backupCodec, | 898 | + name = name, |
| 899 | + videoEncoding = base.videoEncoding, | ||
| 900 | + simulcast = base.simulcast, | ||
| 901 | + videoCodec = base.videoCodec, | ||
| 902 | + scalabilityMode = base.scalabilityMode, | ||
| 903 | + backupCodec = base.backupCodec, | ||
| 904 | + source = source, | ||
| 905 | + stream = stream, | ||
| 882 | ) | 906 | ) |
| 883 | 907 | ||
| 884 | fun createBackupOptions(): VideoTrackPublishOptions? { | 908 | fun createBackupOptions(): VideoTrackPublishOptions? { |
| @@ -913,25 +937,38 @@ abstract class BaseAudioTrackPublishOptions { | @@ -913,25 +937,38 @@ abstract class BaseAudioTrackPublishOptions { | ||
| 913 | abstract val red: Boolean | 937 | abstract val red: Boolean |
| 914 | } | 938 | } |
| 915 | 939 | ||
| 940 | +/** | ||
| 941 | + * Default options for publishing an audio track. | ||
| 942 | + */ | ||
| 916 | data class AudioTrackPublishDefaults( | 943 | data class AudioTrackPublishDefaults( |
| 917 | override val audioBitrate: Int? = 20_000, | 944 | override val audioBitrate: Int? = 20_000, |
| 918 | override val dtx: Boolean = true, | 945 | override val dtx: Boolean = true, |
| 919 | override val red: Boolean = true, | 946 | override val red: Boolean = true, |
| 920 | ) : BaseAudioTrackPublishOptions() | 947 | ) : BaseAudioTrackPublishOptions() |
| 921 | 948 | ||
| 949 | +/** | ||
| 950 | + * Options for publishing an audio track. | ||
| 951 | + */ | ||
| 922 | data class AudioTrackPublishOptions( | 952 | data class AudioTrackPublishOptions( |
| 923 | override val name: String? = null, | 953 | override val name: String? = null, |
| 924 | override val audioBitrate: Int? = null, | 954 | override val audioBitrate: Int? = null, |
| 925 | override val dtx: Boolean = true, | 955 | override val dtx: Boolean = true, |
| 926 | override val red: Boolean = true, | 956 | override val red: Boolean = true, |
| 957 | + override val source: Track.Source? = null, | ||
| 958 | + override val stream: String? = null, | ||
| 927 | ) : BaseAudioTrackPublishOptions(), TrackPublishOptions { | 959 | ) : BaseAudioTrackPublishOptions(), TrackPublishOptions { |
| 928 | constructor( | 960 | constructor( |
| 929 | name: String? = null, | 961 | name: String? = null, |
| 930 | base: BaseAudioTrackPublishOptions, | 962 | base: BaseAudioTrackPublishOptions, |
| 963 | + source: Track.Source? = null, | ||
| 964 | + stream: String? = null, | ||
| 931 | ) : this( | 965 | ) : this( |
| 932 | - name, | ||
| 933 | - base.audioBitrate, | ||
| 934 | - base.dtx, | 966 | + name = name, |
| 967 | + audioBitrate = base.audioBitrate, | ||
| 968 | + dtx = base.dtx, | ||
| 969 | + red = base.red, | ||
| 970 | + source = source, | ||
| 971 | + stream = stream, | ||
| 935 | ) | 972 | ) |
| 936 | } | 973 | } |
| 937 | 974 | ||
| @@ -962,7 +999,7 @@ data class ParticipantTrackPermission( | @@ -962,7 +999,7 @@ data class ParticipantTrackPermission( | ||
| 962 | } | 999 | } |
| 963 | } | 1000 | } |
| 964 | 1001 | ||
| 965 | - fun toProto(): LivekitRtc.TrackPermission { | 1002 | + internal fun toProto(): LivekitRtc.TrackPermission { |
| 966 | return LivekitRtc.TrackPermission.newBuilder() | 1003 | return LivekitRtc.TrackPermission.newBuilder() |
| 967 | .setParticipantIdentity(participantIdentity) | 1004 | .setParticipantIdentity(participantIdentity) |
| 968 | .setParticipantSid(participantSid) | 1005 | .setParticipantSid(participantSid) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -29,17 +29,26 @@ import io.livekit.android.util.flow | @@ -29,17 +29,26 @@ import io.livekit.android.util.flow | ||
| 29 | import io.livekit.android.util.flowDelegate | 29 | import io.livekit.android.util.flowDelegate |
| 30 | import kotlinx.coroutines.* | 30 | import kotlinx.coroutines.* |
| 31 | import kotlinx.coroutines.flow.* | 31 | import kotlinx.coroutines.flow.* |
| 32 | +import kotlinx.serialization.Serializable | ||
| 32 | import livekit.LivekitModels | 33 | import livekit.LivekitModels |
| 33 | import java.util.Date | 34 | import java.util.Date |
| 34 | import javax.inject.Named | 35 | import javax.inject.Named |
| 35 | 36 | ||
| 36 | open class Participant( | 37 | open class Participant( |
| 37 | - var sid: String, | ||
| 38 | - identity: String? = null, | 38 | + var sid: Sid, |
| 39 | + identity: Identity? = null, | ||
| 39 | @Named(InjectionNames.DISPATCHER_DEFAULT) | 40 | @Named(InjectionNames.DISPATCHER_DEFAULT) |
| 40 | private val coroutineDispatcher: CoroutineDispatcher, | 41 | private val coroutineDispatcher: CoroutineDispatcher, |
| 41 | ) { | 42 | ) { |
| 42 | 43 | ||
| 44 | + @Serializable | ||
| 45 | + @JvmInline | ||
| 46 | + value class Identity(val value: String) | ||
| 47 | + | ||
| 48 | + @Serializable | ||
| 49 | + @JvmInline | ||
| 50 | + value class Sid(val value: String) | ||
| 51 | + | ||
| 43 | /** | 52 | /** |
| 44 | * To only be used for flow delegate scoping, and should not be cancelled. | 53 | * To only be used for flow delegate scoping, and should not be cancelled. |
| 45 | **/ | 54 | **/ |
| @@ -65,7 +74,7 @@ open class Participant( | @@ -65,7 +74,7 @@ open class Participant( | ||
| 65 | */ | 74 | */ |
| 66 | @FlowObservable | 75 | @FlowObservable |
| 67 | @get:FlowObservable | 76 | @get:FlowObservable |
| 68 | - var identity: String? by flowDelegate(identity) | 77 | + var identity: Identity? by flowDelegate(identity) |
| 69 | internal set | 78 | internal set |
| 70 | 79 | ||
| 71 | /** | 80 | /** |
| @@ -179,7 +188,7 @@ open class Participant( | @@ -179,7 +188,7 @@ open class Participant( | ||
| 179 | */ | 188 | */ |
| 180 | @FlowObservable | 189 | @FlowObservable |
| 181 | @get:FlowObservable | 190 | @get:FlowObservable |
| 182 | - var tracks by flowDelegate(emptyMap<String, TrackPublication>()) | 191 | + var trackPublications by flowDelegate(emptyMap<String, TrackPublication>()) |
| 183 | protected set | 192 | protected set |
| 184 | 193 | ||
| 185 | private fun Flow<Map<String, TrackPublication>>.trackUpdateFlow(): Flow<List<Pair<TrackPublication, Track?>>> { | 194 | private fun Flow<Map<String, TrackPublication>>.trackUpdateFlow(): Flow<List<Pair<TrackPublication, Track?>>> { |
| @@ -206,8 +215,8 @@ open class Participant( | @@ -206,8 +215,8 @@ open class Participant( | ||
| 206 | */ | 215 | */ |
| 207 | @FlowObservable | 216 | @FlowObservable |
| 208 | @get:FlowObservable | 217 | @get:FlowObservable |
| 209 | - val audioTracks by flowDelegate( | ||
| 210 | - stateFlow = ::tracks.flow | 218 | + val audioTrackPublications by flowDelegate( |
| 219 | + stateFlow = ::trackPublications.flow | ||
| 211 | .map { it.filterValues { publication -> publication.kind == Track.Kind.AUDIO } } | 220 | .map { it.filterValues { publication -> publication.kind == Track.Kind.AUDIO } } |
| 212 | .trackUpdateFlow() | 221 | .trackUpdateFlow() |
| 213 | .stateIn(delegateScope, SharingStarted.Eagerly, emptyList()), | 222 | .stateIn(delegateScope, SharingStarted.Eagerly, emptyList()), |
| @@ -218,8 +227,8 @@ open class Participant( | @@ -218,8 +227,8 @@ open class Participant( | ||
| 218 | */ | 227 | */ |
| 219 | @FlowObservable | 228 | @FlowObservable |
| 220 | @get:FlowObservable | 229 | @get:FlowObservable |
| 221 | - val videoTracks by flowDelegate( | ||
| 222 | - stateFlow = ::tracks.flow | 230 | + val videoTrackPublications by flowDelegate( |
| 231 | + stateFlow = ::trackPublications.flow | ||
| 223 | .map { it.filterValues { publication -> publication.kind == Track.Kind.VIDEO } } | 232 | .map { it.filterValues { publication -> publication.kind == Track.Kind.VIDEO } } |
| 224 | .trackUpdateFlow() | 233 | .trackUpdateFlow() |
| 225 | .stateIn(delegateScope, SharingStarted.Eagerly, emptyList()), | 234 | .stateIn(delegateScope, SharingStarted.Eagerly, emptyList()), |
| @@ -231,7 +240,7 @@ open class Participant( | @@ -231,7 +240,7 @@ open class Participant( | ||
| 231 | fun addTrackPublication(publication: TrackPublication) { | 240 | fun addTrackPublication(publication: TrackPublication) { |
| 232 | val track = publication.track | 241 | val track = publication.track |
| 233 | track?.sid = publication.sid | 242 | track?.sid = publication.sid |
| 234 | - tracks = tracks.toMutableMap().apply { | 243 | + trackPublications = trackPublications.toMutableMap().apply { |
| 235 | this[publication.sid] = publication | 244 | this[publication.sid] = publication |
| 236 | } | 245 | } |
| 237 | } | 246 | } |
| @@ -244,7 +253,7 @@ open class Participant( | @@ -244,7 +253,7 @@ open class Participant( | ||
| 244 | return null | 253 | return null |
| 245 | } | 254 | } |
| 246 | 255 | ||
| 247 | - for ((_, pub) in tracks) { | 256 | + for ((_, pub) in trackPublications) { |
| 248 | if (pub.source == source) { | 257 | if (pub.source == source) { |
| 249 | return pub | 258 | return pub |
| 250 | } | 259 | } |
| @@ -269,7 +278,7 @@ open class Participant( | @@ -269,7 +278,7 @@ open class Participant( | ||
| 269 | * Retrieves the first track that matches [name], or null | 278 | * Retrieves the first track that matches [name], or null |
| 270 | */ | 279 | */ |
| 271 | open fun getTrackPublicationByName(name: String): TrackPublication? { | 280 | open fun getTrackPublicationByName(name: String): TrackPublication? { |
| 272 | - for ((_, pub) in tracks) { | 281 | + for ((_, pub) in trackPublications) { |
| 273 | if (pub.name == name) { | 282 | if (pub.name == name) { |
| 274 | return pub | 283 | return pub |
| 275 | } | 284 | } |
| @@ -300,8 +309,8 @@ open class Participant( | @@ -300,8 +309,8 @@ open class Participant( | ||
| 300 | * @suppress | 309 | * @suppress |
| 301 | */ | 310 | */ |
| 302 | internal open fun updateFromInfo(info: LivekitModels.ParticipantInfo) { | 311 | internal open fun updateFromInfo(info: LivekitModels.ParticipantInfo) { |
| 303 | - sid = info.sid | ||
| 304 | - identity = info.identity | 312 | + sid = Sid(info.sid) |
| 313 | + identity = Identity(info.identity) | ||
| 305 | participantInfo = info | 314 | participantInfo = info |
| 306 | metadata = info.metadata | 315 | metadata = info.metadata |
| 307 | name = info.name | 316 | name = info.name |
| @@ -339,7 +348,7 @@ open class Participant( | @@ -339,7 +348,7 @@ open class Participant( | ||
| 339 | } | 348 | } |
| 340 | 349 | ||
| 341 | internal fun onTrackStreamStateChanged(trackEvent: TrackEvent.StreamStateChanged) { | 350 | internal fun onTrackStreamStateChanged(trackEvent: TrackEvent.StreamStateChanged) { |
| 342 | - val trackPublication = tracks[trackEvent.track.sid] ?: return | 351 | + val trackPublication = trackPublications[trackEvent.track.sid] ?: return |
| 343 | eventBus.postEvent( | 352 | eventBus.postEvent( |
| 344 | ParticipantEvent.TrackStreamStateChanged(this, trackPublication, trackEvent.streamState), | 353 | ParticipantEvent.TrackStreamStateChanged(this, trackPublication, trackEvent.streamState), |
| 345 | scope, | 354 | scope, |
| @@ -355,7 +364,7 @@ open class Participant( | @@ -355,7 +364,7 @@ open class Participant( | ||
| 355 | internal open fun dispose() { | 364 | internal open fun dispose() { |
| 356 | scope.cancel() | 365 | scope.cancel() |
| 357 | 366 | ||
| 358 | - sid = "" | 367 | + sid = Sid("") |
| 359 | name = null | 368 | name = null |
| 360 | identity = null | 369 | identity = null |
| 361 | metadata = null | 370 | metadata = null |
| @@ -455,6 +464,7 @@ enum class ConnectionQuality { | @@ -455,6 +464,7 @@ enum class ConnectionQuality { | ||
| 455 | GOOD, | 464 | GOOD, |
| 456 | POOR, | 465 | POOR, |
| 457 | UNKNOWN, | 466 | UNKNOWN, |
| 467 | + LOST, | ||
| 458 | ; | 468 | ; |
| 459 | 469 | ||
| 460 | companion object { | 470 | companion object { |
| @@ -464,6 +474,7 @@ enum class ConnectionQuality { | @@ -464,6 +474,7 @@ enum class ConnectionQuality { | ||
| 464 | LivekitModels.ConnectionQuality.GOOD -> GOOD | 474 | LivekitModels.ConnectionQuality.GOOD -> GOOD |
| 465 | LivekitModels.ConnectionQuality.POOR -> POOR | 475 | LivekitModels.ConnectionQuality.POOR -> POOR |
| 466 | LivekitModels.ConnectionQuality.UNRECOGNIZED -> UNKNOWN | 476 | LivekitModels.ConnectionQuality.UNRECOGNIZED -> UNKNOWN |
| 477 | + LivekitModels.ConnectionQuality.LOST -> LOST | ||
| 467 | } | 478 | } |
| 468 | } | 479 | } |
| 469 | } | 480 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room.participant | 17 | package io.livekit.android.room.participant |
| 18 | 18 | ||
| 19 | -fun String.mimeTypeToVideoCodec(): String? { | 19 | +internal fun String.mimeTypeToVideoCodec(): String? { |
| 20 | return split("/") | 20 | return split("/") |
| 21 | .takeIf { length > 1 } | 21 | .takeIf { length > 1 } |
| 22 | ?.get(1) | 22 | ?.get(1) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -32,14 +32,14 @@ import kotlinx.coroutines.delay | @@ -32,14 +32,14 @@ import kotlinx.coroutines.delay | ||
| 32 | import kotlinx.coroutines.launch | 32 | import kotlinx.coroutines.launch |
| 33 | import livekit.LivekitModels | 33 | import livekit.LivekitModels |
| 34 | import livekit.LivekitRtc | 34 | import livekit.LivekitRtc |
| 35 | -import org.webrtc.AudioTrack | ||
| 36 | -import org.webrtc.MediaStreamTrack | ||
| 37 | -import org.webrtc.RtpReceiver | ||
| 38 | -import org.webrtc.VideoTrack | 35 | +import livekit.org.webrtc.AudioTrack |
| 36 | +import livekit.org.webrtc.MediaStreamTrack | ||
| 37 | +import livekit.org.webrtc.RtpReceiver | ||
| 38 | +import livekit.org.webrtc.VideoTrack | ||
| 39 | 39 | ||
| 40 | class RemoteParticipant( | 40 | class RemoteParticipant( |
| 41 | - sid: String, | ||
| 42 | - identity: String? = null, | 41 | + sid: Sid, |
| 42 | + identity: Identity? = null, | ||
| 43 | val signalClient: SignalClient, | 43 | val signalClient: SignalClient, |
| 44 | private val ioDispatcher: CoroutineDispatcher, | 44 | private val ioDispatcher: CoroutineDispatcher, |
| 45 | defaultDispatcher: CoroutineDispatcher, | 45 | defaultDispatcher: CoroutineDispatcher, |
| @@ -57,8 +57,8 @@ class RemoteParticipant( | @@ -57,8 +57,8 @@ class RemoteParticipant( | ||
| 57 | ioDispatcher: CoroutineDispatcher, | 57 | ioDispatcher: CoroutineDispatcher, |
| 58 | defaultDispatcher: CoroutineDispatcher, | 58 | defaultDispatcher: CoroutineDispatcher, |
| 59 | ) : this( | 59 | ) : this( |
| 60 | - info.sid, | ||
| 61 | - info.identity, | 60 | + Sid(info.sid), |
| 61 | + Identity(info.identity), | ||
| 62 | signalClient, | 62 | signalClient, |
| 63 | ioDispatcher, | 63 | ioDispatcher, |
| 64 | defaultDispatcher, | 64 | defaultDispatcher, |
| @@ -68,7 +68,7 @@ class RemoteParticipant( | @@ -68,7 +68,7 @@ class RemoteParticipant( | ||
| 68 | 68 | ||
| 69 | private val coroutineScope = CloseableCoroutineScope(defaultDispatcher + SupervisorJob()) | 69 | private val coroutineScope = CloseableCoroutineScope(defaultDispatcher + SupervisorJob()) |
| 70 | 70 | ||
| 71 | - fun getTrackPublication(sid: String): RemoteTrackPublication? = tracks[sid] as? RemoteTrackPublication | 71 | + fun getTrackPublication(sid: String): RemoteTrackPublication? = trackPublications[sid] as? RemoteTrackPublication |
| 72 | 72 | ||
| 73 | /** | 73 | /** |
| 74 | * @suppress | 74 | * @suppress |
| @@ -105,9 +105,9 @@ class RemoteParticipant( | @@ -105,9 +105,9 @@ class RemoteParticipant( | ||
| 105 | eventBus.postEvent(ParticipantEvent.TrackPublished(this, publication), scope) | 105 | eventBus.postEvent(ParticipantEvent.TrackPublished(this, publication), scope) |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | - val invalidKeys = tracks.keys - validTrackPublication.keys | 108 | + val invalidKeys = trackPublications.keys - validTrackPublication.keys |
| 109 | for (invalidKey in invalidKeys) { | 109 | for (invalidKey in invalidKeys) { |
| 110 | - val publication = tracks[invalidKey] ?: continue | 110 | + val publication = trackPublications[invalidKey] ?: continue |
| 111 | unpublishTrack(publication.sid, true) | 111 | unpublishTrack(publication.sid, true) |
| 112 | } | 112 | } |
| 113 | } | 113 | } |
| @@ -175,8 +175,8 @@ class RemoteParticipant( | @@ -175,8 +175,8 @@ class RemoteParticipant( | ||
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | fun unpublishTrack(trackSid: String, sendUnpublish: Boolean = false) { | 177 | fun unpublishTrack(trackSid: String, sendUnpublish: Boolean = false) { |
| 178 | - val publication = tracks[trackSid] as? RemoteTrackPublication ?: return | ||
| 179 | - tracks = tracks.toMutableMap().apply { remove(trackSid) } | 178 | + val publication = trackPublications[trackSid] as? RemoteTrackPublication ?: return |
| 179 | + trackPublications = trackPublications.toMutableMap().apply { remove(trackSid) } | ||
| 180 | 180 | ||
| 181 | val track = publication.track | 181 | val track = publication.track |
| 182 | if (track != null) { | 182 | if (track != null) { |
| @@ -198,7 +198,7 @@ class RemoteParticipant( | @@ -198,7 +198,7 @@ class RemoteParticipant( | ||
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | internal fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) { | 200 | internal fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) { |
| 201 | - val pub = tracks[subscriptionPermissionUpdate.trackSid] as? RemoteTrackPublication ?: return | 201 | + val pub = trackPublications[subscriptionPermissionUpdate.trackSid] as? RemoteTrackPublication ?: return |
| 202 | 202 | ||
| 203 | if (pub.subscriptionAllowed != subscriptionPermissionUpdate.allowed) { | 203 | if (pub.subscriptionAllowed != subscriptionPermissionUpdate.allowed) { |
| 204 | pub.subscriptionAllowed = subscriptionPermissionUpdate.allowed | 204 | pub.subscriptionAllowed = subscriptionPermissionUpdate.allowed |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,16 @@ | @@ -16,7 +16,16 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room.track | 17 | package io.livekit.android.room.track |
| 18 | 18 | ||
| 19 | -import org.webrtc.AudioTrack | 19 | +import livekit.org.webrtc.AudioTrack |
| 20 | 20 | ||
| 21 | -abstract class AudioTrack(name: String, override val rtcTrack: AudioTrack) : | 21 | +/** |
| 22 | + * A class representing an audio track. | ||
| 23 | + */ | ||
| 24 | +abstract class AudioTrack( | ||
| 25 | + name: String, | ||
| 26 | + /** | ||
| 27 | + * The underlying WebRTC audio track. | ||
| 28 | + */ | ||
| 29 | + override val rtcTrack: AudioTrack | ||
| 30 | +) : | ||
| 22 | Track(name, Kind.AUDIO, rtcTrack) | 31 | Track(name, Kind.AUDIO, rtcTrack) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,10 +21,10 @@ import android.content.Context | @@ -21,10 +21,10 @@ import android.content.Context | ||
| 21 | import android.content.pm.PackageManager | 21 | import android.content.pm.PackageManager |
| 22 | import androidx.core.content.ContextCompat | 22 | import androidx.core.content.ContextCompat |
| 23 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread | 23 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread |
| 24 | -import org.webrtc.MediaConstraints | ||
| 25 | -import org.webrtc.PeerConnectionFactory | ||
| 26 | -import org.webrtc.RtpSender | ||
| 27 | -import org.webrtc.RtpTransceiver | 24 | +import livekit.org.webrtc.MediaConstraints |
| 25 | +import livekit.org.webrtc.PeerConnectionFactory | ||
| 26 | +import livekit.org.webrtc.RtpSender | ||
| 27 | +import livekit.org.webrtc.RtpTransceiver | ||
| 28 | import java.util.* | 28 | import java.util.* |
| 29 | 29 | ||
| 30 | /** | 30 | /** |
| @@ -34,7 +34,7 @@ import java.util.* | @@ -34,7 +34,7 @@ import java.util.* | ||
| 34 | */ | 34 | */ |
| 35 | class LocalAudioTrack( | 35 | class LocalAudioTrack( |
| 36 | name: String, | 36 | name: String, |
| 37 | - mediaTrack: org.webrtc.AudioTrack | 37 | + mediaTrack: livekit.org.webrtc.AudioTrack |
| 38 | ) : AudioTrack(name, mediaTrack) { | 38 | ) : AudioTrack(name, mediaTrack) { |
| 39 | var enabled: Boolean | 39 | var enabled: Boolean |
| 40 | get() = executeBlockingOnRTCThread { rtcTrack.enabled() } | 40 | get() = executeBlockingOnRTCThread { rtcTrack.enabled() } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -28,7 +28,7 @@ import dagger.assisted.AssistedInject | @@ -28,7 +28,7 @@ import dagger.assisted.AssistedInject | ||
| 28 | import io.livekit.android.room.DefaultsManager | 28 | import io.livekit.android.room.DefaultsManager |
| 29 | import io.livekit.android.room.track.screencapture.ScreenCaptureConnection | 29 | import io.livekit.android.room.track.screencapture.ScreenCaptureConnection |
| 30 | import io.livekit.android.room.track.screencapture.ScreenCaptureService | 30 | import io.livekit.android.room.track.screencapture.ScreenCaptureService |
| 31 | -import org.webrtc.* | 31 | +import livekit.org.webrtc.* |
| 32 | import java.util.* | 32 | import java.util.* |
| 33 | 33 | ||
| 34 | class LocalScreencastVideoTrack | 34 | class LocalScreencastVideoTrack |
| @@ -38,7 +38,7 @@ constructor( | @@ -38,7 +38,7 @@ constructor( | ||
| 38 | @Assisted source: VideoSource, | 38 | @Assisted source: VideoSource, |
| 39 | @Assisted name: String, | 39 | @Assisted name: String, |
| 40 | @Assisted options: LocalVideoTrackOptions, | 40 | @Assisted options: LocalVideoTrackOptions, |
| 41 | - @Assisted rtcTrack: org.webrtc.VideoTrack, | 41 | + @Assisted rtcTrack: livekit.org.webrtc.VideoTrack, |
| 42 | @Assisted mediaProjectionCallback: MediaProjectionCallback, | 42 | @Assisted mediaProjectionCallback: MediaProjectionCallback, |
| 43 | peerConnectionFactory: PeerConnectionFactory, | 43 | peerConnectionFactory: PeerConnectionFactory, |
| 44 | context: Context, | 44 | context: Context, |
| @@ -104,7 +104,7 @@ constructor( | @@ -104,7 +104,7 @@ constructor( | ||
| 104 | source: VideoSource, | 104 | source: VideoSource, |
| 105 | name: String, | 105 | name: String, |
| 106 | options: LocalVideoTrackOptions, | 106 | options: LocalVideoTrackOptions, |
| 107 | - rtcTrack: org.webrtc.VideoTrack, | 107 | + rtcTrack: livekit.org.webrtc.VideoTrack, |
| 108 | mediaProjectionCallback: MediaProjectionCallback, | 108 | mediaProjectionCallback: MediaProjectionCallback, |
| 109 | ): LocalScreencastVideoTrack | 109 | ): LocalScreencastVideoTrack |
| 110 | } | 110 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -37,22 +37,22 @@ import io.livekit.android.util.FlowObservable | @@ -37,22 +37,22 @@ import io.livekit.android.util.FlowObservable | ||
| 37 | import io.livekit.android.util.LKLog | 37 | import io.livekit.android.util.LKLog |
| 38 | import io.livekit.android.util.flowDelegate | 38 | import io.livekit.android.util.flowDelegate |
| 39 | import livekit.LivekitModels | 39 | import livekit.LivekitModels |
| 40 | -import livekit.LivekitModels.VideoQuality | ||
| 41 | import livekit.LivekitRtc | 40 | import livekit.LivekitRtc |
| 42 | import livekit.LivekitRtc.SubscribedCodec | 41 | import livekit.LivekitRtc.SubscribedCodec |
| 43 | -import org.webrtc.CameraVideoCapturer | ||
| 44 | -import org.webrtc.CameraVideoCapturer.CameraEventsHandler | ||
| 45 | -import org.webrtc.EglBase | ||
| 46 | -import org.webrtc.MediaStreamTrack | ||
| 47 | -import org.webrtc.PeerConnectionFactory | ||
| 48 | -import org.webrtc.RtpParameters | ||
| 49 | -import org.webrtc.RtpSender | ||
| 50 | -import org.webrtc.RtpTransceiver | ||
| 51 | -import org.webrtc.SurfaceTextureHelper | ||
| 52 | -import org.webrtc.VideoCapturer | ||
| 53 | -import org.webrtc.VideoProcessor | ||
| 54 | -import org.webrtc.VideoSource | 42 | +import livekit.org.webrtc.CameraVideoCapturer |
| 43 | +import livekit.org.webrtc.CameraVideoCapturer.CameraEventsHandler | ||
| 44 | +import livekit.org.webrtc.EglBase | ||
| 45 | +import livekit.org.webrtc.MediaStreamTrack | ||
| 46 | +import livekit.org.webrtc.PeerConnectionFactory | ||
| 47 | +import livekit.org.webrtc.RtpParameters | ||
| 48 | +import livekit.org.webrtc.RtpSender | ||
| 49 | +import livekit.org.webrtc.RtpTransceiver | ||
| 50 | +import livekit.org.webrtc.SurfaceTextureHelper | ||
| 51 | +import livekit.org.webrtc.VideoCapturer | ||
| 52 | +import livekit.org.webrtc.VideoProcessor | ||
| 53 | +import livekit.org.webrtc.VideoSource | ||
| 55 | import java.util.UUID | 54 | import java.util.UUID |
| 55 | +import livekit.LivekitModels.VideoQuality as ProtoVideoQuality | ||
| 56 | 56 | ||
| 57 | /** | 57 | /** |
| 58 | * A representation of a local video track (generally input coming from camera or screen). | 58 | * A representation of a local video track (generally input coming from camera or screen). |
| @@ -66,7 +66,7 @@ constructor( | @@ -66,7 +66,7 @@ constructor( | ||
| 66 | @Assisted private var source: VideoSource, | 66 | @Assisted private var source: VideoSource, |
| 67 | @Assisted name: String, | 67 | @Assisted name: String, |
| 68 | @Assisted options: LocalVideoTrackOptions, | 68 | @Assisted options: LocalVideoTrackOptions, |
| 69 | - @Assisted rtcTrack: org.webrtc.VideoTrack, | 69 | + @Assisted rtcTrack: livekit.org.webrtc.VideoTrack, |
| 70 | private val peerConnectionFactory: PeerConnectionFactory, | 70 | private val peerConnectionFactory: PeerConnectionFactory, |
| 71 | private val context: Context, | 71 | private val context: Context, |
| 72 | private val eglBase: EglBase, | 72 | private val eglBase: EglBase, |
| @@ -74,7 +74,7 @@ constructor( | @@ -74,7 +74,7 @@ constructor( | ||
| 74 | private val trackFactory: Factory, | 74 | private val trackFactory: Factory, |
| 75 | ) : VideoTrack(name, rtcTrack) { | 75 | ) : VideoTrack(name, rtcTrack) { |
| 76 | 76 | ||
| 77 | - override var rtcTrack: org.webrtc.VideoTrack = rtcTrack | 77 | + override var rtcTrack: livekit.org.webrtc.VideoTrack = rtcTrack |
| 78 | internal set | 78 | internal set |
| 79 | 79 | ||
| 80 | internal var codec: String? = null | 80 | internal var codec: String? = null |
| @@ -274,14 +274,14 @@ constructor( | @@ -274,14 +274,14 @@ constructor( | ||
| 274 | 274 | ||
| 275 | if (encodings.firstOrNull()?.scalabilityMode != null) { | 275 | if (encodings.firstOrNull()?.scalabilityMode != null) { |
| 276 | val encoding = encodings.first() | 276 | val encoding = encodings.first() |
| 277 | - var maxQuality = VideoQuality.OFF | 277 | + var maxQuality = ProtoVideoQuality.OFF |
| 278 | for (quality in qualities) { | 278 | for (quality in qualities) { |
| 279 | - if (quality.enabled && (maxQuality == VideoQuality.OFF || quality.quality.number > maxQuality.number)) { | 279 | + if (quality.enabled && (maxQuality == ProtoVideoQuality.OFF || quality.quality.number > maxQuality.number)) { |
| 280 | maxQuality = quality.quality | 280 | maxQuality = quality.quality |
| 281 | } | 281 | } |
| 282 | } | 282 | } |
| 283 | 283 | ||
| 284 | - if (maxQuality == VideoQuality.OFF) { | 284 | + if (maxQuality == ProtoVideoQuality.OFF) { |
| 285 | if (encoding.active) { | 285 | if (encoding.active) { |
| 286 | LKLog.v { "setting svc track to disabled" } | 286 | LKLog.v { "setting svc track to disabled" } |
| 287 | encoding.active = false | 287 | encoding.active = false |
| @@ -379,7 +379,7 @@ constructor( | @@ -379,7 +379,7 @@ constructor( | ||
| 379 | source: VideoSource, | 379 | source: VideoSource, |
| 380 | name: String, | 380 | name: String, |
| 381 | options: LocalVideoTrackOptions, | 381 | options: LocalVideoTrackOptions, |
| 382 | - rtcTrack: org.webrtc.VideoTrack, | 382 | + rtcTrack: livekit.org.webrtc.VideoTrack, |
| 383 | ): LocalVideoTrack | 383 | ): LocalVideoTrack |
| 384 | } | 384 | } |
| 385 | 385 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room.track | 17 | package io.livekit.android.room.track |
| 18 | 18 | ||
| 19 | -import org.webrtc.RtpParameters | 19 | +import livekit.org.webrtc.RtpParameters |
| 20 | 20 | ||
| 21 | data class LocalVideoTrackOptions( | 21 | data class LocalVideoTrackOptions( |
| 22 | val isScreencast: Boolean = false, | 22 | val isScreencast: Boolean = false, |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,9 +17,9 @@ | @@ -17,9 +17,9 @@ | ||
| 17 | package io.livekit.android.room.track | 17 | package io.livekit.android.room.track |
| 18 | 18 | ||
| 19 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread | 19 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread |
| 20 | -import org.webrtc.AudioTrack | ||
| 21 | -import org.webrtc.AudioTrackSink | ||
| 22 | -import org.webrtc.RtpReceiver | 20 | +import livekit.org.webrtc.AudioTrack |
| 21 | +import livekit.org.webrtc.AudioTrackSink | ||
| 22 | +import livekit.org.webrtc.RtpReceiver | ||
| 23 | 23 | ||
| 24 | class RemoteAudioTrack( | 24 | class RemoteAudioTrack( |
| 25 | name: String, | 25 | name: String, |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -34,7 +34,6 @@ class RemoteTrackPublication( | @@ -34,7 +34,6 @@ class RemoteTrackPublication( | ||
| 34 | private val ioDispatcher: CoroutineDispatcher, | 34 | private val ioDispatcher: CoroutineDispatcher, |
| 35 | ) : TrackPublication(info, track, participant) { | 35 | ) : TrackPublication(info, track, participant) { |
| 36 | 36 | ||
| 37 | - @OptIn(FlowPreview::class) | ||
| 38 | override var track: Track? | 37 | override var track: Track? |
| 39 | get() = super.track | 38 | get() = super.track |
| 40 | set(value) { | 39 | set(value) { |
| @@ -67,7 +66,7 @@ class RemoteTrackPublication( | @@ -67,7 +66,7 @@ class RemoteTrackPublication( | ||
| 67 | 66 | ||
| 68 | private var unsubscribed: Boolean = false | 67 | private var unsubscribed: Boolean = false |
| 69 | private var disabled: Boolean = false | 68 | private var disabled: Boolean = false |
| 70 | - private var videoQuality: LivekitModels.VideoQuality? = LivekitModels.VideoQuality.HIGH | 69 | + private var videoQuality: VideoQuality? = VideoQuality.HIGH |
| 71 | private var videoDimensions: Track.Dimensions? = null | 70 | private var videoDimensions: Track.Dimensions? = null |
| 72 | private var fps: Int? = null | 71 | private var fps: Int? = null |
| 73 | 72 | ||
| @@ -123,7 +122,7 @@ class RemoteTrackPublication( | @@ -123,7 +122,7 @@ class RemoteTrackPublication( | ||
| 123 | unsubscribed = !subscribed | 122 | unsubscribed = !subscribed |
| 124 | val participant = this.participant.get() as? RemoteParticipant ?: return | 123 | val participant = this.participant.get() as? RemoteParticipant ?: return |
| 125 | val participantTracks = with(LivekitModels.ParticipantTracks.newBuilder()) { | 124 | val participantTracks = with(LivekitModels.ParticipantTracks.newBuilder()) { |
| 126 | - participantSid = participant.sid | 125 | + participantSid = participant.sid.value |
| 127 | addTrackSids(sid) | 126 | addTrackSids(sid) |
| 128 | build() | 127 | build() |
| 129 | } | 128 | } |
| @@ -152,7 +151,7 @@ class RemoteTrackPublication( | @@ -152,7 +151,7 @@ class RemoteTrackPublication( | ||
| 152 | * | 151 | * |
| 153 | * Will override previous calls to [setVideoDimensions]. | 152 | * Will override previous calls to [setVideoDimensions]. |
| 154 | */ | 153 | */ |
| 155 | - fun setVideoQuality(quality: LivekitModels.VideoQuality) { | 154 | + fun setVideoQuality(quality: VideoQuality) { |
| 156 | if (isAutoManaged || | 155 | if (isAutoManaged || |
| 157 | !subscribed || | 156 | !subscribed || |
| 158 | quality == videoQuality || | 157 | quality == videoQuality || |
| @@ -223,7 +222,7 @@ class RemoteTrackPublication( | @@ -223,7 +222,7 @@ class RemoteTrackPublication( | ||
| 223 | val participant = this.participant.get() as? RemoteParticipant ?: return | 222 | val participant = this.participant.get() as? RemoteParticipant ?: return |
| 224 | 223 | ||
| 225 | val rtcTrack = track?.rtcTrack | 224 | val rtcTrack = track?.rtcTrack |
| 226 | - if (rtcTrack is org.webrtc.VideoTrack) { | 225 | + if (rtcTrack is livekit.org.webrtc.VideoTrack) { |
| 227 | rtcTrack.setShouldReceive(!disabled) | 226 | rtcTrack.setShouldReceive(!disabled) |
| 228 | } | 227 | } |
| 229 | 228 | ||
| @@ -231,7 +230,7 @@ class RemoteTrackPublication( | @@ -231,7 +230,7 @@ class RemoteTrackPublication( | ||
| 231 | sid, | 230 | sid, |
| 232 | disabled, | 231 | disabled, |
| 233 | videoDimensions, | 232 | videoDimensions, |
| 234 | - videoQuality, | 233 | + videoQuality?.toProto(), |
| 235 | fps, | 234 | fps, |
| 236 | ) | 235 | ) |
| 237 | } | 236 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -23,14 +23,14 @@ import io.livekit.android.room.track.video.VideoSinkVisibility | @@ -23,14 +23,14 @@ import io.livekit.android.room.track.video.VideoSinkVisibility | ||
| 23 | import io.livekit.android.room.track.video.ViewVisibility | 23 | import io.livekit.android.room.track.video.ViewVisibility |
| 24 | import io.livekit.android.util.LKLog | 24 | import io.livekit.android.util.LKLog |
| 25 | import kotlinx.coroutines.* | 25 | import kotlinx.coroutines.* |
| 26 | -import org.webrtc.RtpReceiver | ||
| 27 | -import org.webrtc.VideoSink | 26 | +import livekit.org.webrtc.RtpReceiver |
| 27 | +import livekit.org.webrtc.VideoSink | ||
| 28 | import javax.inject.Named | 28 | import javax.inject.Named |
| 29 | import kotlin.math.max | 29 | import kotlin.math.max |
| 30 | 30 | ||
| 31 | class RemoteVideoTrack( | 31 | class RemoteVideoTrack( |
| 32 | name: String, | 32 | name: String, |
| 33 | - rtcTrack: org.webrtc.VideoTrack, | 33 | + rtcTrack: livekit.org.webrtc.VideoTrack, |
| 34 | val autoManageVideo: Boolean = false, | 34 | val autoManageVideo: Boolean = false, |
| 35 | @Named(InjectionNames.DISPATCHER_DEFAULT) | 35 | @Named(InjectionNames.DISPATCHER_DEFAULT) |
| 36 | private val dispatcher: CoroutineDispatcher, | 36 | private val dispatcher: CoroutineDispatcher, |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -24,9 +24,9 @@ import io.livekit.android.webrtc.getStats | @@ -24,9 +24,9 @@ import io.livekit.android.webrtc.getStats | ||
| 24 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread | 24 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread |
| 25 | import livekit.LivekitModels | 25 | import livekit.LivekitModels |
| 26 | import livekit.LivekitRtc | 26 | import livekit.LivekitRtc |
| 27 | -import org.webrtc.MediaStreamTrack | ||
| 28 | -import org.webrtc.RTCStatsCollectorCallback | ||
| 29 | -import org.webrtc.RTCStatsReport | 27 | +import livekit.org.webrtc.MediaStreamTrack |
| 28 | +import livekit.org.webrtc.RTCStatsCollectorCallback | ||
| 29 | +import livekit.org.webrtc.RTCStatsReport | ||
| 30 | 30 | ||
| 31 | abstract class Track( | 31 | abstract class Track( |
| 32 | name: String, | 32 | name: String, |
| 1 | +/* | ||
| 2 | + * Copyright 2024 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package io.livekit.android.room.track | ||
| 18 | + | ||
| 19 | +import livekit.LivekitModels | ||
| 20 | + | ||
| 21 | +enum class VideoQuality { | ||
| 22 | + LOW, | ||
| 23 | + MEDIUM, | ||
| 24 | + HIGH, | ||
| 25 | + ; | ||
| 26 | + | ||
| 27 | + fun toProto(): LivekitModels.VideoQuality { | ||
| 28 | + return when (this) { | ||
| 29 | + LOW -> LivekitModels.VideoQuality.LOW | ||
| 30 | + MEDIUM -> LivekitModels.VideoQuality.MEDIUM | ||
| 31 | + HIGH -> LivekitModels.VideoQuality.HIGH | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + companion object { | ||
| 36 | + fun fromProto(quality: LivekitModels.VideoQuality): VideoQuality? { | ||
| 37 | + return when (quality) { | ||
| 38 | + LivekitModels.VideoQuality.LOW -> LOW | ||
| 39 | + LivekitModels.VideoQuality.MEDIUM -> MEDIUM | ||
| 40 | + LivekitModels.VideoQuality.HIGH -> HIGH | ||
| 41 | + LivekitModels.VideoQuality.OFF -> null | ||
| 42 | + LivekitModels.VideoQuality.UNRECOGNIZED -> null | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,8 +17,8 @@ | @@ -17,8 +17,8 @@ | ||
| 17 | package io.livekit.android.room.track | 17 | package io.livekit.android.room.track |
| 18 | 18 | ||
| 19 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread | 19 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread |
| 20 | -import org.webrtc.VideoSink | ||
| 21 | -import org.webrtc.VideoTrack | 20 | +import livekit.org.webrtc.VideoSink |
| 21 | +import livekit.org.webrtc.VideoTrack | ||
| 22 | 22 | ||
| 23 | abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) : | 23 | abstract class VideoTrack(name: String, override val rtcTrack: VideoTrack) : |
| 24 | Track(name, Kind.VIDEO, rtcTrack) { | 24 | Track(name, Kind.VIDEO, rtcTrack) { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -33,10 +33,10 @@ import androidx.core.app.NotificationCompat | @@ -33,10 +33,10 @@ import androidx.core.app.NotificationCompat | ||
| 33 | * This a simple default foreground service to display a notification while screen | 33 | * This a simple default foreground service to display a notification while screen |
| 34 | * capturing. | 34 | * capturing. |
| 35 | */ | 35 | */ |
| 36 | - | ||
| 37 | open class ScreenCaptureService : Service() { | 36 | open class ScreenCaptureService : Service() { |
| 38 | private var binder: IBinder = ScreenCaptureBinder() | 37 | private var binder: IBinder = ScreenCaptureBinder() |
| 39 | private var bindCount = 0 | 38 | private var bindCount = 0 |
| 39 | + | ||
| 40 | override fun onBind(intent: Intent?): IBinder { | 40 | override fun onBind(intent: Intent?): IBinder { |
| 41 | bindCount++ | 41 | bindCount++ |
| 42 | return binder | 42 | return binder |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -22,9 +22,9 @@ import android.graphics.Matrix | @@ -22,9 +22,9 @@ import android.graphics.Matrix | ||
| 22 | import android.graphics.Paint | 22 | import android.graphics.Paint |
| 23 | import android.os.Build | 23 | import android.os.Build |
| 24 | import android.view.Surface | 24 | import android.view.Surface |
| 25 | -import org.webrtc.CapturerObserver | ||
| 26 | -import org.webrtc.SurfaceTextureHelper | ||
| 27 | -import org.webrtc.VideoCapturer | 25 | +import livekit.org.webrtc.CapturerObserver |
| 26 | +import livekit.org.webrtc.SurfaceTextureHelper | ||
| 27 | +import livekit.org.webrtc.VideoCapturer | ||
| 28 | 28 | ||
| 29 | /** | 29 | /** |
| 30 | * A [VideoCapturer] that can be manually driven by passing in [Bitmap]. | 30 | * A [VideoCapturer] that can be manually driven by passing in [Bitmap]. |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -23,14 +23,17 @@ import android.hardware.camera2.CameraManager | @@ -23,14 +23,17 @@ import android.hardware.camera2.CameraManager | ||
| 23 | import io.livekit.android.room.track.CameraPosition | 23 | import io.livekit.android.room.track.CameraPosition |
| 24 | import io.livekit.android.room.track.LocalVideoTrackOptions | 24 | import io.livekit.android.room.track.LocalVideoTrackOptions |
| 25 | import io.livekit.android.util.LKLog | 25 | import io.livekit.android.util.LKLog |
| 26 | -import org.webrtc.Camera1Capturer | ||
| 27 | -import org.webrtc.Camera1Enumerator | ||
| 28 | -import org.webrtc.Camera1Helper | ||
| 29 | -import org.webrtc.Camera2Capturer | ||
| 30 | -import org.webrtc.Camera2Enumerator | ||
| 31 | -import org.webrtc.CameraEnumerator | ||
| 32 | -import org.webrtc.VideoCapturer | ||
| 33 | - | 26 | +import livekit.org.webrtc.Camera1Capturer |
| 27 | +import livekit.org.webrtc.Camera1Enumerator | ||
| 28 | +import livekit.org.webrtc.Camera1Helper | ||
| 29 | +import livekit.org.webrtc.Camera2Capturer | ||
| 30 | +import livekit.org.webrtc.Camera2Enumerator | ||
| 31 | +import livekit.org.webrtc.CameraEnumerator | ||
| 32 | +import livekit.org.webrtc.VideoCapturer | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * Various utils for handling camera capturers. | ||
| 36 | + */ | ||
| 34 | object CameraCapturerUtils { | 37 | object CameraCapturerUtils { |
| 35 | 38 | ||
| 36 | /** | 39 | /** |
livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraEventsDispatchHandler.kt
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room.track.video | 17 | package io.livekit.android.room.track.video |
| 18 | 18 | ||
| 19 | -import org.webrtc.CameraVideoCapturer.CameraEventsHandler | 19 | +import livekit.org.webrtc.CameraVideoCapturer.CameraEventsHandler |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Dispatches CameraEventsHandler callbacks to registered handlers. | 22 | * Dispatches CameraEventsHandler callbacks to registered handlers. |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -44,8 +44,16 @@ class ComposeVisibility : VideoSinkVisibility() { | @@ -44,8 +44,16 @@ class ComposeVisibility : VideoSinkVisibility() { | ||
| 44 | return Track.Dimensions(width, height) | 44 | return Track.Dimensions(width, height) |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | - // Note, LayoutCoordinates are mutable and may be reused. | 47 | + /** |
| 48 | + * To be called from a compose view, using `Modifier.onGloballyPositioned`. | ||
| 49 | + * | ||
| 50 | + * Example: | ||
| 51 | + * ``` | ||
| 52 | + * modifier = Modifier.onGloballyPositioned { videoSinkVisibility.onGloballyPositioned(it) } | ||
| 53 | + * ``` | ||
| 54 | + */ | ||
| 48 | fun onGloballyPositioned(layoutCoordinates: LayoutCoordinates) { | 55 | fun onGloballyPositioned(layoutCoordinates: LayoutCoordinates) { |
| 56 | + // Note, LayoutCoordinates are mutable and may be reused. | ||
| 49 | coordinates = layoutCoordinates | 57 | coordinates = layoutCoordinates |
| 50 | val visible = isVisible() | 58 | val visible = isVisible() |
| 51 | val size = size() | 59 | val size = size() |
| @@ -58,6 +66,18 @@ class ComposeVisibility : VideoSinkVisibility() { | @@ -58,6 +66,18 @@ class ComposeVisibility : VideoSinkVisibility() { | ||
| 58 | lastSize = size | 66 | lastSize = size |
| 59 | } | 67 | } |
| 60 | 68 | ||
| 69 | + /** | ||
| 70 | + * To be called when the associated compose view no longer exists. | ||
| 71 | + * | ||
| 72 | + * Example: | ||
| 73 | + * ``` | ||
| 74 | + * DisposableEffect(room, videoTrack) { | ||
| 75 | + * onDispose { | ||
| 76 | + * videoSinkVisibility.onDispose() | ||
| 77 | + * } | ||
| 78 | + * } | ||
| 79 | + * ``` | ||
| 80 | + */ | ||
| 61 | fun onDispose() { | 81 | fun onDispose() { |
| 62 | if (coordinates == null) { | 82 | if (coordinates == null) { |
| 63 | return | 83 | return |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
| 17 | package io.livekit.android.room.track.video | 17 | package io.livekit.android.room.track.video |
| 18 | 18 | ||
| 19 | import android.hardware.camera2.CameraManager | 19 | import android.hardware.camera2.CameraManager |
| 20 | -import org.webrtc.* | 20 | +import livekit.org.webrtc.* |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * @suppress | 23 | * @suppress |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -32,7 +32,14 @@ import java.util.Observable | @@ -32,7 +32,14 @@ import java.util.Observable | ||
| 32 | * is used. | 32 | * is used. |
| 33 | */ | 33 | */ |
| 34 | abstract class VideoSinkVisibility : Observable() { | 34 | abstract class VideoSinkVisibility : Observable() { |
| 35 | + /** | ||
| 36 | + * @return whether this VideoSink is visible or not. | ||
| 37 | + */ | ||
| 35 | abstract fun isVisible(): Boolean | 38 | abstract fun isVisible(): Boolean |
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * @return the dimensions of this VideoSink. | ||
| 42 | + */ | ||
| 36 | abstract fun size(): Track.Dimensions | 43 | abstract fun size(): Track.Dimensions |
| 37 | 44 | ||
| 38 | /** | 45 | /** |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,15 +21,15 @@ import kotlinx.coroutines.runBlocking | @@ -21,15 +21,15 @@ import kotlinx.coroutines.runBlocking | ||
| 21 | import kotlinx.coroutines.suspendCancellableCoroutine | 21 | import kotlinx.coroutines.suspendCancellableCoroutine |
| 22 | import kotlinx.coroutines.sync.Mutex | 22 | import kotlinx.coroutines.sync.Mutex |
| 23 | import kotlinx.coroutines.sync.withLock | 23 | import kotlinx.coroutines.sync.withLock |
| 24 | -import org.webrtc.MediaConstraints | ||
| 25 | -import org.webrtc.PeerConnection | ||
| 26 | -import org.webrtc.SdpObserver | ||
| 27 | -import org.webrtc.SessionDescription | 24 | +import livekit.org.webrtc.MediaConstraints |
| 25 | +import livekit.org.webrtc.PeerConnection | ||
| 26 | +import livekit.org.webrtc.SdpObserver | ||
| 27 | +import livekit.org.webrtc.SessionDescription | ||
| 28 | import kotlin.coroutines.Continuation | 28 | import kotlin.coroutines.Continuation |
| 29 | import kotlin.coroutines.resume | 29 | import kotlin.coroutines.resume |
| 30 | import kotlin.coroutines.suspendCoroutine | 30 | import kotlin.coroutines.suspendCoroutine |
| 31 | 31 | ||
| 32 | -open class CoroutineSdpObserver : SdpObserver { | 32 | +internal open class CoroutineSdpObserver : SdpObserver { |
| 33 | 33 | ||
| 34 | private val stateLock = Mutex() | 34 | private val stateLock = Mutex() |
| 35 | private var createOutcome: Either<SessionDescription, String?>? = null | 35 | private var createOutcome: Either<SessionDescription, String?>? = null |
| @@ -136,25 +136,25 @@ open class CoroutineSdpObserver : SdpObserver { | @@ -136,25 +136,25 @@ open class CoroutineSdpObserver : SdpObserver { | ||
| 136 | } | 136 | } |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | -suspend fun PeerConnection.createOffer(constraints: MediaConstraints): Either<SessionDescription, String?> { | 139 | +internal suspend fun PeerConnection.createOffer(constraints: MediaConstraints): Either<SessionDescription, String?> { |
| 140 | val observer = CoroutineSdpObserver() | 140 | val observer = CoroutineSdpObserver() |
| 141 | this.createOffer(observer, constraints) | 141 | this.createOffer(observer, constraints) |
| 142 | return observer.awaitCreate() | 142 | return observer.awaitCreate() |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | -suspend fun PeerConnection.createAnswer(constraints: MediaConstraints): Either<SessionDescription, String?> { | 145 | +internal suspend fun PeerConnection.createAnswer(constraints: MediaConstraints): Either<SessionDescription, String?> { |
| 146 | val observer = CoroutineSdpObserver() | 146 | val observer = CoroutineSdpObserver() |
| 147 | this.createAnswer(observer, constraints) | 147 | this.createAnswer(observer, constraints) |
| 148 | return observer.awaitCreate() | 148 | return observer.awaitCreate() |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | -suspend fun PeerConnection.setRemoteDescription(description: SessionDescription): Either<Unit, String?> { | 151 | +internal suspend fun PeerConnection.setRemoteDescription(description: SessionDescription): Either<Unit, String?> { |
| 152 | val observer = CoroutineSdpObserver() | 152 | val observer = CoroutineSdpObserver() |
| 153 | this.setRemoteDescription(observer, description) | 153 | this.setRemoteDescription(observer, description) |
| 154 | return observer.awaitSet() | 154 | return observer.awaitSet() |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | -suspend fun PeerConnection.setLocalDescription(description: SessionDescription): Either<Unit, String?> { | 157 | +internal suspend fun PeerConnection.setLocalDescription(description: SessionDescription): Either<Unit, String?> { |
| 158 | val observer = CoroutineSdpObserver() | 158 | val observer = CoroutineSdpObserver() |
| 159 | this.setLocalDescription(observer, description) | 159 | this.setLocalDescription(observer, description) |
| 160 | return observer.awaitSet() | 160 | return observer.awaitSet() |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -22,7 +22,7 @@ import io.livekit.android.room.track.VideoPreset169 | @@ -22,7 +22,7 @@ import io.livekit.android.room.track.VideoPreset169 | ||
| 22 | import io.livekit.android.room.track.VideoPreset43 | 22 | import io.livekit.android.room.track.VideoPreset43 |
| 23 | import io.livekit.android.room.track.video.ScalabilityMode | 23 | import io.livekit.android.room.track.video.ScalabilityMode |
| 24 | import livekit.LivekitModels | 24 | import livekit.LivekitModels |
| 25 | -import org.webrtc.RtpParameters | 25 | +import livekit.org.webrtc.RtpParameters |
| 26 | import kotlin.math.abs | 26 | import kotlin.math.abs |
| 27 | import kotlin.math.ceil | 27 | import kotlin.math.ceil |
| 28 | import kotlin.math.max | 28 | import kotlin.math.max |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room.util | 17 | package io.livekit.android.room.util |
| 18 | 18 | ||
| 19 | -import org.webrtc.MediaConstraints | 19 | +import livekit.org.webrtc.MediaConstraints |
| 20 | 20 | ||
| 21 | object MediaConstraintKeys { | 21 | object MediaConstraintKeys { |
| 22 | const val OFFER_TO_RECV_AUDIO = "OfferToReceiveAudio" | 22 | const val OFFER_TO_RECV_AUDIO = "OfferToReceiveAudio" |
| 1 | -/* | ||
| 2 | - * Copyright 2023 LiveKit, Inc. | ||
| 3 | - * | ||
| 4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | - * you may not use this file except in compliance with the License. | ||
| 6 | - * You may obtain a copy of the License at | ||
| 7 | - * | ||
| 8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | - * | ||
| 10 | - * Unless required by applicable law or agreed to in writing, software | ||
| 11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | - * See the License for the specific language governing permissions and | ||
| 14 | - * limitations under the License. | ||
| 15 | - */ | ||
| 16 | - | ||
| 17 | -package io.livekit.android.room.util | ||
| 18 | - | ||
| 19 | -import org.webrtc.DataChannel | ||
| 20 | - | ||
| 21 | -fun DataChannel.unpackedTrackLabel(): Triple<String, String, String> { | ||
| 22 | - val parts = label().split("|") | ||
| 23 | - val participantSid: String | ||
| 24 | - val trackSid: String | ||
| 25 | - val name: String | ||
| 26 | - | ||
| 27 | - if (parts.count() == 3) { | ||
| 28 | - participantSid = parts[0] | ||
| 29 | - trackSid = parts[1] | ||
| 30 | - name = parts[2] | ||
| 31 | - } else { | ||
| 32 | - participantSid = "" | ||
| 33 | - trackSid = "" | ||
| 34 | - name = "" | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - return Triple(participantSid, trackSid, name) | ||
| 38 | -} |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -25,7 +25,7 @@ interface NetworkInfo { | @@ -25,7 +25,7 @@ interface NetworkInfo { | ||
| 25 | fun getNetworkType(): NetworkType | 25 | fun getNetworkType(): NetworkType |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | -class AndroidNetworkInfo(private val context: Context) : NetworkInfo { | 28 | +internal class AndroidNetworkInfo(private val context: Context) : NetworkInfo { |
| 29 | override fun getNetworkType(): NetworkType { | 29 | override fun getNetworkType(): NetworkType { |
| 30 | val connectivityManager = | 30 | val connectivityManager = |
| 31 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager ?: return NetworkType.UNKNOWN | 31 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager ?: return NetworkType.UNKNOWN |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -18,7 +18,7 @@ package io.livekit.android.util | @@ -18,7 +18,7 @@ package io.livekit.android.util | ||
| 18 | 18 | ||
| 19 | import kotlinx.coroutines.* | 19 | import kotlinx.coroutines.* |
| 20 | 20 | ||
| 21 | -fun <T, R> debounce( | 21 | +internal fun <T, R> debounce( |
| 22 | waitMs: Long = 300L, | 22 | waitMs: Long = 300L, |
| 23 | coroutineScope: CoroutineScope, | 23 | coroutineScope: CoroutineScope, |
| 24 | destinationFunction: suspend (T) -> R, | 24 | destinationFunction: suspend (T) -> R, |
| @@ -33,6 +33,6 @@ fun <T, R> debounce( | @@ -33,6 +33,6 @@ fun <T, R> debounce( | ||
| 33 | } | 33 | } |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | -fun <R> ((Unit) -> R).invoke() { | 36 | +internal fun <R> ((Unit) -> R).invoke() { |
| 37 | this.invoke(Unit) | 37 | this.invoke(Unit) |
| 38 | } | 38 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,11 +16,11 @@ | @@ -16,11 +16,11 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.util | 17 | package io.livekit.android.util |
| 18 | 18 | ||
| 19 | -sealed class Either<out A, out B> { | 19 | +internal sealed class Either<out A, out B> { |
| 20 | class Left<out A>(val value: A) : Either<A, Nothing>() | 20 | class Left<out A>(val value: A) : Either<A, Nothing>() |
| 21 | class Right<out B>(val value: B) : Either<Nothing, B>() | 21 | class Right<out B>(val value: B) : Either<Nothing, B>() |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | -fun <A> Either<A, String?>?.nullSafe(): Either<A, String?> { | 24 | +internal fun <A> Either<A, String?>?.nullSafe(): Either<A, String?> { |
| 25 | return this ?: Either.Right("null") | 25 | return this ?: Either.Right("null") |
| 26 | } | 26 | } |
| @@ -57,6 +57,10 @@ internal val <T> KProperty0<T>.delegate: Any? | @@ -57,6 +57,10 @@ internal val <T> KProperty0<T>.delegate: Any? | ||
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | +/** | ||
| 61 | + * @return the flow associated with a [FlowObservable] property, | ||
| 62 | + * which can be collected upon to observe changes in the value. | ||
| 63 | + */ | ||
| 60 | @Suppress("UNCHECKED_CAST") | 64 | @Suppress("UNCHECKED_CAST") |
| 61 | val <T> KProperty0<T>.flow: StateFlow<T> | 65 | val <T> KProperty0<T>.flow: StateFlow<T> |
| 62 | get() = delegate as StateFlow<T> | 66 | get() = delegate as StateFlow<T> |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -20,7 +20,7 @@ import com.google.protobuf.MessageLite | @@ -20,7 +20,7 @@ import com.google.protobuf.MessageLite | ||
| 20 | import okio.ByteString | 20 | import okio.ByteString |
| 21 | import okio.ByteString.Companion.toByteString | 21 | import okio.ByteString.Companion.toByteString |
| 22 | 22 | ||
| 23 | -fun MessageLite.toOkioByteString(): ByteString { | 23 | +internal fun MessageLite.toOkioByteString(): ByteString { |
| 24 | val byteArray = toByteArray() | 24 | val byteArray = toByteArray() |
| 25 | return byteArray.toByteString(0, byteArray.size) | 25 | return byteArray.toByteString(0, byteArray.size) |
| 26 | } | 26 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,12 +16,12 @@ | @@ -16,12 +16,12 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.webrtc | 17 | package io.livekit.android.webrtc |
| 18 | 18 | ||
| 19 | -import org.webrtc.EglBase | ||
| 20 | -import org.webrtc.SoftwareVideoDecoderFactory | ||
| 21 | -import org.webrtc.VideoCodecInfo | ||
| 22 | -import org.webrtc.VideoDecoder | ||
| 23 | -import org.webrtc.VideoDecoderFactory | ||
| 24 | -import org.webrtc.WrappedVideoDecoderFactory | 19 | +import livekit.org.webrtc.EglBase |
| 20 | +import livekit.org.webrtc.SoftwareVideoDecoderFactory | ||
| 21 | +import livekit.org.webrtc.VideoCodecInfo | ||
| 22 | +import livekit.org.webrtc.VideoDecoder | ||
| 23 | +import livekit.org.webrtc.VideoDecoderFactory | ||
| 24 | +import livekit.org.webrtc.WrappedVideoDecoderFactory | ||
| 25 | 25 | ||
| 26 | open class CustomVideoDecoderFactory( | 26 | open class CustomVideoDecoderFactory( |
| 27 | sharedContext: EglBase.Context?, | 27 | sharedContext: EglBase.Context?, |
| @@ -31,10 +31,16 @@ open class CustomVideoDecoderFactory( | @@ -31,10 +31,16 @@ open class CustomVideoDecoderFactory( | ||
| 31 | private val softwareVideoDecoderFactory = SoftwareVideoDecoderFactory() | 31 | private val softwareVideoDecoderFactory = SoftwareVideoDecoderFactory() |
| 32 | private val wrappedVideoDecoderFactory = WrappedVideoDecoderFactory(sharedContext) | 32 | private val wrappedVideoDecoderFactory = WrappedVideoDecoderFactory(sharedContext) |
| 33 | 33 | ||
| 34 | + /** | ||
| 35 | + * Set to true to force software codecs. | ||
| 36 | + */ | ||
| 34 | fun setForceSWCodec(forceSWCodec: Boolean) { | 37 | fun setForceSWCodec(forceSWCodec: Boolean) { |
| 35 | this.forceSWCodec = forceSWCodec | 38 | this.forceSWCodec = forceSWCodec |
| 36 | } | 39 | } |
| 37 | 40 | ||
| 41 | + /** | ||
| 42 | + * Set a list of codecs for which to use software codecs. | ||
| 43 | + */ | ||
| 38 | fun setForceSWCodecList(forceSWCodecs: List<String>) { | 44 | fun setForceSWCodecList(forceSWCodecs: List<String>) { |
| 39 | this.forceSWCodecs = forceSWCodecs | 45 | this.forceSWCodecs = forceSWCodecs |
| 40 | } | 46 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,11 +16,11 @@ | @@ -16,11 +16,11 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.webrtc | 17 | package io.livekit.android.webrtc |
| 18 | 18 | ||
| 19 | -import org.webrtc.EglBase | ||
| 20 | -import org.webrtc.SoftwareVideoEncoderFactory | ||
| 21 | -import org.webrtc.VideoCodecInfo | ||
| 22 | -import org.webrtc.VideoEncoder | ||
| 23 | -import org.webrtc.VideoEncoderFactory | 19 | +import livekit.org.webrtc.EglBase |
| 20 | +import livekit.org.webrtc.SoftwareVideoEncoderFactory | ||
| 21 | +import livekit.org.webrtc.VideoCodecInfo | ||
| 22 | +import livekit.org.webrtc.VideoEncoder | ||
| 23 | +import livekit.org.webrtc.VideoEncoderFactory | ||
| 24 | 24 | ||
| 25 | open class CustomVideoEncoderFactory( | 25 | open class CustomVideoEncoderFactory( |
| 26 | sharedContext: EglBase.Context?, | 26 | sharedContext: EglBase.Context?, |
| @@ -37,10 +37,16 @@ open class CustomVideoEncoderFactory( | @@ -37,10 +37,16 @@ open class CustomVideoEncoderFactory( | ||
| 37 | SimulcastVideoEncoderFactoryWrapper(sharedContext, enableIntelVp8Encoder, enableH264HighProfile) | 37 | SimulcastVideoEncoderFactoryWrapper(sharedContext, enableIntelVp8Encoder, enableH264HighProfile) |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | + /** | ||
| 41 | + * Set to true to force software codecs. | ||
| 42 | + */ | ||
| 40 | fun setForceSWCodec(forceSWCodec: Boolean) { | 43 | fun setForceSWCodec(forceSWCodec: Boolean) { |
| 41 | this.forceSWCodec = forceSWCodec | 44 | this.forceSWCodec = forceSWCodec |
| 42 | } | 45 | } |
| 43 | 46 | ||
| 47 | + /** | ||
| 48 | + * Set a list of codecs for which to use software codecs. | ||
| 49 | + */ | ||
| 44 | fun setForceSWCodecList(forceSWCodecs: List<String>) { | 50 | fun setForceSWCodecList(forceSWCodecs: List<String>) { |
| 45 | this.forceSWCodecs = forceSWCodecs | 51 | this.forceSWCodecs = forceSWCodecs |
| 46 | } | 52 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -20,9 +20,9 @@ import android.gov.nist.javax.sdp.fields.AttributeField | @@ -20,9 +20,9 @@ import android.gov.nist.javax.sdp.fields.AttributeField | ||
| 20 | import android.javax.sdp.MediaDescription | 20 | import android.javax.sdp.MediaDescription |
| 21 | import io.livekit.android.util.LKLog | 21 | import io.livekit.android.util.LKLog |
| 22 | 22 | ||
| 23 | -data class SdpRtp(val payload: Long, val codec: String, val rate: Long?, val encoding: String?) | 23 | +internal data class SdpRtp(val payload: Long, val codec: String, val rate: Long?, val encoding: String?) |
| 24 | 24 | ||
| 25 | -fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> { | 25 | +internal fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> { |
| 26 | return getAttributes(true) | 26 | return getAttributes(true) |
| 27 | .filterIsInstance<AttributeField>() | 27 | .filterIsInstance<AttributeField>() |
| 28 | .filter { it.attribute.name == "rtpmap" } | 28 | .filter { it.attribute.name == "rtpmap" } |
| @@ -37,23 +37,23 @@ fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> { | @@ -37,23 +37,23 @@ fun MediaDescription.getRtps(): List<Pair<AttributeField, SdpRtp>> { | ||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | private val RTP = """(\d*) ([\w\-.]*)(?:\s*/(\d*)(?:\s*/(\S*))?)?""".toRegex() | 39 | private val RTP = """(\d*) ([\w\-.]*)(?:\s*/(\d*)(?:\s*/(\S*))?)?""".toRegex() |
| 40 | -fun tryParseRtp(string: String): SdpRtp? { | 40 | +internal fun tryParseRtp(string: String): SdpRtp? { |
| 41 | val match = RTP.matchEntire(string) ?: return null | 41 | val match = RTP.matchEntire(string) ?: return null |
| 42 | val (payload, codec, rate, encoding) = match.destructured | 42 | val (payload, codec, rate, encoding) = match.destructured |
| 43 | return SdpRtp(payload.toLong(), codec, toOptionalLong(rate), toOptionalString(encoding)) | 43 | return SdpRtp(payload.toLong(), codec, toOptionalLong(rate), toOptionalString(encoding)) |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | -data class SdpMsid( | 46 | +internal data class SdpMsid( |
| 47 | /** holds the msid-id (and msid-appdata if available) */ | 47 | /** holds the msid-id (and msid-appdata if available) */ |
| 48 | val value: String, | 48 | val value: String, |
| 49 | ) | 49 | ) |
| 50 | 50 | ||
| 51 | -fun MediaDescription.getMsid(): SdpMsid? { | 51 | +internal fun MediaDescription.getMsid(): SdpMsid? { |
| 52 | val attribute = getAttribute("msid") ?: return null | 52 | val attribute = getAttribute("msid") ?: return null |
| 53 | return SdpMsid(attribute) | 53 | return SdpMsid(attribute) |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | -data class SdpFmtp(val payload: Long, val config: String) { | 56 | +internal data class SdpFmtp(val payload: Long, val config: String) { |
| 57 | fun toAttributeField(): AttributeField { | 57 | fun toAttributeField(): AttributeField { |
| 58 | return AttributeField().apply { | 58 | return AttributeField().apply { |
| 59 | name = "fmtp" | 59 | name = "fmtp" |
| @@ -62,7 +62,7 @@ data class SdpFmtp(val payload: Long, val config: String) { | @@ -62,7 +62,7 @@ data class SdpFmtp(val payload: Long, val config: String) { | ||
| 62 | } | 62 | } |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | -fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> { | 65 | +internal fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> { |
| 66 | return getAttributes(true) | 66 | return getAttributes(true) |
| 67 | .filterIsInstance<AttributeField>() | 67 | .filterIsInstance<AttributeField>() |
| 68 | .filter { it.attribute.name == "fmtp" } | 68 | .filter { it.attribute.name == "fmtp" } |
| @@ -77,13 +77,13 @@ fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> { | @@ -77,13 +77,13 @@ fun MediaDescription.getFmtps(): List<Pair<AttributeField, SdpFmtp>> { | ||
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | private val FMTP = """(\d*) ([\S| ]*)""".toRegex() | 79 | private val FMTP = """(\d*) ([\S| ]*)""".toRegex() |
| 80 | -fun tryParseFmtp(string: String): SdpFmtp? { | 80 | +internal fun tryParseFmtp(string: String): SdpFmtp? { |
| 81 | val match = FMTP.matchEntire(string) ?: return null | 81 | val match = FMTP.matchEntire(string) ?: return null |
| 82 | val (payload, config) = match.destructured | 82 | val (payload, config) = match.destructured |
| 83 | return SdpFmtp(payload.toLong(), config) | 83 | return SdpFmtp(payload.toLong(), config) |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | -data class SdpExt(val value: Long, val direction: String?, val encryptUri: String?, val uri: String, val config: String?) { | 86 | +internal data class SdpExt(val value: Long, val direction: String?, val encryptUri: String?, val uri: String, val config: String?) { |
| 87 | fun toAttributeField(): AttributeField { | 87 | fun toAttributeField(): AttributeField { |
| 88 | return AttributeField().apply { | 88 | return AttributeField().apply { |
| 89 | name = "extmap" | 89 | name = "extmap" |
| @@ -104,7 +104,7 @@ data class SdpExt(val value: Long, val direction: String?, val encryptUri: Strin | @@ -104,7 +104,7 @@ data class SdpExt(val value: Long, val direction: String?, val encryptUri: Strin | ||
| 104 | } | 104 | } |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | -fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> { | 107 | +internal fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> { |
| 108 | return getAttributes(true) | 108 | return getAttributes(true) |
| 109 | .filterIsInstance<AttributeField>() | 109 | .filterIsInstance<AttributeField>() |
| 110 | .filter { it.attribute.name == "extmap" } | 110 | .filter { it.attribute.name == "extmap" } |
| @@ -119,11 +119,11 @@ fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> { | @@ -119,11 +119,11 @@ fun MediaDescription.getExts(): List<Pair<AttributeField, SdpExt>> { | ||
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | private val EXT = """(\d+)(?:/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?""".toRegex() | 121 | private val EXT = """(\d+)(?:/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?""".toRegex() |
| 122 | -fun tryParseExt(string: String): SdpExt? { | 122 | +internal fun tryParseExt(string: String): SdpExt? { |
| 123 | val match = EXT.matchEntire(string) ?: return null | 123 | val match = EXT.matchEntire(string) ?: return null |
| 124 | val (value, direction, encryptUri, uri, config) = match.destructured | 124 | val (value, direction, encryptUri, uri, config) = match.destructured |
| 125 | return SdpExt(value.toLong(), toOptionalString(direction), toOptionalString(encryptUri), uri, toOptionalString(config)) | 125 | return SdpExt(value.toLong(), toOptionalString(direction), toOptionalString(encryptUri), uri, toOptionalString(config)) |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | -fun toOptionalLong(str: String): Long? = if (str.isEmpty()) null else str.toLong() | ||
| 129 | -fun toOptionalString(str: String): String? = str.ifEmpty { null } | 128 | +internal fun toOptionalLong(str: String): Long? = if (str.isEmpty()) null else str.toLong() |
| 129 | +internal fun toOptionalString(str: String): String? = str.ifEmpty { null } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,8 +16,8 @@ | @@ -16,8 +16,8 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.webrtc | 17 | package io.livekit.android.webrtc |
| 18 | 18 | ||
| 19 | -import org.webrtc.PeerConnection | ||
| 20 | -import org.webrtc.PeerConnection.RTCConfiguration | 19 | +import livekit.org.webrtc.PeerConnection |
| 20 | +import livekit.org.webrtc.PeerConnection.RTCConfiguration | ||
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * Completed state is a valid state for a connected connection, so this should be used | 23 | * Completed state is a valid state for a connected connection, so this should be used |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -18,10 +18,10 @@ package io.livekit.android.webrtc | @@ -18,10 +18,10 @@ package io.livekit.android.webrtc | ||
| 18 | 18 | ||
| 19 | import io.livekit.android.util.LKLog | 19 | import io.livekit.android.util.LKLog |
| 20 | import kotlinx.coroutines.suspendCancellableCoroutine | 20 | import kotlinx.coroutines.suspendCancellableCoroutine |
| 21 | -import org.webrtc.MediaStreamTrack | ||
| 22 | -import org.webrtc.RTCStats | ||
| 23 | -import org.webrtc.RTCStatsCollectorCallback | ||
| 24 | -import org.webrtc.RTCStatsReport | 21 | +import livekit.org.webrtc.MediaStreamTrack |
| 22 | +import livekit.org.webrtc.RTCStats | ||
| 23 | +import livekit.org.webrtc.RTCStatsCollectorCallback | ||
| 24 | +import livekit.org.webrtc.RTCStatsReport | ||
| 25 | import kotlin.coroutines.resume | 25 | import kotlin.coroutines.resume |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -18,9 +18,9 @@ package io.livekit.android.webrtc | @@ -18,9 +18,9 @@ package io.livekit.android.webrtc | ||
| 18 | 18 | ||
| 19 | import io.livekit.android.dagger.CapabilitiesGetter | 19 | import io.livekit.android.dagger.CapabilitiesGetter |
| 20 | import io.livekit.android.util.LKLog | 20 | import io.livekit.android.util.LKLog |
| 21 | -import org.webrtc.MediaStreamTrack | ||
| 22 | -import org.webrtc.RtpCapabilities | ||
| 23 | -import org.webrtc.RtpTransceiver | 21 | +import livekit.org.webrtc.MediaStreamTrack |
| 22 | +import livekit.org.webrtc.RtpCapabilities | ||
| 23 | +import livekit.org.webrtc.RtpTransceiver | ||
| 24 | 24 | ||
| 25 | internal fun RtpTransceiver.sortVideoCodecPreferences(targetCodec: String, capabilitiesGetter: CapabilitiesGetter) { | 25 | internal fun RtpTransceiver.sortVideoCodecPreferences(targetCodec: String, capabilitiesGetter: CapabilitiesGetter) { |
| 26 | val capabilities = capabilitiesGetter(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO) | 26 | val capabilities = capabilitiesGetter(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,9 +17,9 @@ | @@ -17,9 +17,9 @@ | ||
| 17 | package io.livekit.android.webrtc | 17 | package io.livekit.android.webrtc |
| 18 | 18 | ||
| 19 | import livekit.LivekitRtc | 19 | import livekit.LivekitRtc |
| 20 | -import org.webrtc.SessionDescription | 20 | +import livekit.org.webrtc.SessionDescription |
| 21 | 21 | ||
| 22 | -fun SessionDescription.toProtoSessionDescription(): LivekitRtc.SessionDescription { | 22 | +internal fun SessionDescription.toProtoSessionDescription(): LivekitRtc.SessionDescription { |
| 23 | val sdBuilder = LivekitRtc.SessionDescription.newBuilder() | 23 | val sdBuilder = LivekitRtc.SessionDescription.newBuilder() |
| 24 | sdBuilder.sdp = description | 24 | sdBuilder.sdp = description |
| 25 | sdBuilder.type = type.canonicalForm() | 25 | sdBuilder.type = type.canonicalForm() |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
| 17 | package io.livekit.android.webrtc | 17 | package io.livekit.android.webrtc |
| 18 | 18 | ||
| 19 | import io.livekit.android.util.LKLog | 19 | import io.livekit.android.util.LKLog |
| 20 | -import org.webrtc.* | 20 | +import livekit.org.webrtc.* |
| 21 | import java.util.concurrent.Callable | 21 | import java.util.concurrent.Callable |
| 22 | import java.util.concurrent.ExecutorService | 22 | import java.util.concurrent.ExecutorService |
| 23 | import java.util.concurrent.Executors | 23 | import java.util.concurrent.Executors |
livekit-android-sdk/src/main/java/io/livekit/android/webrtc/peerconnection/PeerConnectionResource.kt
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,10 +16,10 @@ | @@ -16,10 +16,10 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.webrtc.peerconnection | 17 | package io.livekit.android.webrtc.peerconnection |
| 18 | 18 | ||
| 19 | -import org.webrtc.PeerConnection | ||
| 20 | -import org.webrtc.RtpReceiver | ||
| 21 | -import org.webrtc.RtpSender | ||
| 22 | -import org.webrtc.RtpTransceiver | 19 | +import livekit.org.webrtc.PeerConnection |
| 20 | +import livekit.org.webrtc.RtpReceiver | ||
| 21 | +import livekit.org.webrtc.RtpSender | ||
| 22 | +import livekit.org.webrtc.RtpTransceiver | ||
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Objects obtained through [PeerConnection] are transient, | 25 | * Objects obtained through [PeerConnection] are transient, |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | * limitations under the License. | 14 | * limitations under the License. |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | -package org.webrtc | 17 | +package livekit.org.webrtc |
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| 20 | * A helper to access package-protected methods used in [Camera2Session] | 20 | * A helper to access package-protected methods used in [Camera2Session] |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | * limitations under the License. | 14 | * limitations under the License. |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | -package org.webrtc | 17 | +package livekit.org.webrtc |
| 18 | 18 | ||
| 19 | import android.hardware.camera2.CameraManager | 19 | import android.hardware.camera2.CameraManager |
| 20 | 20 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -30,6 +30,7 @@ import io.livekit.android.util.toOkioByteString | @@ -30,6 +30,7 @@ import io.livekit.android.util.toOkioByteString | ||
| 30 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 30 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 31 | import kotlinx.coroutines.launch | 31 | import kotlinx.coroutines.launch |
| 32 | import livekit.LivekitRtc | 32 | import livekit.LivekitRtc |
| 33 | +import livekit.org.webrtc.PeerConnection | ||
| 33 | import okhttp3.Protocol | 34 | import okhttp3.Protocol |
| 34 | import okhttp3.Request | 35 | import okhttp3.Request |
| 35 | import okhttp3.Response | 36 | import okhttp3.Response |
| @@ -37,7 +38,6 @@ import okio.ByteString | @@ -37,7 +38,6 @@ import okio.ByteString | ||
| 37 | import org.junit.Before | 38 | import org.junit.Before |
| 38 | import org.junit.runner.RunWith | 39 | import org.junit.runner.RunWith |
| 39 | import org.robolectric.RobolectricTestRunner | 40 | import org.robolectric.RobolectricTestRunner |
| 40 | -import org.webrtc.PeerConnection | ||
| 41 | 41 | ||
| 42 | @ExperimentalCoroutinesApi | 42 | @ExperimentalCoroutinesApi |
| 43 | @RunWith(RobolectricTestRunner::class) | 43 | @RunWith(RobolectricTestRunner::class) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.AudioTrack | 19 | +import livekit.org.webrtc.AudioTrack |
| 20 | 20 | ||
| 21 | class MockAudioStreamTrack( | 21 | class MockAudioStreamTrack( |
| 22 | val id: String = "id", | 22 | val id: String = "id", |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.DataChannel | 19 | +import livekit.org.webrtc.DataChannel |
| 20 | 20 | ||
| 21 | class MockDataChannel(private val label: String?) : DataChannel(1L) { | 21 | class MockDataChannel(private val label: String?) : DataChannel(1L) { |
| 22 | 22 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -18,7 +18,7 @@ package io.livekit.android.mock | @@ -18,7 +18,7 @@ package io.livekit.android.mock | ||
| 18 | 18 | ||
| 19 | import android.graphics.SurfaceTexture | 19 | import android.graphics.SurfaceTexture |
| 20 | import android.view.Surface | 20 | import android.view.Surface |
| 21 | -import org.webrtc.EglBase | 21 | +import livekit.org.webrtc.EglBase |
| 22 | 22 | ||
| 23 | class MockEglBase( | 23 | class MockEglBase( |
| 24 | private val eglContext: EglBase.Context = EglBase.Context { 0 }, | 24 | private val eglContext: EglBase.Context = EglBase.Context { 0 }, |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,9 +16,9 @@ | @@ -16,9 +16,9 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.AudioTrack | ||
| 20 | -import org.webrtc.MediaStream | ||
| 21 | -import org.webrtc.VideoTrack | 19 | +import livekit.org.webrtc.AudioTrack |
| 20 | +import livekit.org.webrtc.MediaStream | ||
| 21 | +import livekit.org.webrtc.VideoTrack | ||
| 22 | 22 | ||
| 23 | fun createMediaStreamId(participantSid: String, trackSid: String) = | 23 | fun createMediaStreamId(participantSid: String, trackSid: String) = |
| 24 | "$participantSid|$trackSid" | 24 | "$participantSid|$trackSid" |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.MediaStreamTrack | 19 | +import livekit.org.webrtc.MediaStreamTrack |
| 20 | 20 | ||
| 21 | class MockMediaStreamTrack( | 21 | class MockMediaStreamTrack( |
| 22 | val id: String = "id", | 22 | val id: String = "id", |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,23 +16,23 @@ | @@ -16,23 +16,23 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.DataChannel | ||
| 20 | -import org.webrtc.IceCandidate | ||
| 21 | -import org.webrtc.MediaConstraints | ||
| 22 | -import org.webrtc.MediaStream | ||
| 23 | -import org.webrtc.MediaStreamTrack | ||
| 24 | -import org.webrtc.MockRtpTransceiver | ||
| 25 | -import org.webrtc.NativePeerConnectionFactory | ||
| 26 | -import org.webrtc.PeerConnection | ||
| 27 | -import org.webrtc.RTCStatsCollectorCallback | ||
| 28 | -import org.webrtc.RTCStatsReport | ||
| 29 | -import org.webrtc.RtcCertificatePem | ||
| 30 | -import org.webrtc.RtpReceiver | ||
| 31 | -import org.webrtc.RtpSender | ||
| 32 | -import org.webrtc.RtpTransceiver | ||
| 33 | -import org.webrtc.SdpObserver | ||
| 34 | -import org.webrtc.SessionDescription | ||
| 35 | -import org.webrtc.StatsObserver | 19 | +import livekit.org.webrtc.DataChannel |
| 20 | +import livekit.org.webrtc.IceCandidate | ||
| 21 | +import livekit.org.webrtc.MediaConstraints | ||
| 22 | +import livekit.org.webrtc.MediaStream | ||
| 23 | +import livekit.org.webrtc.MediaStreamTrack | ||
| 24 | +import livekit.org.webrtc.MockRtpTransceiver | ||
| 25 | +import livekit.org.webrtc.NativePeerConnectionFactory | ||
| 26 | +import livekit.org.webrtc.PeerConnection | ||
| 27 | +import livekit.org.webrtc.RTCStatsCollectorCallback | ||
| 28 | +import livekit.org.webrtc.RTCStatsReport | ||
| 29 | +import livekit.org.webrtc.RtcCertificatePem | ||
| 30 | +import livekit.org.webrtc.RtpReceiver | ||
| 31 | +import livekit.org.webrtc.RtpSender | ||
| 32 | +import livekit.org.webrtc.RtpTransceiver | ||
| 33 | +import livekit.org.webrtc.SdpObserver | ||
| 34 | +import livekit.org.webrtc.SessionDescription | ||
| 35 | +import livekit.org.webrtc.StatsObserver | ||
| 36 | 36 | ||
| 37 | private class MockNativePeerConnectionFactory : NativePeerConnectionFactory { | 37 | private class MockNativePeerConnectionFactory : NativePeerConnectionFactory { |
| 38 | override fun createNativePeerConnection(): Long = 0L | 38 | override fun createNativePeerConnection(): Long = 0L |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,8 +16,8 @@ | @@ -16,8 +16,8 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | +import livekit.org.webrtc.RtpReceiver | ||
| 19 | import org.mockito.Mockito | 20 | import org.mockito.Mockito |
| 20 | -import org.webrtc.RtpReceiver | ||
| 21 | 21 | ||
| 22 | object MockRtpReceiver { | 22 | object MockRtpReceiver { |
| 23 | fun create(): RtpReceiver { | 23 | fun create(): RtpReceiver { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,8 +16,8 @@ | @@ -16,8 +16,8 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | +import livekit.org.webrtc.RtpSender | ||
| 19 | import org.mockito.Mockito | 20 | import org.mockito.Mockito |
| 20 | -import org.webrtc.RtpSender | ||
| 21 | 21 | ||
| 22 | object MockRtpSender { | 22 | object MockRtpSender { |
| 23 | fun create(): RtpSender { | 23 | fun create(): RtpSender { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -17,9 +17,9 @@ | @@ -17,9 +17,9 @@ | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | import android.content.Context | 19 | import android.content.Context |
| 20 | -import org.webrtc.CapturerObserver | ||
| 21 | -import org.webrtc.SurfaceTextureHelper | ||
| 22 | -import org.webrtc.VideoCapturer | 20 | +import livekit.org.webrtc.CapturerObserver |
| 21 | +import livekit.org.webrtc.SurfaceTextureHelper | ||
| 22 | +import livekit.org.webrtc.VideoCapturer | ||
| 23 | 23 | ||
| 24 | class MockVideoCapturer : VideoCapturer { | 24 | class MockVideoCapturer : VideoCapturer { |
| 25 | override fun initialize(p0: SurfaceTextureHelper?, p1: Context?, p2: CapturerObserver?) { | 25 | override fun initialize(p0: SurfaceTextureHelper?, p1: Context?, p2: CapturerObserver?) { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,6 +16,6 @@ | @@ -16,6 +16,6 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.VideoSource | 19 | +import livekit.org.webrtc.VideoSource |
| 20 | 20 | ||
| 21 | class MockVideoSource(nativeSource: Long = 100) : VideoSource(nativeSource) | 21 | class MockVideoSource(nativeSource: Long = 100) : VideoSource(nativeSource) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -16,8 +16,8 @@ | @@ -16,8 +16,8 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.mock | 17 | package io.livekit.android.mock |
| 18 | 18 | ||
| 19 | -import org.webrtc.VideoSink | ||
| 20 | -import org.webrtc.VideoTrack | 19 | +import livekit.org.webrtc.VideoSink |
| 20 | +import livekit.org.webrtc.VideoTrack | ||
| 21 | import java.util.UUID | 21 | import java.util.UUID |
| 22 | 22 | ||
| 23 | class MockVideoStreamTrack( | 23 | class MockVideoStreamTrack( |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -21,24 +21,24 @@ import livekit.LivekitModels | @@ -21,24 +21,24 @@ import livekit.LivekitModels | ||
| 21 | object TestData { | 21 | object TestData { |
| 22 | 22 | ||
| 23 | val LOCAL_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { | 23 | val LOCAL_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { |
| 24 | - sid = "local_audio_track_sid" | 24 | + sid = "TR_local_audio_track_sid" |
| 25 | type = LivekitModels.TrackType.AUDIO | 25 | type = LivekitModels.TrackType.AUDIO |
| 26 | build() | 26 | build() |
| 27 | } | 27 | } |
| 28 | val LOCAL_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { | 28 | val LOCAL_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { |
| 29 | - sid = "local_video_track_sid" | 29 | + sid = "TR_local_video_track_sid" |
| 30 | type = LivekitModels.TrackType.VIDEO | 30 | type = LivekitModels.TrackType.VIDEO |
| 31 | build() | 31 | build() |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | val REMOTE_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { | 34 | val REMOTE_AUDIO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { |
| 35 | - sid = "remote_audio_track_sid" | 35 | + sid = "TR_remote_audio_track_sid" |
| 36 | type = LivekitModels.TrackType.AUDIO | 36 | type = LivekitModels.TrackType.AUDIO |
| 37 | build() | 37 | build() |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | val REMOTE_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { | 40 | val REMOTE_VIDEO_TRACK = with(LivekitModels.TrackInfo.newBuilder()) { |
| 41 | - sid = "remote_video_track_sid" | 41 | + sid = "TR_remote_video_track_sid" |
| 42 | type = LivekitModels.TrackType.VIDEO | 42 | type = LivekitModels.TrackType.VIDEO |
| 43 | build() | 43 | build() |
| 44 | } | 44 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -23,7 +23,7 @@ import dagger.Provides | @@ -23,7 +23,7 @@ import dagger.Provides | ||
| 23 | import io.livekit.android.dagger.CapabilitiesGetter | 23 | import io.livekit.android.dagger.CapabilitiesGetter |
| 24 | import io.livekit.android.dagger.InjectionNames | 24 | import io.livekit.android.dagger.InjectionNames |
| 25 | import io.livekit.android.mock.MockEglBase | 25 | import io.livekit.android.mock.MockEglBase |
| 26 | -import org.webrtc.* | 26 | +import livekit.org.webrtc.* |
| 27 | import javax.inject.Named | 27 | import javax.inject.Named |
| 28 | import javax.inject.Singleton | 28 | import javax.inject.Singleton |
| 29 | 29 |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -22,13 +22,13 @@ import io.livekit.android.util.toPBByteString | @@ -22,13 +22,13 @@ import io.livekit.android.util.toPBByteString | ||
| 22 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 22 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 23 | import livekit.LivekitModels | 23 | import livekit.LivekitModels |
| 24 | import livekit.LivekitRtc | 24 | import livekit.LivekitRtc |
| 25 | +import livekit.org.webrtc.PeerConnection | ||
| 25 | import org.junit.Assert | 26 | import org.junit.Assert |
| 26 | import org.junit.Assert.assertEquals | 27 | import org.junit.Assert.assertEquals |
| 27 | import org.junit.Before | 28 | import org.junit.Before |
| 28 | import org.junit.Test | 29 | import org.junit.Test |
| 29 | import org.junit.runner.RunWith | 30 | import org.junit.runner.RunWith |
| 30 | import org.robolectric.RobolectricTestRunner | 31 | import org.robolectric.RobolectricTestRunner |
| 31 | -import org.webrtc.PeerConnection | ||
| 32 | 32 | ||
| 33 | @ExperimentalCoroutinesApi | 33 | @ExperimentalCoroutinesApi |
| 34 | @RunWith(RobolectricTestRunner::class) | 34 | @RunWith(RobolectricTestRunner::class) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -26,9 +26,9 @@ import io.livekit.android.mock.MockPeerConnection | @@ -26,9 +26,9 @@ import io.livekit.android.mock.MockPeerConnection | ||
| 26 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 26 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 27 | import livekit.LivekitModels.DataPacket | 27 | import livekit.LivekitModels.DataPacket |
| 28 | import livekit.LivekitModels.UserPacket | 28 | import livekit.LivekitModels.UserPacket |
| 29 | +import livekit.org.webrtc.DataChannel | ||
| 29 | import org.junit.Assert.assertEquals | 30 | import org.junit.Assert.assertEquals |
| 30 | import org.junit.Test | 31 | import org.junit.Test |
| 31 | -import org.webrtc.DataChannel | ||
| 32 | import java.nio.ByteBuffer | 32 | import java.nio.ByteBuffer |
| 33 | 33 | ||
| 34 | @OptIn(ExperimentalCoroutinesApi::class) | 34 | @OptIn(ExperimentalCoroutinesApi::class) |
| @@ -50,7 +50,7 @@ class RoomDataMockE2ETest : MockE2ETest() { | @@ -50,7 +50,7 @@ class RoomDataMockE2ETest : MockE2ETest() { | ||
| 50 | } | 50 | } |
| 51 | val dataBuffer = DataChannel.Buffer( | 51 | val dataBuffer = DataChannel.Buffer( |
| 52 | ByteBuffer.wrap(dataPacket.toByteArray()), | 52 | ByteBuffer.wrap(dataPacket.toByteArray()), |
| 53 | - true | 53 | + true, |
| 54 | ) | 54 | ) |
| 55 | 55 | ||
| 56 | subDataChannel.observer?.onMessage(dataBuffer) | 56 | subDataChannel.observer?.onMessage(dataBuffer) |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -285,7 +285,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -285,7 +285,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 285 | Assert.assertEquals(true, events[0] is RoomEvent.TrackSubscriptionPermissionChanged) | 285 | Assert.assertEquals(true, events[0] is RoomEvent.TrackSubscriptionPermissionChanged) |
| 286 | 286 | ||
| 287 | val event = events[0] as RoomEvent.TrackSubscriptionPermissionChanged | 287 | val event = events[0] as RoomEvent.TrackSubscriptionPermissionChanged |
| 288 | - Assert.assertEquals(TestData.REMOTE_PARTICIPANT.sid, event.participant.sid) | 288 | + Assert.assertEquals(TestData.REMOTE_PARTICIPANT.sid, event.participant.sid.value) |
| 289 | Assert.assertEquals(TestData.REMOTE_AUDIO_TRACK.sid, event.trackPublication.sid) | 289 | Assert.assertEquals(TestData.REMOTE_AUDIO_TRACK.sid, event.trackPublication.sid) |
| 290 | Assert.assertEquals(false, event.subscriptionAllowed) | 290 | Assert.assertEquals(false, event.subscriptionAllowed) |
| 291 | } | 291 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -22,12 +22,12 @@ import io.livekit.android.room.track.LocalAudioTrack | @@ -22,12 +22,12 @@ import io.livekit.android.room.track.LocalAudioTrack | ||
| 22 | import io.livekit.android.util.toPBByteString | 22 | import io.livekit.android.util.toPBByteString |
| 23 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 23 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 24 | import livekit.LivekitRtc | 24 | import livekit.LivekitRtc |
| 25 | +import livekit.org.webrtc.PeerConnection | ||
| 25 | import org.junit.Assert | 26 | import org.junit.Assert |
| 26 | import org.junit.Assert.assertEquals | 27 | import org.junit.Assert.assertEquals |
| 27 | import org.junit.Test | 28 | import org.junit.Test |
| 28 | import org.junit.runner.RunWith | 29 | import org.junit.runner.RunWith |
| 29 | import org.robolectric.RobolectricTestRunner | 30 | import org.robolectric.RobolectricTestRunner |
| 30 | -import org.webrtc.PeerConnection | ||
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * For tests that only target one reconnection type. | 33 | * For tests that only target one reconnection type. |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -30,9 +30,13 @@ import io.livekit.android.memory.CloseableManager | @@ -30,9 +30,13 @@ import io.livekit.android.memory.CloseableManager | ||
| 30 | import io.livekit.android.mock.* | 30 | import io.livekit.android.mock.* |
| 31 | import io.livekit.android.room.participant.LocalParticipant | 31 | import io.livekit.android.room.participant.LocalParticipant |
| 32 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 32 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 33 | +import kotlinx.coroutines.async | ||
| 33 | import kotlinx.coroutines.flow.MutableSharedFlow | 34 | import kotlinx.coroutines.flow.MutableSharedFlow |
| 34 | import kotlinx.coroutines.flow.SharedFlow | 35 | import kotlinx.coroutines.flow.SharedFlow |
| 36 | +import kotlinx.coroutines.test.advanceUntilIdle | ||
| 35 | import kotlinx.coroutines.test.runTest | 37 | import kotlinx.coroutines.test.runTest |
| 38 | +import livekit.LivekitRtc.JoinResponse | ||
| 39 | +import livekit.org.webrtc.EglBase | ||
| 36 | import org.junit.Assert.* | 40 | import org.junit.Assert.* |
| 37 | import org.junit.Before | 41 | import org.junit.Before |
| 38 | import org.junit.Rule | 42 | import org.junit.Rule |
| @@ -45,7 +49,6 @@ import org.mockito.kotlin.* | @@ -45,7 +49,6 @@ import org.mockito.kotlin.* | ||
| 45 | import org.robolectric.RobolectricTestRunner | 49 | import org.robolectric.RobolectricTestRunner |
| 46 | import org.robolectric.Shadows | 50 | import org.robolectric.Shadows |
| 47 | import org.robolectric.shadows.ShadowConnectivityManager | 51 | import org.robolectric.shadows.ShadowConnectivityManager |
| 48 | -import org.webrtc.EglBase | ||
| 49 | 52 | ||
| 50 | @ExperimentalCoroutinesApi | 53 | @ExperimentalCoroutinesApi |
| 51 | @RunWith(RobolectricTestRunner::class) | 54 | @RunWith(RobolectricTestRunner::class) |
| @@ -99,12 +102,12 @@ class RoomTest { | @@ -99,12 +102,12 @@ class RoomTest { | ||
| 99 | ) | 102 | ) |
| 100 | } | 103 | } |
| 101 | 104 | ||
| 102 | - suspend fun connect() { | 105 | + suspend fun connect(joinResponse: JoinResponse = SignalClientTest.JOIN.join) { |
| 103 | rtcEngine.stub { | 106 | rtcEngine.stub { |
| 104 | onBlocking { rtcEngine.join(any(), any(), anyOrNull(), anyOrNull()) } | 107 | onBlocking { rtcEngine.join(any(), any(), anyOrNull(), anyOrNull()) } |
| 105 | .doSuspendableAnswer { | 108 | .doSuspendableAnswer { |
| 106 | - room.onJoinResponse(SignalClientTest.JOIN.join) | ||
| 107 | - SignalClientTest.JOIN.join | 109 | + room.onJoinResponse(joinResponse) |
| 110 | + joinResponse | ||
| 108 | } | 111 | } |
| 109 | } | 112 | } |
| 110 | rtcEngine.stub { | 113 | rtcEngine.stub { |
| @@ -138,6 +141,7 @@ class RoomTest { | @@ -138,6 +141,7 @@ class RoomTest { | ||
| 138 | room.onRoomUpdate(update) | 141 | room.onRoomUpdate(update) |
| 139 | val events = eventCollector.stopCollecting() | 142 | val events = eventCollector.stopCollecting() |
| 140 | 143 | ||
| 144 | + assertEquals(update.sid, room.sid?.sid) | ||
| 141 | assertEquals(update.metadata, room.metadata) | 145 | assertEquals(update.metadata, room.metadata) |
| 142 | assertEquals(update.activeRecording, room.isRecording) | 146 | assertEquals(update.activeRecording, room.isRecording) |
| 143 | 147 | ||
| @@ -209,4 +213,23 @@ class RoomTest { | @@ -209,4 +213,23 @@ class RoomTest { | ||
| 209 | assertNull(room.name) | 213 | assertNull(room.name) |
| 210 | assertFalse(room.isRecording) | 214 | assertFalse(room.isRecording) |
| 211 | } | 215 | } |
| 216 | + | ||
| 217 | + @Test | ||
| 218 | + fun getSidSuspendsUntilPopulated() = runTest { | ||
| 219 | + val job = async { | ||
| 220 | + room.getSid() | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + assertFalse(job.isCompleted) | ||
| 224 | + connect() | ||
| 225 | + assertFalse(job.isCompleted) | ||
| 226 | + val update = SignalClientTest.ROOM_UPDATE.roomUpdate.room | ||
| 227 | + room.onRoomUpdate(update) | ||
| 228 | + | ||
| 229 | + advanceUntilIdle() | ||
| 230 | + assertTrue(job.isCompleted) | ||
| 231 | + val sid = job.await() | ||
| 232 | + | ||
| 233 | + assertEquals(update.sid, sid.sid) | ||
| 234 | + } | ||
| 212 | } | 235 | } |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -30,8 +30,15 @@ import livekit.LivekitModels | @@ -30,8 +30,15 @@ import livekit.LivekitModels | ||
| 30 | import livekit.LivekitModels.ClientConfiguration | 30 | import livekit.LivekitModels.ClientConfiguration |
| 31 | import livekit.LivekitRtc | 31 | import livekit.LivekitRtc |
| 32 | import livekit.LivekitRtc.ICEServer | 32 | import livekit.LivekitRtc.ICEServer |
| 33 | -import okhttp3.* | ||
| 34 | -import org.junit.Assert.* | 33 | +import livekit.org.webrtc.SessionDescription |
| 34 | +import okhttp3.OkHttpClient | ||
| 35 | +import okhttp3.Protocol | ||
| 36 | +import okhttp3.Request | ||
| 37 | +import okhttp3.Response | ||
| 38 | +import okhttp3.WebSocketListener | ||
| 39 | +import org.junit.Assert.assertEquals | ||
| 40 | +import org.junit.Assert.assertFalse | ||
| 41 | +import org.junit.Assert.assertTrue | ||
| 35 | import org.junit.Before | 42 | import org.junit.Before |
| 36 | import org.junit.Test | 43 | import org.junit.Test |
| 37 | import org.mockito.Mock | 44 | import org.mockito.Mock |
| @@ -40,7 +47,6 @@ import org.mockito.kotlin.any | @@ -40,7 +47,6 @@ import org.mockito.kotlin.any | ||
| 40 | import org.mockito.kotlin.argThat | 47 | import org.mockito.kotlin.argThat |
| 41 | import org.mockito.kotlin.inOrder | 48 | import org.mockito.kotlin.inOrder |
| 42 | import org.mockito.kotlin.times | 49 | import org.mockito.kotlin.times |
| 43 | -import org.webrtc.SessionDescription | ||
| 44 | 50 | ||
| 45 | @ExperimentalCoroutinesApi | 51 | @ExperimentalCoroutinesApi |
| 46 | class SignalClientTest : BaseTest() { | 52 | class SignalClientTest : BaseTest() { |
| @@ -330,7 +336,6 @@ class SignalClientTest : BaseTest() { | @@ -330,7 +336,6 @@ class SignalClientTest : BaseTest() { | ||
| 330 | join = with(LivekitRtc.JoinResponse.newBuilder()) { | 336 | join = with(LivekitRtc.JoinResponse.newBuilder()) { |
| 331 | room = with(LivekitModels.Room.newBuilder()) { | 337 | room = with(LivekitModels.Room.newBuilder()) { |
| 332 | name = "roomname" | 338 | name = "roomname" |
| 333 | - sid = "room_sid" | ||
| 334 | build() | 339 | build() |
| 335 | } | 340 | } |
| 336 | participant = TestData.LOCAL_PARTICIPANT | 341 | participant = TestData.LOCAL_PARTICIPANT |
| @@ -380,6 +385,7 @@ class SignalClientTest : BaseTest() { | @@ -380,6 +385,7 @@ class SignalClientTest : BaseTest() { | ||
| 380 | val ROOM_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) { | 385 | val ROOM_UPDATE = with(LivekitRtc.SignalResponse.newBuilder()) { |
| 381 | roomUpdate = with(LivekitRtc.RoomUpdate.newBuilder()) { | 386 | roomUpdate = with(LivekitRtc.RoomUpdate.newBuilder()) { |
| 382 | room = with(LivekitModels.Room.newBuilder()) { | 387 | room = with(LivekitModels.Room.newBuilder()) { |
| 388 | + sid = "room_sid" | ||
| 383 | metadata = "metadata" | 389 | metadata = "metadata" |
| 384 | activeRecording = true | 390 | activeRecording = true |
| 385 | build() | 391 | build() |
livekit-android-sdk/src/test/java/io/livekit/android/room/participant/LocalParticipantMockE2ETest.kt
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -30,6 +30,7 @@ import io.livekit.android.room.SignalClientTest | @@ -30,6 +30,7 @@ import io.livekit.android.room.SignalClientTest | ||
| 30 | import io.livekit.android.room.track.LocalAudioTrack | 30 | import io.livekit.android.room.track.LocalAudioTrack |
| 31 | import io.livekit.android.room.track.LocalVideoTrack | 31 | import io.livekit.android.room.track.LocalVideoTrack |
| 32 | import io.livekit.android.room.track.LocalVideoTrackOptions | 32 | import io.livekit.android.room.track.LocalVideoTrackOptions |
| 33 | +import io.livekit.android.room.track.Track | ||
| 33 | import io.livekit.android.room.track.VideoCaptureParameter | 34 | import io.livekit.android.room.track.VideoCaptureParameter |
| 34 | import io.livekit.android.room.track.VideoCodec | 35 | import io.livekit.android.room.track.VideoCodec |
| 35 | import io.livekit.android.util.toOkioByteString | 36 | import io.livekit.android.util.toOkioByteString |
| @@ -39,6 +40,7 @@ import livekit.LivekitModels | @@ -39,6 +40,7 @@ import livekit.LivekitModels | ||
| 39 | import livekit.LivekitRtc | 40 | import livekit.LivekitRtc |
| 40 | import livekit.LivekitRtc.SubscribedCodec | 41 | import livekit.LivekitRtc.SubscribedCodec |
| 41 | import livekit.LivekitRtc.SubscribedQuality | 42 | import livekit.LivekitRtc.SubscribedQuality |
| 43 | +import livekit.org.webrtc.VideoSource | ||
| 42 | import org.junit.Assert.* | 44 | import org.junit.Assert.* |
| 43 | import org.junit.Test | 45 | import org.junit.Test |
| 44 | import org.junit.runner.RunWith | 46 | import org.junit.runner.RunWith |
| @@ -46,7 +48,6 @@ import org.mockito.Mockito | @@ -46,7 +48,6 @@ import org.mockito.Mockito | ||
| 46 | import org.mockito.Mockito.mock | 48 | import org.mockito.Mockito.mock |
| 47 | import org.mockito.kotlin.argThat | 49 | import org.mockito.kotlin.argThat |
| 48 | import org.robolectric.RobolectricTestRunner | 50 | import org.robolectric.RobolectricTestRunner |
| 49 | -import org.webrtc.VideoSource | ||
| 50 | 51 | ||
| 51 | @ExperimentalCoroutinesApi | 52 | @ExperimentalCoroutinesApi |
| 52 | @RunWith(RobolectricTestRunner::class) | 53 | @RunWith(RobolectricTestRunner::class) |
| @@ -65,7 +66,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -65,7 +66,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 65 | 66 | ||
| 66 | room.disconnect() | 67 | room.disconnect() |
| 67 | 68 | ||
| 68 | - assertEquals("", room.localParticipant.sid) | 69 | + assertEquals("", room.localParticipant.sid.value) |
| 69 | assertNull(room.localParticipant.name) | 70 | assertNull(room.localParticipant.name) |
| 70 | assertNull(room.localParticipant.identity) | 71 | assertNull(room.localParticipant.identity) |
| 71 | assertNull(room.localParticipant.metadata) | 72 | assertNull(room.localParticipant.metadata) |
| @@ -74,9 +75,9 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -74,9 +75,9 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 74 | assertFalse(room.localParticipant.isSpeaking) | 75 | assertFalse(room.localParticipant.isSpeaking) |
| 75 | assertEquals(ConnectionQuality.UNKNOWN, room.localParticipant.connectionQuality) | 76 | assertEquals(ConnectionQuality.UNKNOWN, room.localParticipant.connectionQuality) |
| 76 | 77 | ||
| 77 | - assertEquals(0, room.localParticipant.tracks.values.size) | ||
| 78 | - assertEquals(0, room.localParticipant.audioTracks.size) | ||
| 79 | - assertEquals(0, room.localParticipant.videoTracks.size) | 78 | + assertEquals(0, room.localParticipant.trackPublications.values.size) |
| 79 | + assertEquals(0, room.localParticipant.audioTrackPublications.size) | ||
| 80 | + assertEquals(0, room.localParticipant.videoTrackPublications.size) | ||
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | @Test | 83 | @Test |
| @@ -97,6 +98,30 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -97,6 +98,30 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 97 | } | 98 | } |
| 98 | 99 | ||
| 99 | @Test | 100 | @Test |
| 101 | + fun publishVideoTrackRequest() = runTest { | ||
| 102 | + connect() | ||
| 103 | + wsFactory.ws.clearRequests() | ||
| 104 | + val videoTrack = createLocalTrack() | ||
| 105 | + val publishOptions = VideoTrackPublishOptions( | ||
| 106 | + name = "name", | ||
| 107 | + source = Track.Source.SCREEN_SHARE, | ||
| 108 | + stream = "stream_id", | ||
| 109 | + ) | ||
| 110 | + room.localParticipant.publishVideoTrack(videoTrack, publishOptions) | ||
| 111 | + | ||
| 112 | + // Verify the add track request gets the proper publish options set. | ||
| 113 | + val requestString = wsFactory.ws.sentRequests.first().toPBByteString() | ||
| 114 | + val sentRequest = LivekitRtc.SignalRequest.newBuilder() | ||
| 115 | + .mergeFrom(requestString) | ||
| 116 | + .build() | ||
| 117 | + | ||
| 118 | + assertTrue(sentRequest.hasAddTrack()) | ||
| 119 | + assertEquals(publishOptions.name, sentRequest.addTrack.name) | ||
| 120 | + assertEquals(publishOptions.source?.toProto(), sentRequest.addTrack.source) | ||
| 121 | + assertEquals(publishOptions.stream, sentRequest.addTrack.stream) | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @Test | ||
| 100 | fun updateMetadata() = runTest { | 125 | fun updateMetadata() = runTest { |
| 101 | connect() | 126 | connect() |
| 102 | val newMetadata = "new_metadata" | 127 | val newMetadata = "new_metadata" |
| @@ -249,7 +274,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -249,7 +274,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 249 | wsFactory.receiveMessage( | 274 | wsFactory.receiveMessage( |
| 250 | with(LivekitRtc.SignalResponse.newBuilder()) { | 275 | with(LivekitRtc.SignalResponse.newBuilder()) { |
| 251 | subscribedQualityUpdate = with(LivekitRtc.SubscribedQualityUpdate.newBuilder()) { | 276 | subscribedQualityUpdate = with(LivekitRtc.SubscribedQualityUpdate.newBuilder()) { |
| 252 | - trackSid = room.localParticipant.videoTracks.first().first.sid | 277 | + trackSid = room.localParticipant.videoTrackPublications.first().first.sid |
| 253 | addAllSubscribedCodecs( | 278 | addAllSubscribedCodecs( |
| 254 | listOf( | 279 | listOf( |
| 255 | with(SubscribedCodec.newBuilder()) { | 280 | with(SubscribedCodec.newBuilder()) { |
| 1 | /* | 1 | /* |
| 2 | - * Copyright 2023 LiveKit, Inc. | 2 | + * Copyright 2023-2024 LiveKit, Inc. |
| 3 | * | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
| @@ -54,7 +54,7 @@ class ParticipantMockE2ETest : MockE2ETest() { | @@ -54,7 +54,7 @@ class ParticipantMockE2ETest : MockE2ETest() { | ||
| 54 | 54 | ||
| 55 | assertEquals(1, events.size) | 55 | assertEquals(1, events.size) |
| 56 | assertEquals(true, events[0] is RoomEvent.TrackUnpublished) | 56 | assertEquals(true, events[0] is RoomEvent.TrackUnpublished) |
| 57 | - assertEquals(0, room.localParticipant.tracks.size) | 57 | + assertEquals(0, room.localParticipant.trackPublications.size) |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | @Test | 60 | @Test |
-
请 注册 或 登录 后发表评论