- 固件:新增 link_control / stream_control,覆盖运行中 Wi-Fi 断线恢复、取帧节拍、 温控与质量/帧率减载;UDP 发送增加有界重试预算与帧期限,PSRAM JPEG 直发减少拷贝。 - 协议:重组器严格校验分片布局,统计未收齐、超时与完全缺失的帧,处理帧号回绕与设备重启。 - 桌面端:视频改 latest-value 通道并前置帧时间标记,拒绝积压和慢解码的过期画面;新增 延迟标记页与 --latency-report 校准,完善录制、服务端与退出流程。 - 文档与脚本:新增硬件、开发、延迟、功耗、实时性、稳定性文档和改动记录,重写根 README; 新增构建、性能、延迟与串口长测脚本,以及长测客户端脚本。 - 构建:固定 esp-1.97.0.0 工具链,分区表随构建复制,更新 sdkconfig 与忽略规则。
66 lines
2.6 KiB
Python
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)
|