Support variable-length OAMD element payloads
Native builds / linux-x64 (push) Failing after 11s
Native builds / macos-arm64 (push) Has been cancelled
Native builds / macos-x64 (push) Has been cancelled
Native builds / windows-x64 (push) Has been cancelled
Native builds / Publish GitHub Release (push) Has been cancelled
Native builds / linux-x64 (push) Failing after 11s
Native builds / macos-arm64 (push) Has been cancelled
Native builds / macos-x64 (push) Has been cancelled
Native builds / windows-x64 (push) Has been cancelled
Native builds / Publish GitHub Release (push) Has been cancelled
This commit is contained in:
@@ -6,6 +6,8 @@ venv/
|
|||||||
|
|
||||||
build/
|
build/
|
||||||
output/
|
output/
|
||||||
|
tests/
|
||||||
|
lib/
|
||||||
metadata_cache/
|
metadata_cache/
|
||||||
|
|
||||||
*.metadata.json
|
*.metadata.json
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ See the [mathematical notes](docs/math.en.md) for the equations used by the deco
|
|||||||
- Only the common contiguous EMDF transport is covered. Fragmented transport across multiple audio-block skip fields is not covered.
|
- Only the common contiguous EMDF transport is covered. Fragmented transport across multiple audio-block skip fields is not covered.
|
||||||
- Dense JOC is the main path. The Sparse JOC branch should not be treated as supported.
|
- Dense JOC is the main path. The Sparse JOC branch should not be treated as supported.
|
||||||
- The speaker path currently covers ordinary point objects; extent, spread, divergence, and similar modes are outside the supported scope.
|
- The speaker path currently covers ordinary point objects; extent, spread, divergence, and similar modes are outside the supported scope.
|
||||||
|
- OAMD trim elements are boundary-checked and skipped; warp, balance, and trim parameters are not applied to raw object trajectories or speaker rendering.
|
||||||
- Multi-data-point streams, uncommon band configurations, and unusual OAMD scheduling have less coverage than common 12-band, single-data-point material.
|
- Multi-data-point streams, uncommon band configurations, and unusual OAMD scheduling have less coverage than common 12-band, single-data-point material.
|
||||||
- A speaker limiter is outside the current primary formula.
|
- A speaker limiter is outside the current primary formula.
|
||||||
- ADM output, native binaries, and speaker layouts still need broader interoperability checks across platforms, players, and real material.
|
- ADM output, native binaries, and speaker layouts still need broader interoperability checks across platforms, players, and real material.
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ JustOneCacophony/
|
|||||||
- 当前只覆盖常见 continuous EMDF transport;跨多个 audio-block skip field 的碎片化 transport 尚未覆盖。
|
- 当前只覆盖常见 continuous EMDF transport;跨多个 audio-block skip field 的碎片化 transport 尚未覆盖。
|
||||||
- Dense JOC 是当前主要路径;Sparse JOC 分支不应视为受支持能力。
|
- Dense JOC 是当前主要路径;Sparse JOC 分支不应视为受支持能力。
|
||||||
- 扬声器路径当前只覆盖普通点对象;extent、spread、divergence 等对象模式不在支持范围内。
|
- 扬声器路径当前只覆盖普通点对象;extent、spread、divergence 等对象模式不在支持范围内。
|
||||||
|
- OAMD trim element 会按声明边界校验并跳过;warp、balance 和 trim 参数不应用于当前原始对象轨迹或扬声器渲染。
|
||||||
- 多数据点、少见参数带配置和特殊 OAMD 调度的覆盖度低于常见 12-band、单数据点素材。
|
- 多数据点、少见参数带配置和特殊 OAMD 调度的覆盖度低于常见 12-band、单数据点素材。
|
||||||
- 扬声器 limiter 不属于当前实现的主公式。
|
- 扬声器 limiter 不属于当前实现的主公式。
|
||||||
- ADM 输出、原生库和扬声器布局仍需在更多平台、播放器与真实素材上确认互操作性。
|
- ADM 输出、原生库和扬声器布局仍需在更多平台、播放器与真实素材上确认互操作性。
|
||||||
|
|||||||
+265
-76
@@ -15,6 +15,10 @@ RAMP_DURATION_INDEX = (
|
|||||||
32, 64, 128, 256, 320, 480, 1000, 1001,
|
32, 64, 128, 256, 320, 480, 1000, 1001,
|
||||||
1024, 1600, 1601, 1602, 1920, 2000, 2002, 2048,
|
1024, 1600, 1601, 1602, 1920, 2000, 2002, 2048,
|
||||||
)
|
)
|
||||||
|
OBJECT_ELEMENT_ID = 1
|
||||||
|
TRIM_ELEMENT_ID = 2
|
||||||
|
EXTENDED_OBJECT_ELEMENT_ID = 5
|
||||||
|
POSITION_WINDOW_END_BIT = 112 + 31 * (15 - 3) + 24
|
||||||
|
|
||||||
|
|
||||||
def q_of(k, n):
|
def q_of(k, n):
|
||||||
@@ -24,36 +28,58 @@ def q_of(k, n):
|
|||||||
|
|
||||||
def _payload_bits(bits_one):
|
def _payload_bits(bits_one):
|
||||||
if isinstance(bits_one, (bytes, bytearray, memoryview)):
|
if isinstance(bits_one, (bytes, bytearray, memoryview)):
|
||||||
src = np.frombuffer(bits_one, dtype=np.uint8)
|
raw_payload = bytes(bits_one)
|
||||||
|
bits = np.unpackbits(
|
||||||
|
np.frombuffer(raw_payload, dtype=np.uint8), bitorder="big")
|
||||||
else:
|
else:
|
||||||
src = np.asarray(bits_one, dtype=np.uint8)
|
src = np.asarray(bits_one)
|
||||||
if src.ndim == 1 and src.shape[0] in (536, 552):
|
if src.ndim != 1:
|
||||||
bits = src
|
raw = np.asarray(bits_one, dtype=np.uint8).tobytes()
|
||||||
elif src.size in (67, 69):
|
raise UnsupportedVariantError(
|
||||||
bits = np.unpackbits(src.reshape(-1), bitorder="big")
|
"oamd", "payload_shape",
|
||||||
else:
|
"OAMD 载荷必须是一维 byte 或 bit 序列",
|
||||||
raw = (bytes(bits_one) if isinstance(bits_one, (bytes, bytearray, memoryview))
|
details={"shape": list(src.shape), "payload": bytes_descriptor(raw)})
|
||||||
else np.asarray(bits_one, dtype=np.uint8).tobytes())
|
is_bit_vector = bool(src.size) and bool(np.all((src == 0) | (src == 1)))
|
||||||
raise UnsupportedVariantError(
|
if is_bit_vector:
|
||||||
"oamd", f"payload_length_{len(raw)}B",
|
if src.size % 8:
|
||||||
f"发现未覆盖的 OAMD 载荷长度 {len(raw)}B",
|
raw = np.packbits(src.astype(np.uint8), bitorder="big").tobytes()
|
||||||
details={
|
raise UnsupportedVariantError(
|
||||||
"supported_payload_bytes": [67, 69],
|
"oamd", "payload_bit_alignment",
|
||||||
"payload": bytes_descriptor(raw),
|
"OAMD bit 载荷没有按整字节对齐",
|
||||||
"repair_hint": "检查 OAMD header、element 数量及可选字段造成的位偏移变化",
|
details={
|
||||||
})
|
"payload_bits": int(src.size),
|
||||||
raw_payload = np.packbits(bits, bitorder="big").tobytes()
|
"payload": bytes_descriptor(raw),
|
||||||
|
})
|
||||||
|
bits = src.astype(np.uint8, copy=False)
|
||||||
|
raw_payload = np.packbits(bits, bitorder="big").tobytes()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
values = src.astype(np.int64, copy=False)
|
||||||
|
except (TypeError, ValueError, OverflowError) as exc:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "payload_type",
|
||||||
|
"OAMD 载荷不能转换为 byte 序列",
|
||||||
|
details={"dtype": str(src.dtype), "parser_error": str(exc)}) from exc
|
||||||
|
if np.any(values < 0) or np.any(values > 255):
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "payload_byte_range",
|
||||||
|
"OAMD byte 载荷包含 0..255 之外的值",
|
||||||
|
details={"dtype": str(src.dtype), "payload_values": int(src.size)})
|
||||||
|
raw_payload = values.astype(np.uint8).tobytes()
|
||||||
|
bits = np.unpackbits(
|
||||||
|
np.frombuffer(raw_payload, dtype=np.uint8), bitorder="big")
|
||||||
return bits, raw_payload
|
return bits, raw_payload
|
||||||
|
|
||||||
|
|
||||||
class _BitReader:
|
class _BitReader:
|
||||||
def __init__(self, bits):
|
def __init__(self, bits, position=0, limit=None):
|
||||||
self.bits = bits
|
self.bits = bits
|
||||||
self.position = 0
|
self.position = int(position)
|
||||||
|
self.limit = len(bits) if limit is None else int(limit)
|
||||||
|
|
||||||
def read(self, count):
|
def read(self, count):
|
||||||
end = self.position + count
|
end = self.position + count
|
||||||
if end > len(self.bits):
|
if end > self.limit:
|
||||||
raise ValueError(f"OAMD 位流越界: bit={self.position}, need={count}")
|
raise ValueError(f"OAMD 位流越界: bit={self.position}, need={count}")
|
||||||
value = 0
|
value = 0
|
||||||
for bit in self.bits[self.position:end]:
|
for bit in self.bits[self.position:end]:
|
||||||
@@ -75,64 +101,216 @@ def _variable_bits(reader, width, max_groups=5):
|
|||||||
raise ValueError(f"OAMD variable_bits({width}) 延伸组过多")
|
raise ValueError(f"OAMD variable_bits({width}) 延伸组过多")
|
||||||
|
|
||||||
|
|
||||||
def _update_timing(bits, alternate_object_present, element_count, raw_payload):
|
def _element_details(elements):
|
||||||
"""按 OAMD element/MDUpdateInfo 读取位置块的开始偏移和 ramp 时长。"""
|
return [{
|
||||||
reader = _BitReader(bits)
|
"ordinal": element["ordinal"],
|
||||||
reader.position = 14
|
"element_id": element["element_id"],
|
||||||
for _ in range(element_count):
|
"size_bytes": element["size_bytes"],
|
||||||
element_index = reader.read(4)
|
"header_start_bit": element["header_start_bit"],
|
||||||
element_length = _variable_bits(reader, 4)
|
"body_start_bit": element["body_start_bit"],
|
||||||
element_end = reader.position + element_length + 1
|
"body_end_bit": element["body_end_bit"],
|
||||||
reader.skip(5 if alternate_object_present else 1)
|
"discard_unknown": element["discard_unknown"],
|
||||||
if element_index == 1:
|
} for element in elements]
|
||||||
offset_code = reader.read(2)
|
|
||||||
if offset_code == 0:
|
|
||||||
sample_offset = 0
|
|
||||||
elif offset_code == 1:
|
|
||||||
sample_offset = SAMPLE_OFFSET_INDEX[reader.read(2)]
|
|
||||||
elif offset_code == 2:
|
|
||||||
sample_offset = reader.read(5)
|
|
||||||
else:
|
|
||||||
raise UnsupportedVariantError(
|
|
||||||
"oamd", "md_sample_offset_mode",
|
|
||||||
"OAMD 使用了当前未覆盖的 MD sample-offset 模式",
|
|
||||||
details={"payload": bytes_descriptor(raw_payload)})
|
|
||||||
|
|
||||||
block_count = reader.read(3) + 1
|
|
||||||
blocks = []
|
def _parse_elements(bits, header, header_end, raw_payload):
|
||||||
for _block in range(block_count):
|
"""建立 OAMD element 目录;element size 表示其后 body 的字节数。"""
|
||||||
block_offset_factor = reader.read(6)
|
reader = _BitReader(bits, header_end)
|
||||||
block_offset = sample_offset + block_offset_factor * 32
|
elements = []
|
||||||
ramp_code = reader.read(2)
|
for ordinal in range(header["element_count"]):
|
||||||
if ramp_code == 3:
|
header_start = reader.position
|
||||||
if reader.read(1):
|
try:
|
||||||
ramp_duration = RAMP_DURATION_INDEX[reader.read(4)]
|
element_id = reader.read(4)
|
||||||
else:
|
size_bytes = _variable_bits(reader, 4) + 1
|
||||||
ramp_duration = reader.read(11)
|
except ValueError as exc:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "element_header_truncated",
|
||||||
|
"OAMD element header 不完整",
|
||||||
|
details={
|
||||||
|
"element_ordinal": ordinal,
|
||||||
|
"header_start_bit": header_start,
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
"parser_error": str(exc),
|
||||||
|
}) from exc
|
||||||
|
|
||||||
|
body_start = reader.position
|
||||||
|
body_end = body_start + size_bytes * 8
|
||||||
|
if body_end > len(bits):
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "element_bounds",
|
||||||
|
"OAMD element 声明长度超过 payload 边界",
|
||||||
|
details={
|
||||||
|
"element_ordinal": ordinal,
|
||||||
|
"element_id": element_id,
|
||||||
|
"size_bytes": size_bytes,
|
||||||
|
"body_start_bit": body_start,
|
||||||
|
"body_end_bit": body_end,
|
||||||
|
"payload_bits": len(bits),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
|
||||||
|
control_bits = 5 if header["alternate_object_present"] else 1
|
||||||
|
if body_start + control_bits > body_end:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "element_control_bounds",
|
||||||
|
"OAMD element 太短,无法容纳控制字段",
|
||||||
|
details={
|
||||||
|
"element_ordinal": ordinal,
|
||||||
|
"element_id": element_id,
|
||||||
|
"size_bytes": size_bytes,
|
||||||
|
"control_bits": control_bits,
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
control = _BitReader(bits, body_start, body_end)
|
||||||
|
alternate_data_id = (control.read(4)
|
||||||
|
if header["alternate_object_present"] else None)
|
||||||
|
discard_unknown = bool(control.read(1))
|
||||||
|
elements.append({
|
||||||
|
"ordinal": ordinal,
|
||||||
|
"element_id": element_id,
|
||||||
|
"size_bytes": size_bytes,
|
||||||
|
"header_start_bit": header_start,
|
||||||
|
"body_start_bit": body_start,
|
||||||
|
"data_start_bit": control.position,
|
||||||
|
"body_end_bit": body_end,
|
||||||
|
"alternate_data_id": alternate_data_id,
|
||||||
|
"discard_unknown": discard_unknown,
|
||||||
|
})
|
||||||
|
reader.position = body_end
|
||||||
|
|
||||||
|
padding = bits[reader.position:]
|
||||||
|
if len(padding) > 7:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "trailing_payload_data",
|
||||||
|
"OAMD element 结束后仍有超过一个字节的未声明数据",
|
||||||
|
details={
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"trailing_bits": len(padding),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
if np.any(padding):
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "nonzero_padding",
|
||||||
|
"OAMD payload 尾部 padding 含非零位",
|
||||||
|
details={
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"padding_start_bit": reader.position,
|
||||||
|
"padding_bits": "".join(str(int(bit)) for bit in padding),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
|
||||||
|
object_elements = [element for element in elements
|
||||||
|
if element["element_id"] == OBJECT_ELEMENT_ID]
|
||||||
|
if len(object_elements) != 1:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "object_element_count",
|
||||||
|
"OAMD 必须包含且只能包含一个 object element",
|
||||||
|
details={
|
||||||
|
"object_element_count": len(object_elements),
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
object_element = object_elements[0]
|
||||||
|
if object_element["ordinal"] != 0 or object_element["header_start_bit"] != header_end:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "object_element_order",
|
||||||
|
"object element 不在当前固定位置窗口支持的首个 element 位置",
|
||||||
|
details={
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
"repair_hint": "以 object element 的实际位置为基准重新定位对象窗口",
|
||||||
|
})
|
||||||
|
|
||||||
|
for element in elements:
|
||||||
|
element_id = element["element_id"]
|
||||||
|
if element_id in (OBJECT_ELEMENT_ID, TRIM_ELEMENT_ID):
|
||||||
|
continue
|
||||||
|
if element_id == EXTENDED_OBJECT_ELEMENT_ID:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "extended_object_element",
|
||||||
|
"OAMD 含可能改变坐标语义的 extended object element",
|
||||||
|
details={
|
||||||
|
"element": _element_details([element])[0],
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
"repair_hint": "解析 divergence/extended-precision position 后再应用轨迹",
|
||||||
|
})
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", f"unsupported_element_{element_id}",
|
||||||
|
f"OAMD 含当前未覆盖的 element id {element_id}",
|
||||||
|
details={
|
||||||
|
"element": _element_details([element])[0],
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
return object_element, elements
|
||||||
|
|
||||||
|
|
||||||
|
def _update_timing(bits, object_element, raw_payload):
|
||||||
|
"""从已定位的 object element 读取位置块开始偏移和 ramp 时长。"""
|
||||||
|
reader = _BitReader(
|
||||||
|
bits, object_element["data_start_bit"], object_element["body_end_bit"])
|
||||||
|
try:
|
||||||
|
offset_code = reader.read(2)
|
||||||
|
if offset_code == 0:
|
||||||
|
sample_offset = 0
|
||||||
|
elif offset_code == 1:
|
||||||
|
sample_offset = SAMPLE_OFFSET_INDEX[reader.read(2)]
|
||||||
|
elif offset_code == 2:
|
||||||
|
sample_offset = reader.read(5)
|
||||||
|
else:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "md_sample_offset_mode",
|
||||||
|
"OAMD 使用了当前未覆盖的 MD sample-offset 模式",
|
||||||
|
details={"payload": bytes_descriptor(raw_payload)})
|
||||||
|
|
||||||
|
block_count = reader.read(3) + 1
|
||||||
|
blocks = []
|
||||||
|
for _block in range(block_count):
|
||||||
|
block_offset_factor = reader.read(6)
|
||||||
|
block_offset = sample_offset + block_offset_factor * 32
|
||||||
|
ramp_code = reader.read(2)
|
||||||
|
if ramp_code == 3:
|
||||||
|
if reader.read(1):
|
||||||
|
ramp_duration = RAMP_DURATION_INDEX[reader.read(4)]
|
||||||
else:
|
else:
|
||||||
ramp_duration = RAMP_DURATIONS[ramp_code]
|
ramp_duration = reader.read(11)
|
||||||
blocks.append((block_offset, ramp_duration))
|
else:
|
||||||
if len(blocks) != 1:
|
ramp_duration = RAMP_DURATIONS[ramp_code]
|
||||||
raise UnsupportedVariantError(
|
blocks.append((block_offset, ramp_duration))
|
||||||
"oamd", "multiple_position_blocks",
|
except UnsupportedVariantError:
|
||||||
"OAMD 一帧含多个对象位置更新块,固定位置窗口不能安全套用",
|
raise
|
||||||
details={
|
except ValueError as exc:
|
||||||
"block_count": len(blocks),
|
raise UnsupportedVariantError(
|
||||||
"blocks": blocks,
|
"oamd", "object_element_syntax",
|
||||||
"payload": bytes_descriptor(raw_payload),
|
"OAMD object element 的 timing 字段越界或不完整",
|
||||||
"repair_hint": "按 ObjectInfoBlock 顺序逐块解析坐标,再生成分段 ADM ramp",
|
details={
|
||||||
})
|
"element": _element_details([object_element])[0],
|
||||||
return blocks[0]
|
"payload": bytes_descriptor(raw_payload),
|
||||||
reader.position = element_end
|
"parser_error": str(exc),
|
||||||
raise UnsupportedVariantError(
|
}) from exc
|
||||||
"oamd", "missing_object_element",
|
|
||||||
"OAMD 中没有 object element (element_index=1)",
|
if len(blocks) != 1:
|
||||||
details={"payload": bytes_descriptor(raw_payload)})
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "multiple_position_blocks",
|
||||||
|
"OAMD 一帧含多个对象位置更新块,固定位置窗口不能安全套用",
|
||||||
|
details={
|
||||||
|
"block_count": len(blocks),
|
||||||
|
"blocks": blocks,
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
"repair_hint": "按 ObjectInfoBlock 顺序逐块解析坐标,再生成分段 ADM ramp",
|
||||||
|
})
|
||||||
|
return blocks[0]
|
||||||
|
|
||||||
|
|
||||||
def frame_update(bits_one):
|
def frame_update(bits_one):
|
||||||
"""单帧 OAMD → 位置字段增量及其 sample offset/ramp duration。"""
|
"""单帧 OAMD → 位置字段增量及其 sample offset/ramp duration。"""
|
||||||
bits, raw_payload = _payload_bits(bits_one)
|
bits, raw_payload = _payload_bits(bits_one)
|
||||||
|
if len(bits) < 14:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "header_truncated",
|
||||||
|
"OAMD payload 不足以容纳受支持的 header",
|
||||||
|
details={"payload_bits": len(bits), "payload": bytes_descriptor(raw_payload)})
|
||||||
header = {
|
header = {
|
||||||
"version": int((bits[0] << 1) | bits[1]),
|
"version": int((bits[0] << 1) | bits[1]),
|
||||||
"objects_minus_one": int(sum(int(bits[2 + i]) << (4 - i) for i in range(5))),
|
"objects_minus_one": int(sum(int(bits[2 + i]) << (4 - i) for i in range(5))),
|
||||||
@@ -144,15 +322,25 @@ def frame_update(bits_one):
|
|||||||
if raw_payload[:2] != b"\x1f\x88":
|
if raw_payload[:2] != b"\x1f\x88":
|
||||||
raise UnsupportedVariantError(
|
raise UnsupportedVariantError(
|
||||||
"oamd", f"header_signature_{raw_payload[:2].hex()}",
|
"oamd", f"header_signature_{raw_payload[:2].hex()}",
|
||||||
"OAMD 长度已知,但 header 与当前位置字段布局不一致",
|
"OAMD header 与当前位置字段布局不一致",
|
||||||
details={
|
details={
|
||||||
"supported_header_prefix_hex": "1f88",
|
"supported_header_prefix_hex": "1f88",
|
||||||
"header_probe": header,
|
"header_probe": header,
|
||||||
"payload": bytes_descriptor(raw_payload),
|
"payload": bytes_descriptor(raw_payload),
|
||||||
"repair_hint": "按新 header 的 program assignment 和 element 布局重新定位对象位置字段",
|
"repair_hint": "按新 header 的 program assignment 和 element 布局重新定位对象位置字段",
|
||||||
})
|
})
|
||||||
block_offset, ramp_duration = _update_timing(
|
object_element, elements = _parse_elements(bits, header, 14, raw_payload)
|
||||||
bits, bool(header["alternate_object_present"]), header["element_count"], raw_payload)
|
if object_element["body_end_bit"] < POSITION_WINDOW_END_BIT:
|
||||||
|
raise UnsupportedVariantError(
|
||||||
|
"oamd", "object_element_too_short",
|
||||||
|
"OAMD object element 无法容纳当前固定位置窗口",
|
||||||
|
details={
|
||||||
|
"element": _element_details([object_element])[0],
|
||||||
|
"required_position_end_bit": POSITION_WINDOW_END_BIT,
|
||||||
|
"elements": _element_details(elements),
|
||||||
|
"payload": bytes_descriptor(raw_payload),
|
||||||
|
})
|
||||||
|
block_offset, ramp_duration = _update_timing(bits, object_element, raw_payload)
|
||||||
weights = 1 << np.arange(7, -1, -1)
|
weights = 1 << np.arange(7, -1, -1)
|
||||||
out = {}
|
out = {}
|
||||||
for obj in range(16):
|
for obj in range(16):
|
||||||
@@ -163,7 +351,7 @@ def frame_update(bits_one):
|
|||||||
if obj and (wq1 >> 6 != 3 or wq2 & 2 != 2 or wq3 & 0x1F != 1):
|
if obj and (wq1 >> 6 != 3 or wq2 & 2 != 2 or wq3 & 0x1F != 1):
|
||||||
raise UnsupportedVariantError(
|
raise UnsupportedVariantError(
|
||||||
"oamd", "position_layout_signature",
|
"oamd", "position_layout_signature",
|
||||||
"OAMD 长度和 header 已知,但对象位置字段标记或位偏移发生变化",
|
"OAMD 对象位置字段标记或位偏移发生变化",
|
||||||
details={
|
details={
|
||||||
"object_slot": obj,
|
"object_slot": obj,
|
||||||
"position_start_bit": start,
|
"position_start_bit": start,
|
||||||
@@ -171,6 +359,7 @@ def frame_update(bits_one):
|
|||||||
"q2_window_hex": f"{wq2:02x}",
|
"q2_window_hex": f"{wq2:02x}",
|
||||||
"q3_window_hex": f"{wq3:02x}",
|
"q3_window_hex": f"{wq3:02x}",
|
||||||
"header_probe": header,
|
"header_probe": header,
|
||||||
|
"elements": _element_details(elements),
|
||||||
"payload": bytes_descriptor(raw_payload),
|
"payload": bytes_descriptor(raw_payload),
|
||||||
"repair_hint": "解析 OAMD element 可选字段并更新每个对象的位置窗口偏移",
|
"repair_hint": "解析 OAMD element 可选字段并更新每个对象的位置窗口偏移",
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user