Skip to content

Commit 7ac99aa

Browse files
WyattBlueclaude
andcommitted
Fix memory growth when remuxing with add_stream_from_template
start_encoding() was calling avcodec_open2() on codec contexts created by add_stream_from_template(), fully initializing the codec (e.g. libx264 allocates x264_t, thread pools, reference frames) even when the stream is only used for remuxing and never calls encode()/decode(). After freeing, the C heap retains this memory, causing RSS to grow with each output segment. Add a _template_initialized flag to CodecContext, set by add_stream_from_template(). In start_encoding(), contexts with this flag skip avcodec_open2() — the codec opens lazily via encode() or decode() if actually needed. Fixes #2135 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1c7beae commit 7ac99aa

2 files changed

Lines changed: 12 additions & 5 deletions

File tree

av/codec/context.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ cdef class CodecContext:
1414
# Whether AVCodecContext.extradata should be de-allocated upon destruction.
1515
cdef bint extradata_set
1616

17+
# True when created via add_stream_from_template(); start_encoding() skips
18+
# avcodec_open2() and lets encode()/decode() open the codec lazily if needed.
19+
cdef readonly bint _template_initialized
20+
1721
# Used as a signal that this is within a stream, and also for us to access that
1822
# stream. This is set "manually" by the stream after constructing this object.
1923
cdef int stream_index

av/container/output.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ def add_stream_from_template(
193193

194194
# Construct the user-land stream
195195
py_codec_context: CodecContext = wrap_codec_context(ctx, codec, None)
196+
py_codec_context._template_initialized = True
196197
py_stream: Stream = wrap_stream(self, stream, py_codec_context)
197198
self.streams.add_stream(py_stream)
198199

@@ -368,12 +369,14 @@ def start_encoding(self):
368369
if not ctx.is_open:
369370
for k, v in self.options.items():
370371
ctx.options.setdefault(k, v)
371-
ctx.open()
372372

373-
# Track option consumption.
374-
for k in self.options:
375-
if k not in ctx.options:
376-
used_options.add(k)
373+
if not ctx._template_initialized:
374+
ctx.open()
375+
376+
# Track option consumption.
377+
for k in self.options:
378+
if k not in ctx.options:
379+
used_options.add(k)
377380

378381
stream._finalize_for_output()
379382

0 commit comments

Comments
 (0)