Skip to content

Commit 8105052

Browse files
Add packet side-data handling mechanism
Adds ability to extract (copy) a side data from one packet and apply it to another packet --------- Co-authored-by: WyattBlue <wyattblue@auto-editor.com>
1 parent 0d988f0 commit 8105052

File tree

5 files changed

+349
-1
lines changed

5 files changed

+349
-1
lines changed

av/packet.pxd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1+
from cython.cimports.libc.stdint import uint8_t
2+
13
cimport libav as lib
24

35
from av.buffer cimport Buffer
46
from av.bytesource cimport ByteSource
57
from av.stream cimport Stream
68

79

10+
cdef class PacketSideData:
11+
cdef uint8_t *data
12+
cdef size_t size
13+
cdef lib.AVPacketSideDataType dtype
14+
815
cdef class Packet(Buffer):
916
cdef lib.AVPacket* ptr
1017
cdef Stream _stream

av/packet.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,183 @@
1+
from typing import Iterator, Literal, get_args
2+
13
import cython
24
from cython.cimports import libav as lib
35
from cython.cimports.av.bytesource import bytesource
46
from cython.cimports.av.error import err_check
57
from cython.cimports.av.opaque import opaque_container
68
from cython.cimports.av.utils import avrational_to_fraction, to_avrational
9+
from cython.cimports.libc.string import memcpy
10+
11+
# Check https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/packet.h#L41
12+
# for new additions in the future ffmpeg releases
13+
# Note: the order must follow that of the AVPacketSideDataType enum def
14+
PktSideDataT = Literal[
15+
"palette",
16+
"new_extradata",
17+
"param_change",
18+
"h263_mb_info",
19+
"replay_gain",
20+
"display_matrix",
21+
"stereo_3d",
22+
"audio_service_type",
23+
"quality_stats",
24+
"fallback_track",
25+
"cpb_properties",
26+
"skip_samples",
27+
"jp_dual_mono",
28+
"strings_metadata",
29+
"subtitle_position",
30+
"matroska_block_additional",
31+
"webvtt_identifier",
32+
"webvtt_settings",
33+
"metadata_update",
34+
"mpegts_stream_id",
35+
"mastering_display_metadata",
36+
"spherical",
37+
"content_light_level",
38+
"a53_cc",
39+
"encryption_init_info",
40+
"encryption_info",
41+
"afd",
42+
"prft",
43+
"icc_profile",
44+
"dovi_conf",
45+
"s12m_timecode",
46+
"dynamic_hdr10_plus",
47+
"iamf_mix_gain_param",
48+
"iamf_info_param",
49+
"iamf_recon_gain_info_param",
50+
"ambient_viewing_environment",
51+
"frame_cropping",
52+
"lcevc",
53+
"3d_reference_displays",
54+
"rtcp_sr",
55+
]
56+
57+
58+
def packet_sidedata_type_to_literal(dtype: lib.AVPacketSideDataType) -> PktSideDataT:
59+
return get_args(PktSideDataT)[cython.cast(int, dtype)]
60+
61+
62+
def packet_sidedata_type_from_literal(dtype: PktSideDataT) -> lib.AVPacketSideDataType:
63+
return get_args(PktSideDataT).index(dtype)
64+
65+
66+
@cython.cclass
67+
class PacketSideData:
68+
@staticmethod
69+
def from_packet(packet: Packet, data_type: PktSideDataT) -> PacketSideData:
70+
"""create new PacketSideData by copying an existing packet's side data
71+
72+
:param packet: Source packet
73+
:type packet: :class:`~av.packet.Packet`
74+
:param data_type: side data type
75+
:return: newly created copy of the side data if the side data of the
76+
requested type is found in the packet, else an empty object
77+
:rtype: :class:`~av.packet.PacketSideData`
78+
"""
79+
80+
dtype = packet_sidedata_type_from_literal(data_type)
81+
return _packet_sidedata_from_packet(packet.ptr, dtype)
82+
83+
def __cinit__(self, dtype: lib.AVPacketSideDataType, size: cython.size_t):
84+
self.dtype = dtype
85+
with cython.nogil:
86+
if size:
87+
self.data = cython.cast(cython.p_uchar, lib.av_malloc(size))
88+
if self.data == cython.NULL:
89+
raise MemoryError("Failed to allocate memory")
90+
else:
91+
self.data = cython.NULL
92+
self.size = size
93+
94+
def __dealloc__(self):
95+
with cython.nogil:
96+
lib.av_freep(cython.address(self.data))
97+
98+
def to_packet(self, packet: Packet, move: cython.bint = False):
99+
"""copy or move side data to the specified packet
100+
101+
:param packet: Target packet
102+
:type packet: :class:`~av.packet.Packet`
103+
:param move: True to move the data from this object to the packet,
104+
defaults to False.
105+
:type move: bool
106+
"""
107+
if self.size == 0:
108+
# nothing to add, should clear existing side_data in packet?
109+
return
110+
111+
data = self.data
112+
113+
with cython.nogil:
114+
if not move:
115+
data = cython.cast(cython.p_uchar, lib.av_malloc(self.size))
116+
if data == cython.NULL:
117+
raise MemoryError("Failed to allocate memory")
118+
memcpy(data, self.data, self.size)
119+
120+
res = lib.av_packet_add_side_data(packet.ptr, self.dtype, data, self.size)
121+
err_check(res)
122+
123+
if move:
124+
self.data = cython.NULL
125+
self.size = 0
126+
127+
@property
128+
def data_type(self) -> str:
129+
"""
130+
The type of this packet side data.
131+
132+
:type: str
133+
"""
134+
return packet_sidedata_type_to_literal(self.dtype)
135+
136+
@property
137+
def data_desc(self) -> str:
138+
"""
139+
The description of this packet side data type.
140+
141+
:type: str
142+
"""
143+
144+
return lib.av_packet_side_data_name(self.dtype)
145+
146+
@property
147+
def data_size(self) -> int:
148+
"""
149+
The size in bytes of this packet side data.
150+
151+
:type: int
152+
"""
153+
return self.size
154+
155+
def __bool__(self) -> bool:
156+
"""
157+
True if this object holds side data.
158+
159+
:type: bool
160+
"""
161+
return self.data != cython.NULL
162+
163+
164+
@cython.cfunc
165+
def _packet_sidedata_from_packet(
166+
packet: cython.pointer[lib.AVPacket], dtype: lib.AVPacketSideDataType
167+
) -> PacketSideData:
168+
with cython.nogil:
169+
c_ptr = lib.av_packet_side_data_get(
170+
packet.side_data, packet.side_data_elems, dtype
171+
)
172+
found: cython.bint = c_ptr != cython.NULL
173+
174+
sdata = PacketSideData(dtype, c_ptr.size if found else 0)
175+
176+
with cython.nogil:
177+
if found:
178+
memcpy(sdata.data, c_ptr.data, c_ptr.size)
179+
180+
return sdata
7181

8182

9183
@cython.cclass
@@ -235,3 +409,48 @@ def opaque(self, v):
235409
if v is None:
236410
return
237411
self.ptr.opaque_ref = opaque_container.add(v)
412+
413+
def has_sidedata(self, dtype: str) -> bool:
414+
"""True if this packet has the specified side data
415+
416+
:param dtype: side data type
417+
:type dtype: str
418+
"""
419+
420+
dtype2 = packet_sidedata_type_from_literal(dtype)
421+
return (
422+
lib.av_packet_side_data_get(
423+
self.ptr.side_data, self.ptr.side_data_elems, dtype2
424+
)
425+
!= cython.NULL
426+
)
427+
428+
def get_sidedata(self, dtype: str) -> PacketSideData:
429+
"""get a copy of the side data
430+
431+
:param dtype: side data type (:method:`~av.packet.PacketSideData.sidedata_types` for the full list of options)
432+
:type dtype: str
433+
:return: newly created copy of the side data if the side data of the
434+
requested type is found in the packet, else an empty object
435+
:rtype: :class:`~av.packet.PacketSideData`
436+
"""
437+
return PacketSideData.from_packet(self, dtype)
438+
439+
def set_sidedata(self, sidedata: PacketSideData, move: cython.bint = False):
440+
"""copy or move side data to this packet
441+
442+
:param sidedata: Source packet side data
443+
:type sidedata: :class:`~av.packet.PacketSideData`
444+
:param move: If True, move the data from `sidedata` object, defaults to False
445+
:type move: bool
446+
"""
447+
sidedata.to_packet(self, move)
448+
449+
def iter_sidedata(self) -> Iterator[PacketSideData]:
450+
"""iterate over side data of this packet.
451+
452+
:yield: :class:`~av.packet.PacketSideData` object
453+
"""
454+
455+
for i in range(self.ptr.side_data_elems):
456+
yield _packet_sidedata_from_packet(self.ptr, self.ptr.side_data[i].type)

av/packet.pyi

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,70 @@
11
from fractions import Fraction
2+
from typing import Iterator, Literal
23

34
from av.subtitles.subtitle import SubtitleSet
45

56
from .buffer import Buffer
67
from .stream import Stream
78

9+
# Sync with definition in 'packet.py'
10+
PktSideDataT = Literal[
11+
"palette",
12+
"new_extradata",
13+
"param_change",
14+
"h263_mb_info",
15+
"replay_gain",
16+
"display_matrix",
17+
"stereo_3d",
18+
"audio_service_type",
19+
"quality_stats",
20+
"fallback_track",
21+
"cpb_properties",
22+
"skip_samples",
23+
"jp_dual_mono",
24+
"strings_metadata",
25+
"subtitle_position",
26+
"matroska_block_additional",
27+
"webvtt_identifier",
28+
"webvtt_settings",
29+
"metadata_update",
30+
"mpegts_stream_id",
31+
"mastering_display_metadata",
32+
"spherical",
33+
"content_light_level",
34+
"a53_cc",
35+
"encryption_init_info",
36+
"encryption_info",
37+
"afd",
38+
"prft",
39+
"icc_profile",
40+
"dovi_conf",
41+
"s12m_timecode",
42+
"dynamic_hdr10_plus",
43+
"iamf_mix_gain_param",
44+
"iamf_info_param",
45+
"iamf_recon_gain_info_param",
46+
"ambient_viewing_environment",
47+
"frame_cropping",
48+
"lcevc",
49+
"3d_reference_displays",
50+
"rtcp_sr",
51+
]
52+
53+
class PacketSideData:
54+
@staticmethod
55+
def from_packet(packet: Packet, dtype: PktSideDataT) -> PacketSideData: ...
56+
def to_packet(self, packet: Packet, move: bool = False): ...
57+
@property
58+
def data_type(self) -> str: ...
59+
@property
60+
def data_desc(self) -> str: ...
61+
@property
62+
def data_size(self) -> int: ...
63+
def __bool__(self) -> bool: ...
64+
65+
def packet_sidedata_type_to_literal(dtype: int) -> PktSideDataT: ...
66+
def packet_sidedata_type_from_literal(dtype: PktSideDataT) -> int: ...
67+
868
class Packet(Buffer):
969
stream: Stream
1070
stream_index: int
@@ -23,3 +83,7 @@ class Packet(Buffer):
2383

2484
def __init__(self, input: int | bytes | None = None) -> None: ...
2585
def decode(self) -> list[SubtitleSet]: ...
86+
def has_sidedata(self, dtype: PktSideDataT) -> bool: ...
87+
def get_sidedata(self, dtype: PktSideDataT) -> PacketSideData: ...
88+
def set_sidedata(self, sidedata: PacketSideData, move: bool = False) -> None: ...
89+
def iter_sidedata(self) -> Iterator[PacketSideData]: ...

include/libavcodec/avcodec.pxd

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from libc.stdint cimport int8_t, int64_t, uint16_t, uint32_t
1+
from libc.stdint cimport int8_t, int64_t, uint16_t, uint32_t, uint8_t
22

33
cdef extern from "libavcodec/codec.h":
44
struct AVCodecTag:
@@ -17,6 +17,17 @@ cdef extern from "libavcodec/packet.h" nogil:
1717
int free_opaque
1818
)
1919

20+
const AVPacketSideData *av_packet_side_data_get(const AVPacketSideData *sd,
21+
int nb_sd,
22+
AVPacketSideDataType type)
23+
24+
uint8_t* av_packet_get_side_data(const AVPacket *pkt, AVPacketSideDataType type,
25+
size_t *size)
26+
27+
int av_packet_add_side_data(AVPacket *pkt, AVPacketSideDataType type,
28+
uint8_t *data, size_t size)
29+
30+
const char *av_packet_side_data_name(AVPacketSideDataType type)
2031

2132
cdef extern from "libavutil/channel_layout.h":
2233
ctypedef enum AVChannelOrder:
@@ -469,6 +480,8 @@ cdef extern from "libavcodec/avcodec.h" nogil:
469480
int size
470481
int stream_index
471482
int flags
483+
AVPacketSideData *side_data
484+
int side_data_elems
472485
int duration
473486
int64_t pos
474487
void *opaque

0 commit comments

Comments
 (0)