Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.6:Update
python-av
python-av-ffmpeg5-compatibility.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File python-av-ffmpeg5-compatibility.patch of Package python-av
From 18704658487ea25e5202ac18438d836dfe65b9d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= <jeremy.laine@m4x.org> Date: Fri, 11 Mar 2022 16:30:43 +0100 Subject: [PATCH] [streams] stop using deprecated Stream.codec, it's gone in FFmpeg 5 We now allocate and populate an AVCodecContext ourselves. avcodec_copy_context is also gone, so stop using it. We relax the Stream.average_rate tests for older FFmpeg, as the videos output by these older FFmpeg's seem to give a slightly wrong FPS since the switch to our own AVCodecContext. --- av/codec/context.pxd | 5 +- av/codec/context.pyx | 7 ++- av/container/input.pyx | 38 +++++++++----- av/container/output.pyx | 28 ++++------- av/container/streams.pyx | 10 ++-- av/data/stream.pyx | 2 +- av/packet.pyx | 2 +- av/stream.pxd | 10 ++-- av/stream.pyx | 85 +++++++++++++------------------- av/video/stream.pyx | 4 +- include/libavcodec/avcodec.pxd | 14 ++++-- include/libavformat/avformat.pxd | 1 - tests/common.py | 1 + tests/test_codec_context.py | 4 +- tests/test_encode.py | 50 +++++++++++++++---- 15 files changed, 143 insertions(+), 118 deletions(-) diff --git a/av/codec/context.pxd b/av/codec/context.pxd index d9b6906f9..387cb7de4 100644 --- a/av/codec/context.pxd +++ b/av/codec/context.pxd @@ -11,9 +11,6 @@ cdef class CodecContext(object): cdef lib.AVCodecContext *ptr - # Whether the AVCodecContext should be de-allocated upon destruction. - cdef bint allocated - # Whether AVCodecContext.extradata should be de-allocated upon destruction. cdef bint extradata_set @@ -64,4 +61,4 @@ cdef class CodecContext(object): cdef Frame _alloc_next_frame(self) -cdef CodecContext wrap_codec_context(lib.AVCodecContext*, const lib.AVCodec*, bint allocated) +cdef CodecContext wrap_codec_context(lib.AVCodecContext*, const lib.AVCodec*) diff --git a/av/codec/context.pyx b/av/codec/context.pyx index c9f5177c1..5c8314615 100644 --- a/av/codec/context.pyx +++ b/av/codec/context.pyx @@ -20,7 +20,7 @@ from av.dictionary import Dictionary cdef object _cinit_sentinel = object() -cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCodec *c_codec, bint allocated): +cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCodec *c_codec): """Build an av.CodecContext for an existing AVCodecContext.""" cdef CodecContext py_ctx @@ -38,7 +38,6 @@ cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCode else: py_ctx = CodecContext(_cinit_sentinel) - py_ctx.allocated = allocated py_ctx._init(c_ctx, c_codec) return py_ctx @@ -147,7 +146,7 @@ cdef class CodecContext(object): def create(codec, mode=None): cdef Codec cy_codec = codec if isinstance(codec, Codec) else Codec(codec, mode) cdef lib.AVCodecContext *c_ctx = lib.avcodec_alloc_context3(cy_codec.ptr) - return wrap_codec_context(c_ctx, cy_codec.ptr, True) + return wrap_codec_context(c_ctx, cy_codec.ptr) def __cinit__(self, sentinel=None, *args, **kwargs): if sentinel is not _cinit_sentinel: @@ -307,7 +306,7 @@ cdef class CodecContext(object): def __dealloc__(self): if self.ptr and self.extradata_set: lib.av_freep(&self.ptr.extradata) - if self.ptr and self.allocated: + if self.ptr: lib.avcodec_close(self.ptr) lib.avcodec_free_context(&self.ptr) if self.parser: diff --git a/av/container/input.pyx b/av/container/input.pyx index e0c7dcc22..e508f16f4 100644 --- a/av/container/input.pyx +++ b/av/container/input.pyx @@ -1,6 +1,7 @@ from libc.stdint cimport int64_t from libc.stdlib cimport free, malloc +from av.codec.context cimport CodecContext, wrap_codec_context from av.container.streams cimport StreamContainer from av.dictionary cimport _Dictionary from av.error cimport err_check @@ -22,7 +23,11 @@ cdef class InputContainer(Container): def __cinit__(self, *args, **kwargs): + cdef CodecContext py_codec_context cdef unsigned int i + cdef lib.AVStream *stream + cdef lib.AVCodec *codec + cdef lib.AVCodecContext *codec_context # If we have either the global `options`, or a `stream_options`, prepare # a mashup of those options for each stream. @@ -65,7 +70,18 @@ cdef class InputContainer(Container): self.streams = StreamContainer() for i in range(self.ptr.nb_streams): - self.streams.add_stream(wrap_stream(self, self.ptr.streams[i])) + stream = self.ptr.streams[i] + codec = lib.avcodec_find_decoder(stream.codecpar.codec_id) + if codec: + # allocate and initialise decoder + codec_context = lib.avcodec_alloc_context3(codec) + err_check(lib.avcodec_parameters_to_context(codec_context, stream.codecpar)) + codec_context.pkt_timebase = stream.time_base + py_codec_context = wrap_codec_context(codec_context, codec) + else: + # no decoder is available + py_codec_context = None + self.streams.add_stream(wrap_stream(self, stream, py_codec_context)) self.metadata = avdict_to_dict(self.ptr.metadata, self.metadata_encoding, self.metadata_errors) @@ -155,7 +171,7 @@ cdef class InputContainer(Container): if packet.ptr.stream_index < len(self.streams): packet._stream = self.streams[packet.ptr.stream_index] # Keep track of this so that remuxing is easier. - packet._time_base = packet._stream._stream.time_base + packet._time_base = packet._stream.ptr.time_base yield packet # Flush! @@ -163,7 +179,7 @@ cdef class InputContainer(Container): if include_stream[i]: packet = Packet() packet._stream = self.streams[i] - packet._time_base = packet._stream._stream.time_base + packet._time_base = packet._stream.ptr.time_base yield packet finally: @@ -254,11 +270,11 @@ cdef class InputContainer(Container): self.flush_buffers() cdef flush_buffers(self): - cdef unsigned int i - cdef lib.AVStream *stream - - with nogil: - for i in range(self.ptr.nb_streams): - stream = self.ptr.streams[i] - if stream.codec and stream.codec.codec and stream.codec.codec_id != lib.AV_CODEC_ID_NONE: - lib.avcodec_flush_buffers(stream.codec) + cdef Stream stream + cdef CodecContext codec_context + + for stream in self.streams: + codec_context = stream.codec_context + if codec_context and codec_context.is_open: + with nogil: + lib.avcodec_flush_buffers(codec_context.ptr) diff --git a/av/container/output.pyx b/av/container/output.pyx index 621ac8f18..a454e121e 100644 --- a/av/container/output.pyx +++ b/av/container/output.pyx @@ -3,12 +3,13 @@ import logging import os from av.codec.codec cimport Codec +from av.codec.context cimport CodecContext, wrap_codec_context from av.container.streams cimport StreamContainer from av.dictionary cimport _Dictionary from av.error cimport err_check from av.packet cimport Packet from av.stream cimport Stream, wrap_stream -from av.utils cimport dict_to_avdict +from av.utils cimport dict_to_avdict, to_avrational from av.dictionary import Dictionary @@ -64,14 +65,11 @@ cdef class OutputContainer(Container): if codec_name is not None: codec_obj = codec_name if isinstance(codec_name, Codec) else Codec(codec_name, 'w') - codec = codec_obj.ptr - else: - if not template._codec: - raise ValueError("template has no codec") - if not template._codec_context: + if not template.codec_context: raise ValueError("template has no codec context") - codec = template._codec + codec_obj = template.codec_context.codec + codec = codec_obj.ptr # Assert that this format supports the requested codec. if not lib.avformat_query_codec( @@ -82,16 +80,13 @@ cdef class OutputContainer(Container): raise ValueError("%r format does not support %r codec" % (self.format.name, codec_name)) # Create new stream in the AVFormatContext, set AVCodecContext values. - # As of last check, avformat_new_stream only calls avcodec_alloc_context3 to create - # the context, but doesn't modify it in any other way. Ergo, we can allow CodecContext - # to finish initializing it. lib.avformat_new_stream(self.ptr, codec) cdef lib.AVStream *stream = self.ptr.streams[self.ptr.nb_streams - 1] - cdef lib.AVCodecContext *codec_context = stream.codec # For readability. + cdef lib.AVCodecContext *codec_context = lib.avcodec_alloc_context3(codec) # Copy from the template. if template is not None: - lib.avcodec_copy_context(codec_context, template._codec_context) + err_check(lib.avcodec_parameters_to_context(codec_context, template.ptr.codecpar)) # Reset the codec tag assuming we are remuxing. codec_context.codec_tag = 0 @@ -103,11 +98,7 @@ cdef class OutputContainer(Container): codec_context.bit_rate = 1024000 codec_context.bit_rate_tolerance = 128000 codec_context.ticks_per_frame = 1 - - rate = Fraction(rate or 24) - - codec_context.framerate.num = rate.numerator - codec_context.framerate.den = rate.denominator + to_avrational(rate or 24, &codec_context.framerate) stream.avg_frame_rate = codec_context.framerate stream.time_base = codec_context.time_base @@ -126,7 +117,8 @@ cdef class OutputContainer(Container): codec_context.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER # Construct the user-land stream - cdef Stream py_stream = wrap_stream(self, stream) + cdef CodecContext py_codec_context = wrap_codec_context(codec_context, codec) + cdef Stream py_stream = wrap_stream(self, stream, py_codec_context) self.streams.add_stream(py_stream) if options: diff --git a/av/container/streams.pyx b/av/container/streams.pyx index 4ed2223d4..eb85d9ff3 100644 --- a/av/container/streams.pyx +++ b/av/container/streams.pyx @@ -37,16 +37,16 @@ cdef class StreamContainer(object): cdef add_stream(self, Stream stream): - assert stream._stream.index == len(self._streams) + assert stream.ptr.index == len(self._streams) self._streams.append(stream) - if stream._codec_context.codec_type == lib.AVMEDIA_TYPE_VIDEO: + if stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_VIDEO: self.video = self.video + (stream, ) - elif stream._codec_context.codec_type == lib.AVMEDIA_TYPE_AUDIO: + elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_AUDIO: self.audio = self.audio + (stream, ) - elif stream._codec_context.codec_type == lib.AVMEDIA_TYPE_SUBTITLE: + elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_SUBTITLE: self.subtitles = self.subtitles + (stream, ) - elif stream._codec_context.codec_type == lib.AVMEDIA_TYPE_DATA: + elif stream.ptr.codecpar.codec_type == lib.AVMEDIA_TYPE_DATA: self.data = self.data + (stream, ) else: self.other = self.other + (stream, ) diff --git a/av/data/stream.pyx b/av/data/stream.pyx index 698242c51..c019961d0 100644 --- a/av/data/stream.pyx +++ b/av/data/stream.pyx @@ -20,7 +20,7 @@ cdef class DataStream(Stream): property name: def __get__(self): - cdef const lib.AVCodecDescriptor *desc = lib.avcodec_descriptor_get(self._codec_context.codec_id) + cdef const lib.AVCodecDescriptor *desc = lib.avcodec_descriptor_get(self.ptr.codecpar.codec_id) if desc == NULL: return None return desc.name diff --git a/av/packet.pyx b/av/packet.pyx index fae970ee3..0687b2237 100644 --- a/av/packet.pyx +++ b/av/packet.pyx @@ -112,7 +112,7 @@ cdef class Packet(Buffer): def __set__(self, Stream stream): self._stream = stream - self.ptr.stream_index = stream._stream.index + self.ptr.stream_index = stream.ptr.index property time_base: """ diff --git a/av/stream.pxd b/av/stream.pxd index 4a3cab488..5ad3b965e 100644 --- a/av/stream.pxd +++ b/av/stream.pxd @@ -8,24 +8,20 @@ from av.packet cimport Packet cdef class Stream(object): + cdef lib.AVStream *ptr # Stream attributes. cdef readonly Container container - - cdef lib.AVStream *_stream cdef readonly dict metadata # CodecContext attributes. - cdef lib.AVCodecContext *_codec_context - cdef const lib.AVCodec *_codec - cdef readonly CodecContext codec_context # Private API. - cdef _init(self, Container, lib.AVStream*) + cdef _init(self, Container, lib.AVStream*, CodecContext) cdef _finalize_for_output(self) cdef _set_time_base(self, value) cdef _set_id(self, value) -cdef Stream wrap_stream(Container, lib.AVStream*) +cdef Stream wrap_stream(Container, lib.AVStream*, CodecContext) diff --git a/av/stream.pyx b/av/stream.pyx index cbab9dde1..73cb3504d 100644 --- a/av/stream.pyx +++ b/av/stream.pyx @@ -17,7 +17,7 @@ from av.utils cimport ( cdef object _cinit_bypass_sentinel = object() -cdef Stream wrap_stream(Container container, lib.AVStream *c_stream): +cdef Stream wrap_stream(Container container, lib.AVStream *c_stream, CodecContext codec_context): """Build an av.Stream for an existing AVStream. The AVStream MUST be fully constructed and ready for use before this is @@ -30,22 +30,22 @@ cdef Stream wrap_stream(Container container, lib.AVStream *c_stream): cdef Stream py_stream - if c_stream.codec.codec_type == lib.AVMEDIA_TYPE_VIDEO: + if c_stream.codecpar.codec_type == lib.AVMEDIA_TYPE_VIDEO: from av.video.stream import VideoStream py_stream = VideoStream.__new__(VideoStream, _cinit_bypass_sentinel) - elif c_stream.codec.codec_type == lib.AVMEDIA_TYPE_AUDIO: + elif c_stream.codecpar.codec_type == lib.AVMEDIA_TYPE_AUDIO: from av.audio.stream import AudioStream py_stream = AudioStream.__new__(AudioStream, _cinit_bypass_sentinel) - elif c_stream.codec.codec_type == lib.AVMEDIA_TYPE_SUBTITLE: + elif c_stream.codecpar.codec_type == lib.AVMEDIA_TYPE_SUBTITLE: from av.subtitles.stream import SubtitleStream py_stream = SubtitleStream.__new__(SubtitleStream, _cinit_bypass_sentinel) - elif c_stream.codec.codec_type == lib.AVMEDIA_TYPE_DATA: + elif c_stream.codecpar.codec_type == lib.AVMEDIA_TYPE_DATA: from av.data.stream import DataStream py_stream = DataStream.__new__(DataStream, _cinit_bypass_sentinel) else: py_stream = Stream.__new__(Stream, _cinit_bypass_sentinel) - py_stream._init(container, c_stream) + py_stream._init(container, c_stream, codec_context) return py_stream @@ -69,14 +69,15 @@ cdef class Stream(object): def __cinit__(self, name): if name is _cinit_bypass_sentinel: return - raise RuntimeError('cannot manually instatiate Stream') - - cdef _init(self, Container container, lib.AVStream *stream): + raise RuntimeError('cannot manually instantiate Stream') + cdef _init(self, Container container, lib.AVStream *stream, CodecContext codec_context): self.container = container - self._stream = stream + self.ptr = stream - self._codec_context = stream.codec + self.codec_context = codec_context + if self.codec_context: + self.codec_context.stream_index = stream.index self.metadata = avdict_to_dict( stream.metadata, @@ -84,23 +85,6 @@ cdef class Stream(object): errors=self.container.metadata_errors, ) - # This is an input container! - if self.container.ptr.iformat: - - # Find the codec. - self._codec = lib.avcodec_find_decoder(self._codec_context.codec_id) - if not self._codec: - # TODO: Setup a dummy CodecContext. - self.codec_context = None - return - - # This is an output container! - else: - self._codec = self._codec_context.codec - - self.codec_context = wrap_codec_context(self._codec_context, self._codec, False) - self.codec_context.stream_index = stream.index - def __repr__(self): return '<av.%s #%d %s/%s at 0x%x>' % ( self.__class__.__name__, @@ -137,17 +121,17 @@ cdef class Stream(object): cdef _finalize_for_output(self): dict_to_avdict( - &self._stream.metadata, self.metadata, + &self.ptr.metadata, self.metadata, encoding=self.container.metadata_encoding, errors=self.container.metadata_errors, ) - if not self._stream.time_base.num: - self._stream.time_base = self._codec_context.time_base + if not self.ptr.time_base.num: + self.ptr.time_base = self.codec_context.ptr.time_base # It prefers if we pass it parameters via this other object. # Lets just copy what we want. - err_check(lib.avcodec_parameters_from_context(self._stream.codecpar, self._stream.codec)) + err_check(lib.avcodec_parameters_from_context(self.ptr.codecpar, self.codec_context.ptr)) def encode(self, frame=None): """ @@ -165,7 +149,7 @@ cdef class Stream(object): cdef Packet packet for packet in packets: packet._stream = self - packet.ptr.stream_index = self._stream.index + packet.ptr.stream_index = self.ptr.index return packets def decode(self, packet=None): @@ -190,16 +174,16 @@ cdef class Stream(object): """ def __get__(self): - return self._stream.id + return self.ptr.id cdef _set_id(self, value): """ Setter used by __setattr__ for the id property. """ if value is None: - self._stream.id = 0 + self.ptr.id = 0 else: - self._stream.id = value + self.ptr.id = value property profile: """ @@ -208,8 +192,8 @@ cdef class Stream(object): :type: str """ def __get__(self): - if self._codec and lib.av_get_profile_name(self._codec, self._codec_context.profile): - return lib.av_get_profile_name(self._codec, self._codec_context.profile) + if self.codec_context: + return self.codec_context.profile else: return None @@ -219,7 +203,7 @@ cdef class Stream(object): :type: int """ - def __get__(self): return self._stream.index + def __get__(self): return self.ptr.index property time_base: """ @@ -229,13 +213,13 @@ cdef class Stream(object): """ def __get__(self): - return avrational_to_fraction(&self._stream.time_base) + return avrational_to_fraction(&self.ptr.time_base) cdef _set_time_base(self, value): """ Setter used by __setattr__ for the time_base property. """ - to_avrational(value, &self._stream.time_base) + to_avrational(value, &self.ptr.time_base) property average_rate: """ @@ -249,7 +233,7 @@ cdef class Stream(object): """ def __get__(self): - return avrational_to_fraction(&self._stream.avg_frame_rate) + return avrational_to_fraction(&self.ptr.avg_frame_rate) property base_rate: """ @@ -263,7 +247,7 @@ cdef class Stream(object): """ def __get__(self): - return avrational_to_fraction(&self._stream.r_frame_rate) + return avrational_to_fraction(&self.ptr.r_frame_rate) property guessed_rate: """The guessed frame rate of this stream. @@ -276,7 +260,7 @@ cdef class Stream(object): """ def __get__(self): # The two NULL arguments aren't used in FFmpeg >= 4.0 - cdef lib.AVRational val = lib.av_guess_frame_rate(NULL, self._stream, NULL) + cdef lib.AVRational val = lib.av_guess_frame_rate(NULL, self.ptr, NULL) return avrational_to_fraction(&val) property start_time: @@ -287,8 +271,8 @@ cdef class Stream(object): :type: :class:`int` or ``None`` """ def __get__(self): - if self._stream.start_time != lib.AV_NOPTS_VALUE: - return self._stream.start_time + if self.ptr.start_time != lib.AV_NOPTS_VALUE: + return self.ptr.start_time property duration: """ @@ -298,8 +282,8 @@ cdef class Stream(object): """ def __get__(self): - if self._stream.duration != lib.AV_NOPTS_VALUE: - return self._stream.duration + if self.ptr.duration != lib.AV_NOPTS_VALUE: + return self.ptr.duration property frames: """ @@ -309,7 +293,8 @@ cdef class Stream(object): :type: :class:`int` """ - def __get__(self): return self._stream.nb_frames + def __get__(self): + return self.ptr.nb_frames property language: """ @@ -329,4 +314,4 @@ cdef class Stream(object): :type: str """ - return lib.av_get_media_type_string(self._codec_context.codec_type) + return lib.av_get_media_type_string(self.ptr.codecpar.codec_type) diff --git a/av/video/stream.pyx b/av/video/stream.pyx index 70b8f3209..8694b63ba 100644 --- a/av/video/stream.pyx +++ b/av/video/stream.pyx @@ -6,7 +6,7 @@ cdef class VideoStream(Stream): self.index, self.name, self.format.name if self.format else None, - self._codec_context.width, - self._codec_context.height, + self.codec_context.width, + self.codec_context.height, id(self), ) diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd index 8c0a9685b..1e6111808 100644 --- a/include/libavcodec/avcodec.pxd +++ b/include/libavcodec/avcodec.pxd @@ -194,6 +194,7 @@ cdef extern from "libavcodec/avcodec.h" nogil: float rc_min_vbv_overflow_use AVRational framerate + AVRational pkt_timebase AVRational time_base int ticks_per_frame @@ -237,7 +238,6 @@ cdef extern from "libavcodec/avcodec.h" nogil: cdef void avcodec_free_context(AVCodecContext **ctx) cdef AVClass* avcodec_get_class() - cdef int avcodec_copy_context(AVCodecContext *dst, const AVCodecContext *src) cdef struct AVCodecDescriptor: AVCodecID id @@ -455,10 +455,18 @@ cdef extern from "libavcodec/avcodec.h" nogil: cdef struct AVCodecParameters: - pass + AVMediaType codec_type + AVCodecID codec_id + cdef int avcodec_parameters_copy( + AVCodecParameters *dst, + const AVCodecParameters *src + ) cdef int avcodec_parameters_from_context( AVCodecParameters *par, const AVCodecContext *codec, ) - + cdef int avcodec_parameters_to_context( + AVCodecContext *codec, + const AVCodecParameters *par + ) diff --git a/include/libavformat/avformat.pxd b/include/libavformat/avformat.pxd index 0a33cf9f6..ed3e503f5 100644 --- a/include/libavformat/avformat.pxd +++ b/include/libavformat/avformat.pxd @@ -33,7 +33,6 @@ cdef extern from "libavformat/avformat.h" nogil: int index int id - AVCodecContext *codec AVCodecParameters *codecpar AVRational time_base diff --git a/tests/common.py b/tests/common.py index 5d1bf74cc..a49b7bec2 100644 --- a/tests/common.py +++ b/tests/common.py @@ -82,6 +82,7 @@ def _inner(self, *args, **kwargs): return func(self, *args, **kwargs) finally: os.chdir(current_dir) + return _inner diff --git a/tests/test_codec_context.py b/tests/test_codec_context.py index a62c05c4e..7087804f7 100644 --- a/tests/test_codec_context.py +++ b/tests/test_codec_context.py @@ -180,7 +180,7 @@ def image_sequence_encode(self, codec_name): ctx.width = width ctx.height = height - ctx.time_base = video_stream.codec_context.time_base + ctx.time_base = video_stream.time_base ctx.pix_fmt = pix_fmt ctx.open() @@ -262,7 +262,7 @@ def video_encoding(self, codec_name, options={}, codec_tag=None): width = options.pop("width", 640) height = options.pop("height", 480) max_frames = options.pop("max_frames", 50) - time_base = options.pop("time_base", video_stream.codec_context.time_base) + time_base = options.pop("time_base", video_stream.time_base) ctx = codec.create() ctx.width = width diff --git a/tests/test_encode.py b/tests/test_encode.py index 018c6ac31..7c5d0353f 100644 --- a/tests/test_encode.py +++ b/tests/test_encode.py @@ -70,27 +70,59 @@ def assert_rgb_rotate(self, input_, is_dash=False): if is_dash: # FFmpeg 4.2 added parsing of the programme information and it is named "Title" if av.library_versions["libavformat"] >= (58, 28): - self.assertTrue(input_.metadata.get("Title") == "container", input_.metadata) + self.assertTrue( + input_.metadata.get("Title") == "container", input_.metadata + ) else: self.assertEqual(input_.metadata.get("title"), "container", input_.metadata) self.assertEqual(input_.metadata.get("key"), None) + stream = input_.streams[0] - self.assertIsInstance(stream, VideoStream) - self.assertEqual(stream.type, "video") - self.assertEqual(stream.name, "mpeg4") - self.assertEqual( - stream.average_rate, 24 - ) # Only because we constructed is precisely. - self.assertEqual(stream.rate, Fraction(24, 1)) + if is_dash: # The DASH format doesn't provide a duration for the stream # and so the container duration (micro seconds) is checked instead self.assertEqual(input_.duration, 2000000) + expected_average_rate = 24 + expected_duration = None + expected_frames = 0 + expected_id = 0 else: - self.assertEqual(stream.time_base * stream.duration, 2) + if av.library_versions["libavformat"] < (58, 76): + # FFmpeg < 4.4 + expected_average_rate = Fraction(1152, 47) + expected_duration = 24064 + else: + # FFmpeg >= 4.4 + expected_average_rate = 24 + expected_duration = 24576 + expected_frames = 48 + expected_id = 1 + + # actual stream properties + self.assertIsInstance(stream, VideoStream) + self.assertEqual(stream.average_rate, expected_average_rate) + self.assertEqual(stream.base_rate, 24) + self.assertEqual(stream.duration, expected_duration) + self.assertEqual(stream.guessed_rate, 24) + self.assertEqual(stream.frames, expected_frames) + self.assertEqual(stream.id, expected_id) + self.assertEqual(stream.index, 0) + self.assertEqual(stream.profile, "Simple Profile") + self.assertEqual(stream.start_time, 0) + self.assertEqual(stream.time_base, Fraction(1, 12288)) + self.assertEqual(stream.type, "video") + + # codec properties + self.assertEqual(stream.name, "mpeg4") + self.assertEqual(stream.long_name, "MPEG-4 part 2") + + # codec context properties self.assertEqual(stream.format.name, "yuv420p") self.assertEqual(stream.format.width, WIDTH) self.assertEqual(stream.format.height, HEIGHT) + self.assertEqual(stream.rate, None) + self.assertEqual(stream.ticks_per_frame, 1) class TestBasicVideoEncoding(TestCase):
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor