Fix: DVR recording leaks one heap buffer per frame - #115
Open
iflyhere wants to merge 1 commit into
Open
Conversation
onNewNALU() allocated a copy of every NALU with new uint8_t[] and pushed it
into naluQueue as a NALU. NALU is documented as a non-owning view ("it does
not do any memory management", NALU.hpp) and its destructor is defaulted, so
popping the queue never freed anything.
The buffer leaks for as long as a recording runs - roughly the video bitrate,
so ~1 MB/s at 8 Mbit/s. A ten minute recording leaks a few hundred MB and the
app eventually gets killed by the OOM killer, mid flight.
The queue now holds an owning DvrNalu { std::vector<uint8_t>, bool } that is
moved in and out, so the bytes are freed with the queue entry and anything
still queued is released when the writer thread stops.
Note: the queued NALU is also read after the h265 flag was needed for
mp4_h26x_write_init(), so the flag is read from the front element before the
move instead.
PR Summary by QodoPrevent DVR per-frame NALU buffer leak
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
This was referenced Aug 27, 2026
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
VideoPlayer::onNewNALU()hands every NALU to the DVR writer thread by copyingit onto the heap:
NALUis a non-owning view. FromNALU.hpp:Its destructor is
= default, sonaluQueue.pop()inprocessQueue()freesnothing. There is no matching
delete[]anywhere.Every recorded frame leaks its full buffer, so the leak rate is the video
bitrate: about 1 MB/s at 8 Mbit/s. A ten minute recording leaks a few hundred MB
and the process eventually gets killed by the OOM killer — while recording, i.e.
while flying. Anything still queued when recording stops leaks as well.
The fix
The queue holds an owning value instead:
enqueueNALU()takes it by rvalue reference and moves it in,processQueue()moves it back out. The bytes are freed with the queue entry, and whatever is
still queued is released when the queue is destroyed.
One small restructure in
processQueue(): the H.265 flag of the front element isneeded for
mp4_h26x_write_init()before the element is consumed, and theexisting
continuepath (waiting forcurrentFPS > 0) must not consume it. Theflag is therefore read from
naluQueue.front()and the element is only moved outafter that.
Noticed while in there, not fixed here
processQueue()callsMP4E_close(mux)beforemp4_h26x_write_close(&mp4wr),and
mp4wris uninitialised if the thread stops before the first frame waswritten (
framerate == 0), so that close reads uninitialised stack. Left out tokeep this PR to the leak — happy to add it here or open a separate one.
Part of a small series of independent fixes found while profiling the receive path.
Each one is standalone and mergeable on its own, in any order — no dependencies
between them, and no shared files except
VideoActivity.java/VideoPlayer.*,which touch different methods:
All five compile clean for arm64-v8a + armeabi-v7a.