From 7a5c2d1aabfb75ead1dd3d73dc990a6a84185dd6 Mon Sep 17 00:00:00 2001 From: Roland van Laar Date: Tue, 12 Mar 2024 08:29:27 +0100 Subject: [PATCH] Expose bits_per_coded_sample on VideoCodecContext --------- Co-authored-by: JoeUgly <41972063+JoeUgly@users.noreply.github.com> Co-authored-by: WyattBlue --- av/video/codeccontext.pyx | 20 ++++++++++++++++++++ include/libavcodec/avcodec.pxd | 2 ++ tests/test_codec_context.py | 25 +++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/av/video/codeccontext.pyx b/av/video/codeccontext.pyx index 2e101d935..8f6762fcf 100644 --- a/av/video/codeccontext.pyx +++ b/av/video/codeccontext.pyx @@ -97,6 +97,26 @@ cdef class VideoCodecContext(CodecContext): self.ptr.height = value self._build_format() + @property + def bits_per_coded_sample(self): + """ + The number of bits per sample in the codedwords, basically the bitrate per sample. + It is mandatory for this to be set for some formats to decode them. + + Wraps :ffmpeg:`AVCodecContext::bits_per_coded_sample` + + :type: int + """ + return self.ptr.bits_per_coded_sample + + @bits_per_coded_sample.setter + def bits_per_coded_sample(self, int value): + if self.is_encoder: + raise ValueError("Not supported for encoders") + + self.ptr.bits_per_coded_sample = value + self._build_format() + @property def pix_fmt(self): """ diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd index b58047014..49758be4c 100644 --- a/include/libavcodec/avcodec.pxd +++ b/include/libavcodec/avcodec.pxd @@ -171,6 +171,8 @@ cdef extern from "libavcodec/avcodec.h" nogil: int bit_rate_tolerance int mb_decision + int bits_per_coded_sample + int global_quality int compression_level diff --git a/tests/test_codec_context.py b/tests/test_codec_context.py index 0be4ed621..2f9a34aa7 100644 --- a/tests/test_codec_context.py +++ b/tests/test_codec_context.py @@ -146,6 +146,31 @@ def test_encoder_pix_fmt(self): self.assertEqual(str(cm.exception), "not a pixel format: '__unknown_pix_fmt'") self.assertEqual(ctx.pix_fmt, "yuv420p") + def test_bits_per_coded_sample(self): + with av.open(fate_suite("qtrle/aletrek-rle.mov")) as container: + stream = container.streams.video[0] + stream.bits_per_coded_sample = 32 + + for packet in container.demux(stream): + for frame in packet.decode(): + pass + self.assertEqual(packet.stream.bits_per_coded_sample, 32) + + with av.open(fate_suite("qtrle/aletrek-rle.mov")) as container: + stream = container.streams.video[0] + stream.bits_per_coded_sample = 31 + + with self.assertRaises(av.error.InvalidDataError): + for packet in container.demux(stream): + for frame in packet.decode(): + pass + + with av.open(self.sandboxed("output.mov"), "w") as output: + stream = output.add_stream("qtrle") + + with self.assertRaises(ValueError): + stream.codec_context.bits_per_coded_sample = 32 + def test_parse(self): # This one parses into a single packet. self._assert_parse("mpeg4", fate_suite("h264/interlaced_crop.mp4"))