forked from pool/python-av
Accepting request 994510 from home:badshah400:branches:home:dimstar:ffmpeg5
Add python-av-ffmpeg5-compatibility.patch to drop references to symbols in ffmpeg4 and dropped from ffmpeg5 to allow building against ffmpeg5; patch taken from upstream git commit. OBS-URL: https://build.opensuse.org/request/show/994510 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-av?expand=0&rev=21
This commit is contained in:
746
python-av-ffmpeg5-compatibility.patch
Normal file
746
python-av-ffmpeg5-compatibility.patch
Normal file
@@ -0,0 +1,746 @@
|
||||
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):
|
||||
@@ -1,3 +1,10 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon Aug 8 21:41:40 UTC 2022 - Atri Bhattacharya <badshah400@gmail.com>
|
||||
|
||||
- Add python-av-ffmpeg5-compatibility.patch to drop references to
|
||||
symbols in ffmpeg4 and dropped from ffmpeg5 to allow building
|
||||
against ffmpeg5; patch taken from upstream git commit.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon May 2 21:45:05 UTC 2022 - Ferdinand Thiessen <rpm@fthiessen.de>
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ Summary: Python bindings for FFmpeg's libraries
|
||||
License: BSD-3-Clause
|
||||
URL: https://github.com/PyAV-Org/PyAV
|
||||
Source: https://files.pythonhosted.org/packages/source/a/av/av-%{version}.tar.gz
|
||||
# PATCH-FIX-UPSTREAM gh#PyAV-Org/PyAV#817 badshah400@gmail.com -- Add ffmpeg5 support, patch taken from upstream git
|
||||
Patch0: https://github.com/PyAV-Org/PyAV/commit/18704658487ea25e5202ac18438d836dfe65b9d0.patch#/python-av-ffmpeg5-compatibility.patch
|
||||
BuildRequires: %{python_module Cython}
|
||||
BuildRequires: %{python_module devel}
|
||||
BuildRequires: %{python_module numpy}
|
||||
@@ -46,7 +48,7 @@ Requires(postun):update-alternatives
|
||||
Pythonic bindings for FFmpeg's libraries.
|
||||
|
||||
%prep
|
||||
%setup -q -n av-%{version}
|
||||
%autosetup -p1 -n av-%{version}
|
||||
|
||||
# doctests and timeout require network to setup tests
|
||||
rm tests/test_doctests.py tests/test_timeout.py
|
||||
|
||||
Reference in New Issue
Block a user