Validate length-prefixed NAL headers consistently with Annex B input

This commit is contained in:
evgeny committed 2026-10-29 13:34:24 +01:00
1 parent 92ce2d84e9
commit cc7648bb33
2 files changed
+11 -1

No files matched your search

@@ -3,7 +3,10 @@ package com.utun.chat.data
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
/** Единый wire-формат AVC: Annex B. Никакого поиска кадра по произвольным байтам после потери блока. */
/** AVC NAL framing: Annex B со start code 3/4 байта либо big-endian length[4] перед каждым NAL.
* normalize копирует/конвертирует целый access unit <=2 MiB и проверяет границы/forbidden bit;
* это не SPS/декодерная проверка bitstream. Ошибки аргументов бросаются владельцу для диагностики.
* keyFrame возвращает обычный Annex B с SPS/PPS/IDR, без uAV2 config length из StreamVideoCodec. */
object H264AnnexB {
const val MAX_FRAME = 2 * 1024 * 1024
private val start = byteArrayOf(0, 0, 0, 1)
@@ -20,6 +23,7 @@ object H264AnnexB {
require(b.remaining() >= 4)
val length = b.int
require(length > 0 && length <= b.remaining())
require((bytes[b.position()].toInt() and 0x80) == 0)
out.write(start); out.write(bytes, b.position(), length); b.position(b.position() + length)
}
require(out.size() <= MAX_FRAME)
@@ -30,4 +30,10 @@ class H264AnnexBTest {
assertThrows(IllegalArgumentException::class.java) { H264AnnexB.keyFrame(byteArrayOf(0, 0, 1, 0x61, 0x11), sps + pps) }
assertThrows(IllegalArgumentException::class.java) { H264AnnexB.normalize(ByteArray(H264AnnexB.MAX_FRAME + 1)) }
}
@Test fun lengthPrefixedNalUsesTheSameForbiddenBitValidationAsAnnexB() {
val invalid = byteArrayOf(0, 0, 0, 2, -1, 1)
assertThrows(IllegalArgumentException::class.java) { H264AnnexB.normalize(invalid) }
assertThrows(IllegalArgumentException::class.java) { H264AnnexB.normalize(byteArrayOf(0, 0, 0, 1, -1, 1)) }
}
}