Công tyĐọc khoảng 2 phút
Internal Productivity & Biometric Suite
Bộ module xử lý media phía client bằng ffmpeg.wasm và xác minh khuôn mặt bằng vector embedding cho terminal Kiosk.
- Next.js
- Capacitor
- FastAPI
- Qdrant
- FFmpeg
- IndexedDB
Bộ module nội bộ gồm hai phần: xử lý media ngay trên trình duyệt và một hệ thống xác minh khuôn mặt chạy trên terminal Kiosk.
Xử lý media phía client#
Yêu cầu: người dùng tải lên video ngắn, hệ thống cần nén trước khi gửi. Đẩy toàn bộ việc nén lên server nghĩa là phải truyền file gốc rất nặng qua đường truyền yếu tại điểm bán.
Giải pháp là dùng ffmpeg.wasm xử lý ngay trong trình duyệt, chạy trong Web Worker
để không chặn giao diện:
self.onmessage = async ({ data }: MessageEvent<TranscodeRequest>) => {
const ffmpeg = await loadFFmpeg();
await ffmpeg.writeFile('in.mp4', data.bytes);
await ffmpeg.exec([
'-i', 'in.mp4',
/* CRF 28 + preset nhanh: đủ nhỏ để gửi, đủ rõ để duyệt thủ công. */
'-vcodec', 'libx264', '-crf', '28', '-preset', 'veryfast',
'-vf', 'scale=-2:720',
'out.mp4',
]);
const output = await ffmpeg.readFile('out.mp4');
/* Transferable: chuyển quyền sở hữu buffer thay vì copy sang main thread. */
self.postMessage({ bytes: output }, [(output as Uint8Array).buffer]);
};Xác minh khuôn mặt#
Phần sinh trắc học so khớp khuôn mặt bằng vector embedding lưu trong Qdrant, không lưu ảnh gốc:
@router.post("/verify")
async def verify(payload: VerifyRequest) -> VerifyResponse:
embedding = encoder.encode(payload.frame)
hits = qdrant.search(
collection_name="faces",
query_vector=embedding,
limit=1,
)
# Ngưỡng đặt cao: từ chối nhầm còn chấp nhận được, chấp nhận nhầm thì không.
if not hits or hits[0].score < MATCH_THRESHOLD:
return VerifyResponse(matched=False)
return VerifyResponse(matched=True, subject_id=hits[0].payload["subject_id"])Kết quả#
- Video được nén ngay trên máy người dùng, giảm mạnh dung lượng phải truyền
- Giao diện không bị đơ trong lúc xử lý nhờ Web Worker
- Hệ thống xác minh không lưu trữ ảnh khuôn mặt gốc