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):
openSUSE Build Service is sponsored by