fix: 修复时间戳显示
This commit is contained in:
@@ -22,6 +22,10 @@ OMNISOCKET_CONFIG_PATH = PROJECT_ROOT / "config" / "omnisocket_demo.yaml"
|
||||
VIDEO_SOURCE_MODE = os.getenv("VIDEO_SOURCE_MODE", "auto").strip().lower()
|
||||
OMNISOCKET_FRAME_FRESH_SECONDS = 2.0
|
||||
VIDEO_TIMESTAMP_SAMPLE_SIZE = 10
|
||||
VIDEO_TIMESTAMP_TRAILER_BYTES = 8
|
||||
VIDEO_TIMESTAMP_ENDIANNESS = "little"
|
||||
VIDEO_TIMESTAMP_UNIT = "ms"
|
||||
VIDEO_TIMESTAMP_MULTIPLIER_NS = 1_000_000
|
||||
VIDEO_TIMESTAMP_MAX_SKEW_NS = 7 * 24 * 60 * 60 * 1_000_000_000
|
||||
|
||||
|
||||
@@ -211,16 +215,35 @@ class OmniSocketVideoReceiver:
|
||||
return frame[8:]
|
||||
return None
|
||||
|
||||
def _extract_jpeg_frame(self, frame: bytes) -> bytes | None:
|
||||
def _split_jpeg_frame_and_trailer(self, frame: bytes) -> tuple[bytes, bytes] | None:
|
||||
jpeg_payload = self._extract_jpeg_payload(frame)
|
||||
if jpeg_payload is None:
|
||||
return None
|
||||
|
||||
# 当前发送端协议是 JPEG 末尾固定追加 8 字节 little-endian 毫秒时间戳。
|
||||
if jpeg_payload.endswith(b"\xff\xd9"):
|
||||
return jpeg_payload, b""
|
||||
|
||||
if (
|
||||
len(jpeg_payload) >= VIDEO_TIMESTAMP_TRAILER_BYTES + 2
|
||||
and jpeg_payload[-(VIDEO_TIMESTAMP_TRAILER_BYTES + 2) : -VIDEO_TIMESTAMP_TRAILER_BYTES] == b"\xff\xd9"
|
||||
):
|
||||
return jpeg_payload[:-VIDEO_TIMESTAMP_TRAILER_BYTES], jpeg_payload[-VIDEO_TIMESTAMP_TRAILER_BYTES:]
|
||||
|
||||
# 兜底兼容旧格式,避免直接丢帧。
|
||||
eoi_index = jpeg_payload.rfind(b"\xff\xd9")
|
||||
if eoi_index < 0:
|
||||
return jpeg_payload
|
||||
return jpeg_payload, b""
|
||||
|
||||
return jpeg_payload[: eoi_index + 2]
|
||||
trailer_start = eoi_index + 2
|
||||
return jpeg_payload[:trailer_start], jpeg_payload[trailer_start:]
|
||||
|
||||
def _extract_jpeg_frame(self, frame: bytes) -> bytes | None:
|
||||
split_payload = self._split_jpeg_frame_and_trailer(frame)
|
||||
if split_payload is None:
|
||||
return None
|
||||
jpeg_frame, _ = split_payload
|
||||
return jpeg_frame
|
||||
|
||||
def _extract_sequence(self, frame: bytes) -> int | None:
|
||||
if len(frame) >= 8 and not frame.startswith(b"\xff\xd8"):
|
||||
@@ -228,50 +251,26 @@ class OmniSocketVideoReceiver:
|
||||
return None
|
||||
|
||||
def _extract_frame_tail(self, frame: bytes) -> bytes:
|
||||
jpeg_payload = self._extract_jpeg_payload(frame)
|
||||
if jpeg_payload is None:
|
||||
split_payload = self._split_jpeg_frame_and_trailer(frame)
|
||||
if split_payload is None:
|
||||
return b""
|
||||
|
||||
eoi_index = jpeg_payload.rfind(b"\xff\xd9")
|
||||
if eoi_index < 0:
|
||||
return b""
|
||||
|
||||
trailer_start = eoi_index + 2
|
||||
if trailer_start >= len(jpeg_payload):
|
||||
return b""
|
||||
|
||||
return jpeg_payload[trailer_start:]
|
||||
_, trailer = split_payload
|
||||
return trailer
|
||||
|
||||
def _extract_frame_timestamp(self, frame: bytes) -> tuple[int, str, str] | None:
|
||||
trailer = self._extract_frame_tail(frame)
|
||||
if len(trailer) < 8:
|
||||
if len(trailer) != VIDEO_TIMESTAMP_TRAILER_BYTES:
|
||||
return None
|
||||
|
||||
now_ns = time.time_ns()
|
||||
raw_timestamp = trailer[-8:]
|
||||
best_candidate: tuple[int, str, str] | None = None
|
||||
best_distance_ns: int | None = None
|
||||
|
||||
for endianness in ("big", "little"):
|
||||
value = int.from_bytes(raw_timestamp, endianness, signed=True)
|
||||
value = int.from_bytes(trailer, VIDEO_TIMESTAMP_ENDIANNESS, signed=False)
|
||||
if value <= 0:
|
||||
continue
|
||||
return None
|
||||
|
||||
for unit, multiplier in (
|
||||
("ns", 1),
|
||||
("us", 1_000),
|
||||
("ms", 1_000_000),
|
||||
("s", 1_000_000_000),
|
||||
):
|
||||
timestamp_ns = value * multiplier
|
||||
distance_ns = abs(now_ns - timestamp_ns)
|
||||
if distance_ns > VIDEO_TIMESTAMP_MAX_SKEW_NS:
|
||||
continue
|
||||
if best_distance_ns is None or distance_ns < best_distance_ns:
|
||||
best_distance_ns = distance_ns
|
||||
best_candidate = (timestamp_ns, unit, endianness)
|
||||
timestamp_ns = value * VIDEO_TIMESTAMP_MULTIPLIER_NS
|
||||
if abs(time.time_ns() - timestamp_ns) > VIDEO_TIMESTAMP_MAX_SKEW_NS:
|
||||
return None
|
||||
|
||||
return best_candidate
|
||||
return timestamp_ns, VIDEO_TIMESTAMP_UNIT, VIDEO_TIMESTAMP_ENDIANNESS
|
||||
|
||||
def _run(self) -> None:
|
||||
# 后台持续接收循环:
|
||||
|
||||
Reference in New Issue
Block a user