Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions av/codec/context.pxd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cimport libav as lib
from libc.stdint cimport int64_t
from libc.stdint cimport int64_t, uint8_t

from av.buffer cimport ByteSource
from av.codec.codec cimport Codec
Expand All @@ -11,13 +11,6 @@ from av.packet cimport Packet
cdef class CodecContext:
cdef lib.AVCodecContext *ptr

# Whether AVCodecContext.extradata should be de-allocated upon destruction.
cdef bint extradata_set

# True when created via add_stream_from_template(); start_encoding() skips
# avcodec_open2() and lets encode()/decode() open the codec lazily if needed.
cdef readonly bint _template_initialized

# Used as a signal that this is within a stream, and also for us to access that
# stream. This is set "manually" by the stream after constructing this object.
cdef int stream_index
Expand All @@ -40,6 +33,10 @@ cdef class CodecContext:
# Used by hardware-accelerated decode.
cdef HWAccel hwaccel_ctx

cdef uint8_t _ctxflags # ctxEnum: template_initialized
# True when created via add_stream_from_template(); start_encoding() skips
# avcodec_open2() and lets encode()/decode() open the codec lazily if needed.

# Used by both transcode APIs to setup user-land objects.
# TODO: Remove the `Packet` from `_setup_decoded_frame` (because flushing packets
# are bogus). It should take all info it needs from the context and/or stream.
Expand Down
4 changes: 1 addition & 3 deletions av/codec/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ def extradata(self, data):
raise MemoryError("Cannot allocate extradata")
memcpy(self.ptr.extradata, source.ptr, source.length)
self.ptr.extradata_size = source.length
self.extradata_set = True

@property
def extradata_size(self):
Expand Down Expand Up @@ -253,9 +252,8 @@ def open(self, strict: cython.bint = True):
self.options = dict(options)

def __dealloc__(self):
if self.ptr and self.extradata_set:
lib.av_freep(cython.address(self.ptr.extradata))
if self.ptr:
lib.av_freep(cython.address(self.ptr.extradata))
lib.avcodec_free_context(cython.address(self.ptr))
if self.parser:
lib.av_parser_close(self.parser)
Expand Down
14 changes: 8 additions & 6 deletions av/container/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
def close_output(self: OutputContainer):
self.streams = StreamContainer()
if self._myflag & 12 == 4: # enum.started and not enum.done
# If the underlying Python IO file was already closed (e.g. during GC
# finalization where cycle ordering is undefined), skip the trailer.
if self.file is not None and getattr(self.file.file, "closed", False):
self._myflag |= 8 # enum.done = True
return
# We must only ever call av_write_trailer *once*, otherwise we get a
# segmentation fault. Therefore no matter whether it succeeds or not
# we must absolutely set enum.done.
Expand All @@ -36,10 +41,7 @@ def __cinit__(self, *args, **kwargs):
self.packet_ptr = lib.av_packet_alloc()

def __del__(self):
try:
close_output(self)
except Exception:
pass
close_output(self)

def __dealloc__(self):
with cython.nogil:
Expand Down Expand Up @@ -275,7 +277,7 @@ def add_stream_from_template(

# Construct the user-land stream
py_codec_context: CodecContext = wrap_codec_context(ctx, codec, None)
py_codec_context._template_initialized = True
py_codec_context._ctxflags |= 1 # _template_initialized = True
py_stream: Stream = wrap_stream(self, stream, py_codec_context)
self.streams.add_stream(py_stream)

Expand Down Expand Up @@ -446,7 +448,7 @@ def start_encoding(self):
for k, v in self.options.items():
ctx.options.setdefault(k, v)

if not ctx._template_initialized:
if not (ctx._ctxflags & 1): # template_initialized
ctx.open()

# Track option consumption.
Expand Down
4 changes: 0 additions & 4 deletions av/video/codeccontext.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ cdef struct AVCodecPrivateData:

cdef class VideoCodecContext(CodecContext):
cdef AVCodecPrivateData _private_data
cdef VideoFormat _format
cdef _build_format(self)
cdef int last_w
cdef int last_h
cdef readonly VideoReformatter reformatter
cdef readonly int encoded_frame_count
cdef VideoFrame next_frame
32 changes: 9 additions & 23 deletions av/video/codeccontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ def _get_hw_format(

@cython.cclass
class VideoCodecContext(CodecContext):
def __cinit__(self, *args, **kwargs):
self.last_w = 0
self.last_h = 0

@cython.cfunc
def _init(
self,
Expand Down Expand Up @@ -76,21 +72,17 @@ def _init(
# is_hwaccel() function on each stream's codec context.
self.hwaccel_ctx = None

self._build_format()
self.encoded_frame_count = 0

@cython.cfunc
def _prepare_frames_for_encode(self, input: Frame | None) -> list:
if input is None or not input:
return [None]

if self._format is None:
raise ValueError("self._format is None, cannot encode")

vframe: VideoFrame = input
# Reformat if it doesn't match.
if (
vframe.format.pix_fmt != self._format.pix_fmt
vframe.format.pix_fmt != self.pix_fmt
or vframe.width != self.ptr.width
or vframe.height != self.ptr.height
):
Expand All @@ -101,7 +93,7 @@ def _prepare_frames_for_encode(self, input: Frame | None) -> list:
vframe,
self.ptr.width,
self.ptr.height,
self._format,
self.format,
threads=self.ptr.thread_count,
)

Expand Down Expand Up @@ -141,24 +133,19 @@ def _transfer_hwframe(self, frame: Frame):
frame_sw.pts = frame.pts
return frame_sw

@cython.cfunc
def _build_format(self):
self._format = get_video_format(
@property
def format(self):
return get_video_format(
cython.cast(lib.AVPixelFormat, self.ptr.pix_fmt),
self.ptr.width,
self.ptr.height,
)

@property
def format(self):
return self._format

@format.setter
def format(self, format: VideoFormat):
self.ptr.pix_fmt = format.pix_fmt
self.ptr.width = format.width
self.ptr.height = format.height
self._build_format() # Kinda wasteful.

@property
def width(self):
Expand All @@ -169,7 +156,6 @@ def width(self):
@width.setter
def width(self, value: cython.uint):
self.ptr.width = value
self._build_format()

@property
def height(self):
Expand All @@ -180,7 +166,6 @@ def height(self):
@height.setter
def height(self, value: cython.uint):
self.ptr.height = value
self._build_format()

@property
def bits_per_coded_sample(self):
Expand All @@ -199,7 +184,6 @@ def bits_per_coded_sample(self, value: cython.int):
raise ValueError("Not supported for encoders")

self.ptr.bits_per_coded_sample = value
self._build_format()

@property
def pix_fmt(self):
Expand All @@ -208,12 +192,14 @@ def pix_fmt(self):

:type: str | None
"""
return getattr(self._format, "name", None)
desc: cython.pointer[cython.const[lib.AVPixFmtDescriptor]] = (
lib.av_pix_fmt_desc_get(cython.cast(lib.AVPixelFormat, self.ptr.pix_fmt))
)
return cython.cast(str, desc.name)

@pix_fmt.setter
def pix_fmt(self, value):
self.ptr.pix_fmt = get_pix_fmt(value)
self._build_format()

@property
def framerate(self):
Expand Down
Loading