///|
priv struct Av1ObuSpan {
offset : Int
payload_offset : Int
end : Int
kind : Int
extension : Bool
temporal_id : Int
spatial_id : Int
}
///|
/// None means incomplete input only when eof=false. Unsized terminal OBUs need
/// EOF. The byte-stream adapter and complete-buffer diagnostic API share this.
fn av1_read_obu_span(
data : Array[Byte],
start : Int,
eof : Bool,
limit : Int,
) -> Result[Av1ObuSpan?, DecodeError] {
let short = fn(at : Int) -> Result[Av1ObuSpan?, DecodeError] {
if eof {
Err(
decode_error(
TruncatedInput,
"obu",
"Incomplete OBU header or payload",
offset=Some(at),
),
)
} else {
Ok(None)
}
}
if start < 0 || start >= data.length() {
return short(start)
}
let header = data[start].to_int()
if (header & 0x81) != 0 {
return Err(
decode_error(
InvalidData,
"obu",
"Forbidden or reserved OBU header bit",
offset=Some(start),
),
)
}
let mut pos = start + 1
let extension = (header & 4) != 0
let mut temporal_id = 0
let mut spatial_id = 0
if extension {
if pos == data.length() {
return short(pos)
}
let ext = data[pos].to_int()
if (ext & 7) != 0 {
return Err(
decode_error(
InvalidData,
"obu",
"Reserved OBU extension bits",
offset=Some(pos),
),
)
}
temporal_id = ext >> 5
spatial_id = (ext >> 3) & 3
pos += 1
}
let mut length = 0
if (header & 2) == 0 {
if !eof {
return Ok(None)
}
length = data.length() - pos
} else {
let length_offset = pos
let mut terminated = false
for i in 0..<8 {
if pos == data.length() {
return short(pos)
}
let byte = data[pos].to_int()
pos += 1
let part = byte & 127
if (i == 4 && part > 7) || (i > 4 && part != 0) {
return Err(
decode_error(
LimitExceeded,
"obu",
"OBU length exceeds the supported input size",
offset=Some(length_offset),
),
)
}
if i <= 4 {
length = length | (part << (i * 7))
}
if (byte & 128) == 0 {
terminated = true
break
}
}
if !terminated {
return Err(
decode_error(
InvalidData,
"obu",
"OBU length exceeds eight leb128 bytes",
offset=Some(length_offset),
),
)
}
}
if length > limit || pos - start > limit - length {
return Err(
decode_error(
LimitExceeded,
"obu",
"Declared OBU exceeds the input budget",
offset=Some(start),
),
)
}
if length > data.length() - pos {
return short(data.length())
}
Ok(
Some({
offset: start,
payload_offset: pos,
end: pos + length,
kind: (header >> 3) & 15,
extension,
temporal_id,
spatial_id,
}),
)
}
///|
fn av1_obu_packets_checked(
data : Array[Byte],
limit? : Int = 0x7FFFFFFF,
) -> Result[Array[Av1ObuPacket], DecodeError] {
let packets : Array[Av1ObuPacket] = []
let mut pos = 0
while pos < data.length() {
let span = match av1_read_obu_span(data, pos, true, limit) {
Ok(Some(value)) => value
Err(error) => return Err(error)
Ok(None) =>
return Err(
decode_error(
TruncatedInput,
"obu",
"Incomplete OBU",
offset=Some(pos),
),
)
}
packets.push({
kind: span.kind,
extension: span.extension,
temporal_id: span.temporal_id,
spatial_id: span.spatial_id,
offset: span.offset,
payload_offset: span.payload_offset,
payload: Array::makei(span.end - span.payload_offset, i => {
data[span.payload_offset + i]
}),
})
pos = span.end
}
Ok(packets)
}