feat(gateway): split video recording into silence-based segments like voice

Video (camera + screen share) DAVE stream-watch now produces per-burst
MP4 segments instead of one long .h264 per watch:
- Detects VIDEO_SILENCE_MS (4000ms) of no H264 packets → closes the
  current segment, muxes to MP4, registers in voice_recordings + uploads
  to TeleUploader, then reopens for the next burst (mirrors voice AfterSilence).
- Per-watch segment counter + per-segment depacketizer reset + closing
  guard + write-error swallow so races (silence close vs in-flight UDP
  packet) never corrupt files or crash the gateway.
- Frontend: recordings deck renders a native <video> player for MP4 rows
  (detected by filename), keeps single-playback registry across audio+video.
This commit is contained in:
asepharyana
2026-09-01 21:51:44 +07:00
committed by asepharyana
parent eb2f8f5b6f
commit 4ec9685194
5 changed files with 417 additions and 49 deletions
@@ -55,6 +55,7 @@ import type {
SpeakerSummary,
VoiceRecording,
} from "@/lib/types";
import { isVideoRecording } from "@/lib/types/recording";
import { useWebSocket } from "@/lib/ws/context";
const ALL = "__all__";
@@ -557,7 +558,10 @@ export function RecordingsView({
{r.download_url ? (
<RecordingAudioPlayer
src={r.download_url}
label={`Voice recording by ${r.username}`}
label={`${
isVideoRecording(r) ? "Video" : "Voice"
} recording by ${r.username}`}
video={isVideoRecording(r)}
onPlayStateChange={(active) =>
setPlayingId((prev) => {
if (active) return r.id;