EncodingUtils.kt
3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package io.livekit.android.room.util
import io.livekit.android.room.track.VideoEncoding
import io.livekit.android.room.track.VideoPreset
import io.livekit.android.room.track.VideoPreset169
import io.livekit.android.room.track.VideoPreset43
import livekit.LivekitModels
import org.webrtc.RtpParameters
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
internal object EncodingUtils {
val VIDEO_RIDS = arrayOf("q", "h", "f")
// Note: maintain order from smallest to biggest.
private val PRESETS_16_9 = listOf(
VideoPreset169.QVGA,
VideoPreset169.VGA,
VideoPreset169.QHD,
VideoPreset169.HD,
VideoPreset169.FHD
)
// Note: maintain order from smallest to biggest.
private val PRESETS_4_3 = listOf(
VideoPreset43.QVGA,
VideoPreset43.VGA,
VideoPreset43.QHD,
VideoPreset43.HD,
VideoPreset43.FHD
)
fun determineAppropriateEncoding(width: Int, height: Int): VideoEncoding {
val presets = presetsForResolution(width, height)
// presets assume width is longest size
val longestSize = max(width, height)
val preset = presets
.firstOrNull { it.capture.width >= longestSize }
?: presets.last()
return preset.encoding
}
fun presetsForResolution(width: Int, height: Int): List<VideoPreset> {
val longestSize = max(width, height)
val shortestSize = min(width, height)
val aspectRatio = longestSize.toFloat() / shortestSize
return if (abs(aspectRatio - 16f / 9f) < abs(aspectRatio - 4f / 3f)) {
PRESETS_16_9
} else {
PRESETS_4_3
}
}
fun videoLayersFromEncodings(
trackWidth: Int,
trackHeight: Int,
encodings: List<RtpParameters.Encoding>
): List<LivekitModels.VideoLayer> {
return if (encodings.isEmpty()) {
listOf(
LivekitModels.VideoLayer.newBuilder().apply {
width = trackWidth
height = trackHeight
quality = LivekitModels.VideoQuality.HIGH
bitrate = 0
ssrc = 0
}.build()
)
} else {
encodings.map { encoding ->
val scaleDownBy = encoding.scaleResolutionDownBy ?: 1.0
var videoQuality = videoQualityForRid(encoding.rid ?: "")
if (videoQuality == LivekitModels.VideoQuality.UNRECOGNIZED && encodings.size == 1) {
videoQuality = LivekitModels.VideoQuality.HIGH
}
LivekitModels.VideoLayer.newBuilder().apply {
// Internally, WebRTC casts directly to int without rounding.
// https://github.com/webrtc-sdk/webrtc/blob/8c7139f8e6fa19ddf2c91510c177a19746e1ded3/media/engine/webrtc_video_engine.cc#L3676
width = (trackWidth / scaleDownBy).toInt()
height = (trackHeight / scaleDownBy).toInt()
quality = videoQuality
bitrate = encoding.maxBitrateBps ?: 0
ssrc = 0
}.build()
}
}
}
fun videoQualityForRid(rid: String): LivekitModels.VideoQuality {
return when (rid) {
"f" -> LivekitModels.VideoQuality.HIGH
"h" -> LivekitModels.VideoQuality.MEDIUM
"q" -> LivekitModels.VideoQuality.LOW
else -> LivekitModels.VideoQuality.UNRECOGNIZED
}
}
fun ridForVideoQuality(quality: LivekitModels.VideoQuality): String? {
return when (quality) {
LivekitModels.VideoQuality.HIGH -> "f"
LivekitModels.VideoQuality.MEDIUM -> "h"
LivekitModels.VideoQuality.LOW -> "q"
else -> null
}
}
}