Skip to content

Commit 040da79

Browse files
committed
Free the codec ctx when building a stream fails
1 parent e537231 commit 040da79

1 file changed

Lines changed: 66 additions & 36 deletions

File tree

av/container/output.py

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from cython.cimports.av.packet import Packet
1313
from cython.cimports.av.stream import Stream, wrap_stream
1414
from cython.cimports.av.utils import dict_to_avdict, to_avrational
15-
from cython.cimports.libc.stdint import uint8_t
15+
from cython.cimports.libc.stdint import int64_t, uint8_t
1616
from cython.cimports.libc.string import memcpy, memset
1717

1818

@@ -146,12 +146,22 @@ def add_stream(
146146
has_time_base: cython.bint = "time_base" in kwargs
147147
if has_time_base:
148148
to_avrational(kwargs.pop("time_base"), cython.address(c_time_base))
149+
150+
c_width: cython.int = 0
151+
c_height: cython.int = 0
152+
c_bit_rate: int64_t = 0
153+
c_bit_rate_tolerance: cython.int = 0
149154
if codec.type == lib.AVMEDIA_TYPE_VIDEO:
150155
to_avrational(rate or 24, cython.address(c_framerate))
151-
elif codec.type == lib.AVMEDIA_TYPE_AUDIO and not (
152-
rate is None or type(rate) is int
153-
):
154-
raise TypeError("audio stream `rate` must be: int | None")
156+
c_width = kwargs.pop("width", 640)
157+
c_height = kwargs.pop("height", 480)
158+
c_bit_rate = kwargs.pop("bit_rate", 0)
159+
c_bit_rate_tolerance = kwargs.pop("bit_rate_tolerance", 128000)
160+
elif codec.type == lib.AVMEDIA_TYPE_AUDIO:
161+
if not (rate is None or type(rate) is int):
162+
raise TypeError("audio stream `rate` must be: int | None")
163+
c_bit_rate = kwargs.pop("bit_rate", 0)
164+
c_bit_rate_tolerance = kwargs.pop("bit_rate_tolerance", 32000)
155165

156166
# Create new stream in the AVFormatContext, set AVCodecContext values.
157167
ctx: cython.pointer[lib.AVCodecContext] = lib.avcodec_alloc_context3(codec)
@@ -168,10 +178,10 @@ def add_stream(
168178
# Now let's set some more sane video defaults
169179
if codec.type == lib.AVMEDIA_TYPE_VIDEO:
170180
ctx.pix_fmt = lib.AV_PIX_FMT_YUV420P
171-
ctx.width = kwargs.pop("width", 640)
172-
ctx.height = kwargs.pop("height", 480)
173-
ctx.bit_rate = kwargs.pop("bit_rate", 0)
174-
ctx.bit_rate_tolerance = kwargs.pop("bit_rate_tolerance", 128000)
181+
ctx.width = c_width
182+
ctx.height = c_height
183+
ctx.bit_rate = c_bit_rate
184+
ctx.bit_rate_tolerance = c_bit_rate_tolerance
175185
ctx.framerate = c_framerate
176186

177187
stream.avg_frame_rate = ctx.framerate
@@ -190,8 +200,8 @@ def add_stream(
190200
)
191201
if out:
192202
ctx.sample_fmt = cython.cast(cython.pointer[lib.AVSampleFormat], out)[0]
193-
ctx.bit_rate = kwargs.pop("bit_rate", 0)
194-
ctx.bit_rate_tolerance = kwargs.pop("bit_rate_tolerance", 32000)
203+
ctx.bit_rate = c_bit_rate
204+
ctx.bit_rate_tolerance = c_bit_rate_tolerance
195205
ctx.sample_rate = 48000 if rate is None else rate
196206
stream.time_base = ctx.time_base
197207
lib.av_channel_layout_default(cython.address(ctx.ch_layout), 2)
@@ -204,9 +214,13 @@ def add_stream(
204214
#
205215
# Subsequent changes to the codec context will be applied just before
206216
# encoding starts in `start_encoding()`.
207-
err_check(lib.avcodec_parameters_from_context(stream.codecpar, ctx))
217+
try:
218+
err_check(lib.avcodec_parameters_from_context(stream.codecpar, ctx))
219+
except Exception:
220+
lib.avcodec_free_context(cython.address(ctx))
221+
raise
208222

209-
# Construct the user-land stream
223+
# Construct the user-land stream, which takes ownership of ctx.
210224
py_codec_context: CodecContext = wrap_codec_context(ctx, codec, hwaccel)
211225
py_stream: Stream = wrap_stream(self, stream, py_codec_context)
212226
self.streams.add_stream(py_stream)
@@ -268,10 +282,15 @@ def add_mux_stream(self, codec_name: str, rate=None, **kwargs) -> Stream:
268282
)
269283

270284
c_rate: lib.AVRational
271-
if rate is not None:
272-
if codec_type == lib.AVMEDIA_TYPE_VIDEO:
285+
c_width: cython.int = 0
286+
c_height: cython.int = 0
287+
if codec_type == lib.AVMEDIA_TYPE_VIDEO:
288+
if rate is not None:
273289
to_avrational(rate, cython.address(c_rate))
274-
elif codec_type == lib.AVMEDIA_TYPE_AUDIO and type(rate) is not int:
290+
c_width = kwargs.pop("width", 0)
291+
c_height = kwargs.pop("height", 0)
292+
elif codec_type == lib.AVMEDIA_TYPE_AUDIO:
293+
if rate is not None and type(rate) is not int:
275294
raise TypeError("audio stream `rate` must be: int | None")
276295

277296
# Create stream with no codec context.
@@ -285,8 +304,8 @@ def add_mux_stream(self, codec_name: str, rate=None, **kwargs) -> Stream:
285304
stream.codecpar.codec_type = codec_type
286305

287306
if codec_type == lib.AVMEDIA_TYPE_VIDEO:
288-
stream.codecpar.width = kwargs.pop("width", 0)
289-
stream.codecpar.height = kwargs.pop("height", 0)
307+
stream.codecpar.width = c_width
308+
stream.codecpar.height = c_height
290309
if rate is not None:
291310
stream.avg_frame_rate = c_rate
292311
elif codec_type == lib.AVMEDIA_TYPE_AUDIO and rate is not None:
@@ -346,28 +365,34 @@ def add_stream_from_template(
346365
lib.avcodec_free_context(cython.address(ctx))
347366
raise MemoryError("Could not allocate stream")
348367

349-
err_check(lib.avcodec_parameters_to_context(ctx, template.ptr.codecpar))
350-
# Reset the codec tag assuming we are remuxing.
351-
ctx.codec_tag = 0
368+
try:
369+
err_check(lib.avcodec_parameters_to_context(ctx, template.ptr.codecpar))
370+
# Reset the codec tag assuming we are remuxing.
371+
ctx.codec_tag = 0
352372

353-
# Copy the template's stream time_base
354-
stream.time_base = template.ptr.time_base
355-
ctx.time_base = template.ptr.time_base
373+
# Copy the template's stream time_base
374+
stream.time_base = template.ptr.time_base
375+
ctx.time_base = template.ptr.time_base
356376

357-
# Some formats want stream headers to be separate
358-
if self.ptr.oformat.flags & lib.AVFMT_GLOBALHEADER:
359-
ctx.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER
377+
# Some formats want stream headers to be separate
378+
if self.ptr.oformat.flags & lib.AVFMT_GLOBALHEADER:
379+
ctx.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER
360380

361-
# Copy flags If we're creating a new codec object. This fixes some muxing issues.
362-
# Overwriting `ctx.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER` is intentional.
363-
if not opaque:
364-
ctx.flags = template.codec_context.flags
381+
# Copy flags If we're creating a new codec object. This fixes some
382+
# muxing issues. Overwriting the flag set just above is intentional.
383+
if not opaque:
384+
ctx.flags = template.codec_context.flags
365385

366-
# Initialize stream codec parameters to populate the codec type. Subsequent changes to
367-
# the codec context will be applied just before encoding starts in `start_encoding()`.
368-
err_check(lib.avcodec_parameters_from_context(stream.codecpar, ctx))
386+
# Initialize stream codec parameters to populate the codec type.
387+
# Subsequent changes to the codec context will be applied just
388+
# before encoding starts in `start_encoding()`.
389+
err_check(lib.avcodec_parameters_from_context(stream.codecpar, ctx))
390+
except Exception:
391+
# Nothing owns ctx until wrap_codec_context() below.
392+
lib.avcodec_free_context(cython.address(ctx))
393+
raise
369394

370-
# Construct the user-land stream
395+
# Construct the user-land stream, which takes ownership of ctx.
371396
py_codec_context: CodecContext = wrap_codec_context(ctx, codec, None)
372397
py_codec_context._ctxflags |= 1 # _template_initialized = True
373398
py_stream: Stream = wrap_stream(self, stream, py_codec_context)
@@ -509,7 +534,12 @@ def add_data_stream(self, codec_name=None, options: dict | None = None):
509534
ctx.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER
510535

511536
# Initialize stream codec parameters
512-
err_check(lib.avcodec_parameters_from_context(stream.codecpar, ctx))
537+
try:
538+
err_check(lib.avcodec_parameters_from_context(stream.codecpar, ctx))
539+
except Exception:
540+
# Nothing owns ctx until wrap_codec_context() below.
541+
lib.avcodec_free_context(cython.address(ctx))
542+
raise
513543
else:
514544
# No codec available - set basic parameters for data stream
515545
stream.codecpar.codec_type = lib.AVMEDIA_TYPE_DATA

0 commit comments

Comments
 (0)