Skip to content

Commit e89f86c

Browse files
authored
Merge pull request #2157 from pedroSG94/refactor-sdp
refactoring sdpbody
2 parents e762f0f + c6f7016 commit e89f86c

33 files changed

Lines changed: 950 additions & 382 deletions

File tree

common/src/main/java/com/pedro/common/BitBuffer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class BitBuffer(val buffer: ByteBuffer) {
9393

9494
rbsp.put(buffer, 0, headerLength)
9595

96-
var previous = buffer.position()
96+
var previous = headerLength
9797
indices.forEach {
9898
rbsp.put(buffer, previous, it + 2 - previous)
9999
previous = it + 3 // skip emulation_prevention_three_byte

common/src/main/java/com/pedro/common/Extensions.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ fun ByteBuffer.toByteArray(
7676
}
7777

7878
fun ByteBuffer.getStartCodeSize(): Int {
79-
if (this.remaining() < 4) return 0
79+
//the start code is searched using absolute indexes so the limit is the value that matters
80+
if (this.limit() < 4) return 0
8081
var startCodeSize = 0
8182
if (this.get(0).toInt() == 0x00 && this.get(1).toInt() == 0x00
8283
&& this.get(2).toInt() == 0x00 && this.get(3).toInt() == 0x01) {
@@ -348,9 +349,10 @@ fun InetAddress.addressToString(): String {
348349
fun ByteBuffer.getData(): ByteArray = removeHeader().toByteArray()
349350

350351
fun ByteBuffer.removeHeader(): ByteBuffer {
351-
val startCodeSize = this.getStartCodeSize()
352-
this.position(startCodeSize)
353-
return this.slice()
352+
val buffer = this.duplicate()
353+
val startCodeSize = buffer.getStartCodeSize()
354+
buffer.position(startCodeSize)
355+
return buffer.slice()
354356
}
355357

356358
fun ByteArray.writeUInt32(offset: Int, value: Int) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.pedro.common
2+
3+
import java.nio.ByteBuffer
4+
5+
/**
6+
* Created by pedro on 27/07/26.
7+
*
8+
* ISO/IEC 14496-10 7.3.2.1.1
9+
*
10+
*/
11+
class SpsH264Parser {
12+
var profileIdc = 0
13+
var profileCompatibility = 0
14+
var levelIdc = 0
15+
//only present in the profiles of profilesWithChromaInfo, in other case the inferred values are used
16+
var chromaFormat = 1
17+
var bitDepthLumaMinus8 = 0
18+
var bitDepthChromaMinus8 = 0
19+
20+
fun parse(sps: ByteArray) {
21+
parse(ByteBuffer.wrap(sps))
22+
}
23+
24+
fun parse(sps: ByteBuffer) {
25+
val rbsp = BitBuffer.extractRbsp(ByteBuffer.wrap(sps.getData()), 1)
26+
val bitBuffer = BitBuffer(rbsp)
27+
bitBuffer.skip(8)
28+
//profile_idc
29+
profileIdc = bitBuffer.getInt(8)
30+
//constraint_set0_flag to constraint_set5_flag and reserved_zero_2bits
31+
profileCompatibility = bitBuffer.getInt(8)
32+
//level_idc
33+
levelIdc = bitBuffer.getInt(8)
34+
//seq_parameter_set_id
35+
bitBuffer.readUE()
36+
if (profileIdc in profilesWithChromaInfo) {
37+
//chroma_format_idc
38+
chromaFormat = bitBuffer.readUE()
39+
if (chromaFormat == 3) {
40+
//separate_colour_plane_flag
41+
bitBuffer.skipBool()
42+
}
43+
//bit_depth_luma_minus8
44+
bitDepthLumaMinus8 = bitBuffer.readUE()
45+
//bit_depth_chroma_minus8
46+
bitDepthChromaMinus8 = bitBuffer.readUE()
47+
}
48+
//The buffer continue but we don't need read more
49+
}
50+
51+
companion object {
52+
private val profilesWithChromaInfo = listOf(
53+
100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135
54+
)
55+
}
56+
}

rtmp/src/main/java/com/pedro/rtmp/flv/video/config/SPSH265Parser.kt renamed to common/src/main/java/com/pedro/common/SpsH265Parser.kt

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
1-
/*
2-
* Copyright (C) 2024 pedroSG94.
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 com.pedro.rtmp.flv.video.config
1+
package com.pedro.common
182

19-
import com.pedro.common.BitBuffer
20-
import com.pedro.common.toInt
213
import java.nio.ByteBuffer
224

235
/**
@@ -26,7 +8,7 @@ import java.nio.ByteBuffer
268
* ISO/IEC 23008-2 7.3.2.2.1
279
*
2810
*/
29-
class SPSH265Parser {
11+
class SpsH265Parser {
3012
var generalProfileSpace = 0
3113
var generalTierFlag = 0
3214
var generalProfileIdc = 0
@@ -42,7 +24,7 @@ class SPSH265Parser {
4224
}
4325

4426
fun parse(sps: ByteBuffer) {
45-
val rbsp = BitBuffer.extractRbsp(sps, 2)
27+
val rbsp = BitBuffer.extractRbsp(ByteBuffer.wrap(sps.getData()), 2)
4628
val bitBuffer = BitBuffer(rbsp)
4729
//Dropping nal_unit_header
4830
bitBuffer.skip(16)
@@ -69,7 +51,7 @@ class SPSH265Parser {
6951
}
7052

7153
if (maxSubLayersMinus1 > 0) {
72-
repeat((maxSubLayersMinus1..8).count()) {
54+
repeat((maxSubLayersMinus1 until 8).count()) {
7355
bitBuffer.skip(2) // reserved_zero_2bits
7456
}
7557
}
@@ -117,4 +99,4 @@ class SPSH265Parser {
11799
bitDepthChromaMinus8 = bitBuffer.readUE()
118100
//The buffer continue but we don't need read more
119101
}
120-
}
102+
}

common/src/main/java/com/pedro/common/av1/Av1Parser.kt

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,38 +54,45 @@ class Av1Parser {
5454
val obuList = mutableListOf<Obu>()
5555
var index = 0
5656
while (index < av1Data.size) {
57-
val header = readHeader(av1Data, index)
57+
val header = readHeader(av1Data, index) ?: break
5858
index += header.size
59-
val leb128Value = readLeb128(av1Data, index)
60-
val length = av1Data.sliceArray(index until index + leb128Value.second)
61-
index += length.size
62-
val data = av1Data.sliceArray(index until index + leb128Value.first.toInt())
59+
val leb128 = if (((header[0].toInt() ushr 1) and 0x01) == 1) {
60+
val b = readLeb128(av1Data, index) ?: break
61+
index += b.size
62+
b
63+
} else {
64+
header[0] = (header[0].toInt() or 0x02).toByte()
65+
writeLeb128(av1Data.size.toLong() - index)
66+
}
67+
val leb128Length = leb128.leb128ToLength()
68+
if (index + leb128Length > av1Data.size) break //discard obu with invalid leb128
69+
val data = av1Data.sliceArray(index until index + leb128Length.toInt())
6370
index += data.size
64-
val obu = Obu(header, length, data)
65-
obuList.add(obu)
71+
obuList.add(Obu(header, leb128, data))
6672
}
6773
return obuList
6874
}
6975

70-
private fun readHeader(av1Data: ByteArray, offset: Int): ByteArray {
71-
val header = mutableListOf<Byte>()
76+
private fun readHeader(av1Data: ByteArray, offset: Int): ByteArray? {
77+
if (offset >= av1Data.size) return null
7278
val info = av1Data[offset]
73-
header.add(info)
7479
val containExtended = ((info.toInt() ushr 2) and 0x01) == 1
75-
if (containExtended) header.add(av1Data[offset + 1])
76-
return header.toByteArray()
80+
if (containExtended) {
81+
if (offset + 1 >= av1Data.size) return null
82+
return byteArrayOf(info, av1Data[offset + 1])
83+
}
84+
return byteArrayOf(info)
7785
}
7886

79-
private fun readLeb128(data: ByteArray, offset: Int): Pair<Long, Int> {
80-
var result: Long = 0
87+
private fun readLeb128(data: ByteArray, offset: Int): ByteArray? {
8188
var index = 0
8289
var b: Byte
8390
do {
91+
if (index >= 8 || offset + index >= data.size) return null
8492
b = data[offset + index]
85-
result = result or ((b.toLong() and 0x7F) shl (index * 7))
8693
index++
8794
} while (b.toInt() and 0x80 != 0)
88-
return Pair(result, index)
95+
return data.sliceArray(offset until offset + index)
8996
}
9097

9198
fun writeLeb128(length: Long) : ByteArray {
@@ -101,4 +108,22 @@ class Av1Parser {
101108
} while (remainingValue != 0L)
102109
return result.toByteArray()
103110
}
111+
112+
fun leb128Size(value: Int): Int {
113+
var size = 1
114+
var v = value ushr 7
115+
while (v != 0) {
116+
size++
117+
v = v ushr 7
118+
}
119+
return size
120+
}
121+
122+
private fun ByteArray.leb128ToLength(): Long {
123+
var result = 0L
124+
for (i in this.indices) {
125+
result = result or ((this[i].toLong() and 0x7F) shl (i * 7))
126+
}
127+
return result
128+
}
104129
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.pedro.common.av1
2+
3+
import com.pedro.common.BitBuffer
4+
import com.pedro.common.toByteArray
5+
import java.nio.ByteBuffer
6+
7+
class Av1SequenceHeaderParser {
8+
9+
var seqProfile = 0
10+
var seqLevelIdx = 0
11+
var seqTier = false
12+
var highBitDepth = false
13+
var twelveBit = false
14+
var monochrome = false
15+
var subsamplingX = false
16+
var subsamplingY = false
17+
var samplePosition = 0
18+
var initialDisplayDelayPresentFlag = false
19+
var initialPresentationDelay = 0
20+
21+
22+
fun parse(sequenceObu: ByteBuffer) {
23+
parse(sequenceObu.toByteArray())
24+
}
25+
26+
fun parse(sequenceObu: ByteArray) {
27+
val av1Parser = Av1Parser()
28+
val obu = av1Parser.getObus(sequenceObu).firstOrNull {
29+
av1Parser.getObuType(it.header[0]) == ObuType.SEQUENCE_HEADER
30+
} ?: throw IllegalArgumentException("sequence header obu not found")
31+
val bitBuffer = BitBuffer(ByteBuffer.wrap(obu.data))
32+
33+
seqProfile = bitBuffer.getInt(3)
34+
bitBuffer.skipBool()
35+
val reducedStillPictureHeader = bitBuffer.getBool()
36+
initialDisplayDelayPresentFlag = false
37+
if (reducedStillPictureHeader) {
38+
seqLevelIdx = bitBuffer.getInt(5)
39+
} else {
40+
val timingInfoPresentFlag = bitBuffer.getBool()
41+
var decoderModelInfoPresentFlag = false
42+
var bufferDelayLengthMinus1 = 0
43+
if (timingInfoPresentFlag) {
44+
bitBuffer.skip(64)
45+
val equalPictureInterval = bitBuffer.getBool()
46+
if (equalPictureInterval) {
47+
bitBuffer.readUVLC()
48+
}
49+
decoderModelInfoPresentFlag = bitBuffer.getBool()
50+
if (decoderModelInfoPresentFlag) {
51+
bufferDelayLengthMinus1 = bitBuffer.getInt(5)
52+
bitBuffer.skip(42) //skip this
53+
}
54+
}
55+
initialDisplayDelayPresentFlag = bitBuffer.getBool()
56+
val operatingPointsCntMinus1 = bitBuffer.getInt(5)
57+
for (i in 0..operatingPointsCntMinus1) {
58+
bitBuffer.skip(12) //skip
59+
val levelIdx = bitBuffer.getInt(5)
60+
if (i == 0) seqLevelIdx = levelIdx
61+
if (levelIdx > 7) {
62+
val sTier = bitBuffer.getBool()
63+
if (i == 0) seqTier = sTier
64+
}
65+
if (decoderModelInfoPresentFlag) {
66+
val decoderModelPresentForThisOp = bitBuffer.getBool()
67+
if (decoderModelPresentForThisOp) {
68+
val n = bufferDelayLengthMinus1 + 1
69+
bitBuffer.skip(n * 2 + 1) //skip this
70+
}
71+
}
72+
if (initialDisplayDelayPresentFlag) {
73+
val initialDisplayDelayPresentForThisOp = bitBuffer.getBool()
74+
if (initialDisplayDelayPresentForThisOp) {
75+
val initialDisplayDelayMinus1 = bitBuffer.getInt(4)
76+
if (i == 0) initialPresentationDelay = initialDisplayDelayMinus1
77+
}
78+
}
79+
}
80+
}
81+
82+
val frameWidthBitsMinus1 = bitBuffer.getInt(4)
83+
val frameHeightBitsMinus1 = bitBuffer.getInt(4)
84+
bitBuffer.skip(frameWidthBitsMinus1 + 1 + frameHeightBitsMinus1 + 1)
85+
var frameIdNumbersPresentFlag = false
86+
if (!reducedStillPictureHeader) {
87+
frameIdNumbersPresentFlag = bitBuffer.getBool()
88+
}
89+
if (frameIdNumbersPresentFlag) bitBuffer.skip(7)
90+
bitBuffer.skip(3)
91+
if (!reducedStillPictureHeader) {
92+
bitBuffer.skip(4)
93+
val enableOrderHint = bitBuffer.getBool()
94+
if (enableOrderHint) bitBuffer.skip(2)
95+
val seqChooseScreenContentTools = bitBuffer.getBool()
96+
val seqForceScreenContentTools = seqChooseScreenContentTools || bitBuffer.getBool()
97+
if (seqForceScreenContentTools) {
98+
val seqChooseIntegerMv = bitBuffer.getBool()
99+
if (!seqChooseIntegerMv) bitBuffer.skipBool()
100+
}
101+
if (enableOrderHint) bitBuffer.skip(3)
102+
}
103+
bitBuffer.skip(3)
104+
//config color
105+
highBitDepth = bitBuffer.getBool()
106+
twelveBit = false
107+
var bitDepth = 0
108+
if (seqProfile == 2 && highBitDepth) {
109+
twelveBit = bitBuffer.getBool()
110+
bitDepth = if (twelveBit) 12 else 10
111+
} else if (seqProfile <= 2) {
112+
bitDepth = if (highBitDepth) 10 else 8
113+
}
114+
monochrome = if (seqProfile == 1) {
115+
false
116+
} else {
117+
val chrome = bitBuffer.getBool()
118+
chrome
119+
}
120+
val colorDescriptionPresentFlag = bitBuffer.getBool()
121+
var colorPrimaries = 0
122+
var transferCharacteristics = 0
123+
var matrixCoefficients = 0
124+
if (colorDescriptionPresentFlag) {
125+
colorPrimaries = bitBuffer.getInt(8)
126+
transferCharacteristics = bitBuffer.getInt(8)
127+
matrixCoefficients = bitBuffer.getInt(8)
128+
}
129+
samplePosition = 0
130+
if (monochrome) {
131+
bitBuffer.getBool()
132+
subsamplingX = true
133+
subsamplingY = true
134+
} else if (colorPrimaries == 1 && transferCharacteristics == 13 && matrixCoefficients == 0) {
135+
subsamplingX = false
136+
subsamplingY = false
137+
} else {
138+
bitBuffer.skipBool()
139+
if (seqProfile == 0) {
140+
subsamplingX = true
141+
subsamplingY = true
142+
} else if (seqProfile == 1) {
143+
subsamplingX = false
144+
subsamplingY = false
145+
} else {
146+
if (bitDepth == 12) {
147+
subsamplingX = bitBuffer.getBool()
148+
subsamplingY = if (subsamplingX) {
149+
bitBuffer.getBool()
150+
} else {
151+
false
152+
}
153+
} else {
154+
subsamplingX = true
155+
subsamplingY = false
156+
}
157+
}
158+
if (subsamplingX && subsamplingY) {
159+
samplePosition = bitBuffer.getInt(2)
160+
}
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)