The decoder thread holds a raw AVCodecContext* obtained from
camera->getVideoCodecContext() and pushes packet locks into Monitor's
decoder_queue for each send_packet() that hasn't yet been matched by a
receive_frame(). Monitor::PrimeCapture() used to call camera->PrimeCapture()
(which Close()s the camera and frees the codec context) without first
stopping the decoder thread. Two problems followed:
1. Use-after-free race between the decoder thread and the camera teardown.
2. The stale decoder_queue entries survived the reconnect. The new codec
context produced frames in send-order, so we popped the oldest stale
entries to attribute frames that actually came from packets sent later.
The net effect was a permanent N-packet offset between capture and
decode (~92 packets observed in the field). Analysis blocks on
!packet->decoded for those packets, so the packetqueue saturates at
max_video_packet_count and stays there, spamming the "max video packets
in the queue" warning forever.
Fix:
- Stop+Join the decoder in Monitor::PrimeCapture() before tearing down the
codec context.
- Add Monitor::flushDecoderQueue() which marks in-flight packets decoded,
notifies waiters, and clears the queue.
- Call it at the end of DecoderThread::Run() so any Stop()+Join() (including
the existing one in Pause()) naturally releases stale entries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>