-
-
Notifications
You must be signed in to change notification settings - Fork 0
Thread Safety
The libnvmpi decoder uses two threads:
-
User thread — calls the public API (
create,put_packet,get_frame,flush,close). Only one user thread may interact with a decoder context. -
Capture thread — runs
dec_capture_loop_fcn()for the lifetime of the context. Dequeues decoded V4L2 buffers, transforms them via VIC, and publishes frames to the pool.
The two threads communicate through the buffer pool (NVMPI_bufPool) and the
atomic eos flag.
The encoder follows the same two-thread pattern: a user thread feeds frames
via put_frame and reads packets via get_packet, while NVIDIA's internal
DQ thread calls back into the packet pool.
User thread Capture thread
----------- --------------
put_packet() dec_capture_loop_fcn()
| memcpy -> V4L2 output |
| +- dqBuffer (V4L2 capture)
get_frame(wait=true) +- VIC transform
| +- qFilledBuf() -> notify_one()
+- dqFilledBuf(timeout) |
| '- cv.wait_for(100ms) <-----'
| '- check shutdown
| '- re-wait or return +- on EOS: shutdown() -> notify_all()
| '- return
|
close()
+- eos.store(true)
+- framePool->shutdown()
+- capture_plane.setStreamStatus(false)
'- thread.join()
| Primitive | Type | Location | Protects |
|---|---|---|---|
m_filledBuf |
std::mutex |
NVMPI_bufPool |
filledBuf queue and cv_filledBuf
|
m_emptyBuf |
std::mutex |
NVMPI_bufPool |
emptyBuf queue |
cv_filledBuf |
std::condition_variable |
NVMPI_bufPool |
Blocking wait on filled-buffer availability |
m_shutdown |
std::atomic<bool> |
NVMPI_bufPool |
Shutdown signal for blocked consumers |
eos |
std::atomic<bool> |
nvmpictx |
End-of-stream signal between user and capture threads |
-
Single-lock rule: No function acquires more than one mutex.
shutdown()acquiresm_filledBufbriefly to setm_shutdown, then releases beforenotify_all(). -
Tiered timeout:
dqFilledBuf(timeout)re-checksm_shutdownevery 100ms (hardcoded granularity). Even if anotify_all()is missed, the worst-case wake latency is 100ms. -
Shutdown on every exit path:
dec_capture_loop_fcn()callsframePool->shutdown()before returning. This covers: normal EOS, dqEvent error, dqBuffer error, and external eos signal. -
Close ordering:
close()setseos, callsshutdown(), thensetStreamStatus(false), thenjoin(). The capture thread will either already be exiting (eos) or will exit on the next dqBuffer failure.
| Function | Thread | Notes |
|---|---|---|
nvmpi_create_decoder() |
User | Creates capture thread; no concurrent access yet |
nvmpi_decoder_put_packet() |
User | Feeds V4L2 output plane; may block on dqBuffer |
nvmpi_decoder_get_frame() |
User | wait=false: non-blocking. wait=true: blocks on CV |
nvmpi_decoder_flush() |
User | Joins capture thread, drains pool, restarts |
nvmpi_decoder_close() |
User | Joins capture thread, frees everything |
dec_capture_loop_fcn() |
Capture | Runs for context lifetime; calls shutdown() on exit |
respondToResolutionEvent() |
Capture | Reinits planes and pool |
qFilledBuf() |
Capture | Lock + push + notify_one |
dqFilledBuf() |
User | Lock + pop (non-blocking) or CV wait (blocking) |
shutdown() |
Either | Lock + set flag + notify_all |
reset() |
User only | Called during flush, after thread join |
-
flush()setseos = true, stops capture stream, joins thread. - Drains filled frames back to empty queue.
- Calls
framePool->reset()to clear shutdown flag. - Sets
eos = false, restarts output stream, spawns new capture thread.
The pool's reset() must be called between shutdown() and the next blocking
dequeue, otherwise the new capture thread's frames would be ignored.
When nvmpi_decoder_get_frame() is called with wait=true:
- Calls
dqFilledBuf(milliseconds(wait_timeout_ms))on the frame pool. - Inside, the thread acquires
m_filledBufand enters a loop:- If
filledBufis non-empty, pop and return immediately. - If
m_shutdownis set, return NULL. - If deadline has passed, return NULL.
- Otherwise,
cv.wait_for(min(remaining, 100ms))— releases the lock during the wait and reacquires on wakeup.
- If
- The 100ms granularity ensures the thread checks
m_shutdownat least 10 times per second, regardless of whethernotify_all()arrived.
The wait_timeout_ms defaults to 500ms and is configurable via
nvDecParam.wait_timeout (range 50–5000ms, 0 = use default).
The encoder uses NVIDIA's built-in DQ thread (startDQThread()/stopDQThread()):
- User thread calls
put_frame()to feed raw frames. - DQ thread dequeues encoded packets from the capture plane and calls
encoder_capture_plane_dq_callback(), which fills packet buffers from the pool viadqEmptyPacket()/qFilledPacket(). -
close()callsstopDQThread()+waitForDQThread(1000)before destroying the pool and encoder.
The same NVMPI_bufPool template is used for the encoder's packet pool,
with the same mutex/queue semantics but without blocking dequeue (the
encoder wrapper uses non-blocking polling).
🏠 Home · 📦 Repository · 🐞 Issues · 🏷 Releases · 📋 README · 📝 CHANGELOG
Documentation lives in this wiki. To change a doc, edit the relevant wiki page (clone jetson-ffmpeg.wiki.git) — the docs/ folder in the repo has been retired.
📦 Usage & Runtime
❓ FAQ
- Overview
- Hardware & Platform
- Versions & Compatibility
- Build & Install
- Performance
- Features & Fork Differences
🛠 Development
⚙️ CI / Infrastructure
- GitHub Actions
- GitLab CI
- GitHub Runner — Manual
- GitHub Runner — Kubernetes
- GitLab Runner — Manual
- GitLab Runner — Kubernetes
- Release Process
🔬 Project / Fork Analysis