Audio noise cancellation optionns, video capture resolution control
正在显示
10 个修改的文件
包含
93 行增加
和
34 行删除
| @@ -11,7 +11,7 @@ buildscript { | @@ -11,7 +11,7 @@ buildscript { | ||
| 11 | 11 | ||
| 12 | } | 12 | } |
| 13 | dependencies { | 13 | dependencies { |
| 14 | - classpath 'com.android.tools.build:gradle:7.0.0-beta02' | 14 | + classpath 'com.android.tools.build:gradle:7.0.0-beta03' |
| 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" |
| 16 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" | 16 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" |
| 17 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" | 17 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" |
| @@ -22,7 +22,6 @@ constructor( | @@ -22,7 +22,6 @@ constructor( | ||
| 22 | ) { | 22 | ) { |
| 23 | val peerConnection: PeerConnection = connectionFactory.createPeerConnection( | 23 | val peerConnection: PeerConnection = connectionFactory.createPeerConnection( |
| 24 | config, | 24 | config, |
| 25 | - RTCEngine.CONN_CONSTRAINTS, | ||
| 26 | listener | 25 | listener |
| 27 | ) ?: throw IllegalStateException("peer connection creation failed?") | 26 | ) ?: throw IllegalStateException("peer connection creation failed?") |
| 28 | val pendingCandidates = mutableListOf<IceCandidate>() | 27 | val pendingCandidates = mutableListOf<IceCandidate>() |
| @@ -225,6 +225,7 @@ constructor( | @@ -225,6 +225,7 @@ constructor( | ||
| 225 | val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply { | 225 | val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply { |
| 226 | sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN | 226 | sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN |
| 227 | continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY | 227 | continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY |
| 228 | + enableDtlsSrtp = true | ||
| 228 | } | 229 | } |
| 229 | 230 | ||
| 230 | publisher = pctFactory.create(rtcConfig, publisherObserver) | 231 | publisher = pctFactory.create(rtcConfig, publisherObserver) |
| @@ -33,19 +33,30 @@ internal constructor( | @@ -33,19 +33,30 @@ internal constructor( | ||
| 33 | get() = tracks.values.toList() | 33 | get() = tracks.values.toList() |
| 34 | 34 | ||
| 35 | fun createAudioTrack( | 35 | fun createAudioTrack( |
| 36 | - audioConstraints: MediaConstraints = MediaConstraints(), | ||
| 37 | - name: String = "" | ||
| 38 | - ) = LocalAudioTrack.createTrack(peerConnectionFactory, audioConstraints, name) | 36 | + name: String = "", |
| 37 | + options: LocalAudioTrackOptions = LocalAudioTrackOptions(), | ||
| 38 | + ): LocalAudioTrack { | ||
| 39 | + val audioConstraints = MediaConstraints() | ||
| 40 | + val items = listOf( | ||
| 41 | + MediaConstraints.KeyValuePair("googEchoCancellation", options.echoCancellation.toString()), | ||
| 42 | + MediaConstraints.KeyValuePair("googAutoGainControl", options.autoGainControl.toString()), | ||
| 43 | + MediaConstraints.KeyValuePair("googHighpassFilter", options.highPassFilter.toString()), | ||
| 44 | + MediaConstraints.KeyValuePair("googNoiseSuppression", options.noiseSuppression.toString()), | ||
| 45 | + MediaConstraints.KeyValuePair("googTypingNoiseDetection", options.typingNoiseDetection.toString()), | ||
| 46 | + ) | ||
| 47 | +// audioConstraints.optional.addAll(items) | ||
| 48 | + return LocalAudioTrack.createTrack(peerConnectionFactory, audioConstraints, name) | ||
| 49 | + } | ||
| 39 | 50 | ||
| 40 | fun createVideoTrack( | 51 | fun createVideoTrack( |
| 41 | - isScreencast: Boolean = false, | ||
| 42 | name: String = "", | 52 | name: String = "", |
| 53 | + options: LocalVideoTrackOptions = LocalVideoTrackOptions(), | ||
| 43 | ): LocalVideoTrack { | 54 | ): LocalVideoTrack { |
| 44 | return LocalVideoTrack.createTrack( | 55 | return LocalVideoTrack.createTrack( |
| 45 | peerConnectionFactory, | 56 | peerConnectionFactory, |
| 46 | context, | 57 | context, |
| 47 | - isScreencast, | ||
| 48 | name, | 58 | name, |
| 59 | + options, | ||
| 49 | eglBase | 60 | eglBase |
| 50 | ) | 61 | ) |
| 51 | } | 62 | } |
| @@ -5,5 +5,4 @@ import org.webrtc.AudioTrack | @@ -5,5 +5,4 @@ import org.webrtc.AudioTrack | ||
| 5 | 5 | ||
| 6 | open class AudioTrack(name: String, override val rtcTrack: AudioTrack) : | 6 | open class AudioTrack(name: String, override val rtcTrack: AudioTrack) : |
| 7 | Track(name, Kind.AUDIO, rtcTrack) { | 7 | Track(name, Kind.AUDIO, rtcTrack) { |
| 8 | - | ||
| 9 | } | 8 | } |
| @@ -11,7 +11,6 @@ import java.util.* | @@ -11,7 +11,6 @@ import java.util.* | ||
| 11 | */ | 11 | */ |
| 12 | class LocalAudioTrack( | 12 | class LocalAudioTrack( |
| 13 | name: String, | 13 | name: String, |
| 14 | - audioOptions: AudioOptions? = null, | ||
| 15 | mediaTrack: org.webrtc.AudioTrack | 14 | mediaTrack: org.webrtc.AudioTrack |
| 16 | ) : AudioTrack(name, mediaTrack) { | 15 | ) : AudioTrack(name, mediaTrack) { |
| 17 | var enabled: Boolean | 16 | var enabled: Boolean |
| @@ -20,16 +19,12 @@ class LocalAudioTrack( | @@ -20,16 +19,12 @@ class LocalAudioTrack( | ||
| 20 | rtcTrack.setEnabled(value) | 19 | rtcTrack.setEnabled(value) |
| 21 | } | 20 | } |
| 22 | 21 | ||
| 23 | - var audioOptions = audioOptions | ||
| 24 | - private set | ||
| 25 | - | ||
| 26 | companion object { | 22 | companion object { |
| 27 | internal fun createTrack( | 23 | internal fun createTrack( |
| 28 | factory: PeerConnectionFactory, | 24 | factory: PeerConnectionFactory, |
| 29 | audioConstraints: MediaConstraints = MediaConstraints(), | 25 | audioConstraints: MediaConstraints = MediaConstraints(), |
| 30 | name: String = "" | 26 | name: String = "" |
| 31 | ): LocalAudioTrack { | 27 | ): LocalAudioTrack { |
| 32 | - | ||
| 33 | val audioSource = factory.createAudioSource(audioConstraints) | 28 | val audioSource = factory.createAudioSource(audioConstraints) |
| 34 | val rtcAudioTrack = | 29 | val rtcAudioTrack = |
| 35 | factory.createAudioTrack(UUID.randomUUID().toString(), audioSource) | 30 | factory.createAudioTrack(UUID.randomUUID().toString(), audioSource) |
| @@ -14,10 +14,11 @@ class LocalVideoTrack( | @@ -14,10 +14,11 @@ class LocalVideoTrack( | ||
| 14 | private val capturer: VideoCapturer, | 14 | private val capturer: VideoCapturer, |
| 15 | private val source: VideoSource, | 15 | private val source: VideoSource, |
| 16 | name: String, | 16 | name: String, |
| 17 | + private val options: LocalVideoTrackOptions, | ||
| 17 | rtcTrack: org.webrtc.VideoTrack | 18 | rtcTrack: org.webrtc.VideoTrack |
| 18 | ) : VideoTrack(name, rtcTrack) { | 19 | ) : VideoTrack(name, rtcTrack) { |
| 19 | fun startCapture() { | 20 | fun startCapture() { |
| 20 | - capturer.startCapture(400, 400, 30) | 21 | + capturer.startCapture(options.captureParams.width, options.captureParams.height, options.captureParams.maxFps) |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | override fun stop() { | 24 | override fun stop() { |
| @@ -29,12 +30,12 @@ class LocalVideoTrack( | @@ -29,12 +30,12 @@ class LocalVideoTrack( | ||
| 29 | internal fun createTrack( | 30 | internal fun createTrack( |
| 30 | peerConnectionFactory: PeerConnectionFactory, | 31 | peerConnectionFactory: PeerConnectionFactory, |
| 31 | context: Context, | 32 | context: Context, |
| 32 | - isScreencast: Boolean, | ||
| 33 | name: String, | 33 | name: String, |
| 34 | + options: LocalVideoTrackOptions, | ||
| 34 | rootEglBase: EglBase, | 35 | rootEglBase: EglBase, |
| 35 | ): LocalVideoTrack { | 36 | ): LocalVideoTrack { |
| 36 | - val source = peerConnectionFactory.createVideoSource(isScreencast) | ||
| 37 | - val capturer = createVideoCapturer(context) ?: TODO() | 37 | + val source = peerConnectionFactory.createVideoSource(options.isScreencast) |
| 38 | + val capturer = createVideoCapturer(context, options.position) ?: TODO() | ||
| 38 | capturer.initialize( | 39 | capturer.initialize( |
| 39 | SurfaceTextureHelper.create("VideoCaptureThread", rootEglBase.eglBaseContext), | 40 | SurfaceTextureHelper.create("VideoCaptureThread", rootEglBase.eglBaseContext), |
| 40 | context, | 41 | context, |
| @@ -45,16 +46,17 @@ class LocalVideoTrack( | @@ -45,16 +46,17 @@ class LocalVideoTrack( | ||
| 45 | return LocalVideoTrack( | 46 | return LocalVideoTrack( |
| 46 | capturer = capturer, | 47 | capturer = capturer, |
| 47 | source = source, | 48 | source = source, |
| 49 | + options = options, | ||
| 48 | name = name, | 50 | name = name, |
| 49 | rtcTrack = track, | 51 | rtcTrack = track, |
| 50 | ) | 52 | ) |
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | - private fun createVideoCapturer(context: Context): VideoCapturer? { | 55 | + private fun createVideoCapturer(context: Context, position: CameraPosition): VideoCapturer? { |
| 54 | val videoCapturer: VideoCapturer? = if (Camera2Enumerator.isSupported(context)) { | 56 | val videoCapturer: VideoCapturer? = if (Camera2Enumerator.isSupported(context)) { |
| 55 | - createCameraCapturer(Camera2Enumerator(context)) | 57 | + createCameraCapturer(Camera2Enumerator(context), position) |
| 56 | } else { | 58 | } else { |
| 57 | - createCameraCapturer(Camera1Enumerator(true)) | 59 | + createCameraCapturer(Camera1Enumerator(true), position) |
| 58 | } | 60 | } |
| 59 | if (videoCapturer == null) { | 61 | if (videoCapturer == null) { |
| 60 | Timber.d { "Failed to open camera" } | 62 | Timber.d { "Failed to open camera" } |
| @@ -63,24 +65,18 @@ class LocalVideoTrack( | @@ -63,24 +65,18 @@ class LocalVideoTrack( | ||
| 63 | return videoCapturer | 65 | return videoCapturer |
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | - private fun createCameraCapturer(enumerator: CameraEnumerator): VideoCapturer? { | 68 | + private fun createCameraCapturer(enumerator: CameraEnumerator, position: CameraPosition): VideoCapturer? { |
| 67 | val deviceNames = enumerator.deviceNames | 69 | val deviceNames = enumerator.deviceNames |
| 68 | 70 | ||
| 69 | - // First, try to find front facing camera | ||
| 70 | for (deviceName in deviceNames) { | 71 | for (deviceName in deviceNames) { |
| 71 | - if (enumerator.isFrontFacing(deviceName)) { | 72 | + if (enumerator.isFrontFacing(deviceName) && position == CameraPosition.FRONT) { |
| 72 | Timber.v { "Creating front facing camera capturer." } | 73 | Timber.v { "Creating front facing camera capturer." } |
| 73 | val videoCapturer = enumerator.createCapturer(deviceName, null) | 74 | val videoCapturer = enumerator.createCapturer(deviceName, null) |
| 74 | if (videoCapturer != null) { | 75 | if (videoCapturer != null) { |
| 75 | return videoCapturer | 76 | return videoCapturer |
| 76 | } | 77 | } |
| 77 | - } | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - // Front facing camera not found, try something else | ||
| 81 | - for (deviceName in deviceNames) { | ||
| 82 | - if (!enumerator.isFrontFacing(deviceName)) { | ||
| 83 | - Timber.v { "Creating other camera capturer." } | 78 | + } else if (enumerator.isBackFacing(deviceName) && position == CameraPosition.BACK) { |
| 79 | + Timber.v { "Creating back facing camera capturer." } | ||
| 84 | val videoCapturer = enumerator.createCapturer(deviceName, null) | 80 | val videoCapturer = enumerator.createCapturer(deviceName, null) |
| 85 | if (videoCapturer != null) { | 81 | if (videoCapturer != null) { |
| 86 | return videoCapturer | 82 | return videoCapturer |
livekit-android-sdk/src/main/java/io/livekit/android/room/track/LocalVideoTrackOptions.kt
0 → 100644
| 1 | +package io.livekit.android.room.track | ||
| 2 | + | ||
| 3 | +class LocalVideoTrackOptions( | ||
| 4 | + var isScreencast: Boolean = false, | ||
| 5 | + var position: CameraPosition = CameraPosition.FRONT, | ||
| 6 | + var captureParams: VideoCaptureParameter = VideoPreset.QHD.capture | ||
| 7 | +) | ||
| 8 | + | ||
| 9 | +class VideoCaptureParameter( | ||
| 10 | + val width: Int, | ||
| 11 | + val height: Int, | ||
| 12 | + val maxFps: Int, | ||
| 13 | +) | ||
| 14 | + | ||
| 15 | +class VideoEncoding( | ||
| 16 | + val maxBitrate: Int, | ||
| 17 | + val maxFps: Int, | ||
| 18 | +) | ||
| 19 | + | ||
| 20 | +enum class CameraPosition { | ||
| 21 | + FRONT, | ||
| 22 | + BACK | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + * Video presets along with suggested bitrates | ||
| 27 | + */ | ||
| 28 | +enum class VideoPreset( | ||
| 29 | + val capture: VideoCaptureParameter, | ||
| 30 | + val encoding: VideoEncoding, | ||
| 31 | +) { | ||
| 32 | + QVGA( | ||
| 33 | + VideoCaptureParameter(320, 240, 15), | ||
| 34 | + VideoEncoding(100_000, 15), | ||
| 35 | + ), | ||
| 36 | + VGA( | ||
| 37 | + VideoCaptureParameter(640, 360, 30), | ||
| 38 | + VideoEncoding(400_000, 30), | ||
| 39 | + ), | ||
| 40 | + QHD( | ||
| 41 | + VideoCaptureParameter(960, 540, 30), | ||
| 42 | + VideoEncoding(700_000, 30), | ||
| 43 | + ), | ||
| 44 | + HD( | ||
| 45 | + VideoCaptureParameter(1280, 720, 30), | ||
| 46 | + VideoEncoding(2_000_000, 30), | ||
| 47 | + ), | ||
| 48 | + FHD( | ||
| 49 | + VideoCaptureParameter(1920, 1080, 30), | ||
| 50 | + VideoEncoding(4_000_000, 30), | ||
| 51 | + ) | ||
| 52 | + | ||
| 53 | +} |
-
请 注册 或 登录 后发表评论