Files
ESP32S3_vt/scripts/test-latest-frame.py
T
sloray cc6b82d7ec
构建客户端 / 检查(类型 + 单元测试) (push) Canceled after 0s
构建客户端 / Linux x64(deb + AppImage) (push) Canceled after 0s
构建客户端 / Windows x64(NSIS 安装包) (push) Canceled after 0s
构建客户端 / macOS 通用(.app + .dmg) (push) Canceled after 0s
feat: 链路控制、延迟校准与长测工具,补全文档
- 固件:新增 link_control / stream_control,覆盖运行中 Wi-Fi 断线恢复、取帧节拍、
  温控与质量/帧率减载;UDP 发送增加有界重试预算与帧期限,PSRAM JPEG 直发减少拷贝。
- 协议:重组器严格校验分片布局,统计未收齐、超时与完全缺失的帧,处理帧号回绕与设备重启。
- 桌面端:视频改 latest-value 通道并前置帧时间标记,拒绝积压和慢解码的过期画面;新增
  延迟标记页与 --latency-report 校准,完善录制、服务端与退出流程。
- 文档与脚本:新增硬件、开发、延迟、功耗、实时性、稳定性文档和改动记录,重写根 README;
  新增构建、性能、延迟与串口长测脚本,以及长测客户端脚本。
- 构建:固定 esp-1.97.0.0 工具链,分区表随构建复制,更新 sdkconfig 与忽略规则。
2026-10-19 00:19:07 +08:00

66 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Compile the actual driver handoff function with deterministic queue stubs."""
from pathlib import Path
import shutil
import subprocess
import sys
root = Path(__file__).resolve().parents[1]
source = (root/'components/esp32-camera/driver/cam_hal.c').read_text()
start = source.index('camera_fb_t *cam_take_latest(')
end = source.index('\nvoid cam_give(', start)
function = source[start:end]
out = root/'target/latest-frame-test'
out.mkdir(parents=True, exist_ok=True)
test = r'''
#include <assert.h>
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
typedef unsigned TickType_t;
typedef struct { int id; } camera_fb_t;
static struct { size_t frame_cnt; } object = {3}, *cam_obj = &object;
static camera_fb_t frames[] = {{0},{1},{2},{3},{4},{5}};
static int calls, releases[6], mode;
static camera_fb_t *cam_take(TickType_t wait) {
assert(wait == (calls == 0 ? 100 : 0));
++calls;
if (mode == 1 || (mode == 2 && calls == 2)) return NULL;
assert(calls < 6);
return &frames[calls];
}
static void cam_give(camera_fb_t *frame) { releases[frame->id]++; }
'''+function+r'''
static void reset(int scenario) {
mode=scenario; calls=0;
for(int i=0;i<6;i++) releases[i]=0;
}
int main(void) {
uint32_t skipped=99;
// A producer that stays ahead cannot make the drain loop run forever.
reset(0);
camera_fb_t *latest=cam_take_latest(100,&skipped);
assert(calls==3 && latest->id==3 && skipped==2);
assert(releases[1]==1 && releases[2]==1 && releases[3]==0);
// An unavailable/invalid newer candidate must not release our valid frame.
reset(2);
latest=cam_take_latest(100,&skipped);
assert(calls==2 && latest->id==1 && skipped==0 && releases[1]==0);
// A first-frame timeout has no handle to return, and resets the skip count.
reset(1);
assert(cam_take_latest(100,&skipped)==NULL && skipped==0 && calls==1);
// A single capture buffer performs no second, nonblocking acquisition.
reset(0); object.frame_cnt=1;
latest=cam_take_latest(100,NULL);
assert(calls==1 && latest->id==1 && releases[1]==0);
puts("4 latest-frame ownership and bounded-drain scenarios passed");
}
'''
(out/'latest-frame.c').write_text(test)
compiler = shutil.which('clang') or shutil.which('gcc')
if not compiler:
sys.exit('A host clang or gcc is required for this optional driver test')
binary = out/('latest-frame.exe' if sys.platform=='win32' else 'latest-frame')
subprocess.run([compiler,'-std=c11','-Wall','-Wextra','-Werror',str(out/'latest-frame.c'),'-o',str(binary)],check=True)
subprocess.run([str(binary)],check=True)