Skip to content

Commit 4c4a54c

Browse files
authored
Add read/write access to PacketSideData
1 parent b22003a commit 4c4a54c

File tree

4 files changed

+85
-4
lines changed

4 files changed

+85
-4
lines changed

av/packet.pxd

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ from av.bytesource cimport ByteSource
77
from av.stream cimport Stream
88

99

10-
cdef class PacketSideData:
10+
cdef class PacketSideData(Buffer):
1111
cdef uint8_t *data
1212
cdef size_t size
1313
cdef lib.AVPacketSideDataType dtype
1414

15+
cdef size_t _buffer_size(self)
16+
cdef void* _buffer_ptr(self)
17+
cdef bint _buffer_writable(self)
18+
1519
cdef class Packet(Buffer):
1620
cdef lib.AVPacket* ptr
1721
cdef Stream _stream

av/packet.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import cython
44
from cython.cimports import libav as lib
5+
from cython.cimports.av.buffer import Buffer
56
from cython.cimports.av.bytesource import bytesource
67
from cython.cimports.av.error import err_check
78
from cython.cimports.av.opaque import opaque_container
@@ -64,7 +65,7 @@ def packet_sidedata_type_from_literal(dtype: PktSideDataT) -> lib.AVPacketSideDa
6465

6566

6667
@cython.cclass
67-
class PacketSideData:
68+
class PacketSideData(Buffer):
6869
@staticmethod
6970
def from_packet(packet: Packet, data_type: PktSideDataT) -> PacketSideData:
7071
"""create new PacketSideData by copying an existing packet's side data
@@ -152,6 +153,19 @@ def data_size(self) -> int:
152153
"""
153154
return self.size
154155

156+
# Buffer protocol implementation
157+
@cython.cfunc
158+
def _buffer_size(self) -> cython.size_t:
159+
return self.size
160+
161+
@cython.cfunc
162+
def _buffer_ptr(self) -> cython.p_void:
163+
return self.data
164+
165+
@cython.cfunc
166+
def _buffer_writable(self) -> cython.bint:
167+
return True
168+
155169
def __bool__(self) -> bool:
156170
"""
157171
True if this object holds side data.

av/packet.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ PktSideDataT = Literal[
5050
"rtcp_sr",
5151
]
5252

53-
class PacketSideData:
53+
class PacketSideData(Buffer):
5454
@staticmethod
5555
def from_packet(packet: Packet, dtype: PktSideDataT) -> PacketSideData: ...
5656
def to_packet(self, packet: Packet, move: bool = False): ...

tests/test_packet.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import struct
12
from typing import get_args
23
from unittest import SkipTest
34

45
import av
56

6-
from .common import fate_suite
7+
from .common import fate_suite, sandboxed
78

89

910
class TestProperties:
@@ -100,3 +101,65 @@ def test_palette(self) -> None:
100101

101102
nxt.set_sidedata(sdata, move=True)
102103
assert not bool(sdata)
104+
105+
def test_buffer_protocol(self) -> None:
106+
with av.open(fate_suite("h264/extradata-reload-multi-stsd.mov")) as container:
107+
for pkt in container.demux():
108+
if pkt.has_sidedata("new_extradata"):
109+
sdata = pkt.get_sidedata("new_extradata")
110+
111+
raw = bytes(sdata)
112+
assert len(raw) == sdata.data_size > 0
113+
assert sdata.buffer_size == sdata.data_size
114+
assert sdata.buffer_ptr != 0
115+
assert bytes(memoryview(sdata)) == raw
116+
117+
# Modify and verify changes stick
118+
sdata.update(b"\xde\xad\xbe\xef" + raw[4:])
119+
assert bytes(sdata)[:4] == b"\xde\xad\xbe\xef"
120+
121+
pkt.set_sidedata(sdata)
122+
assert (
123+
bytes(pkt.get_sidedata("new_extradata"))[:4]
124+
== b"\xde\xad\xbe\xef"
125+
)
126+
return
127+
128+
raise AssertionError("No packet with new_extradata side data found")
129+
130+
def test_skip_samples_remux(self) -> None:
131+
# Source file has skip_end=706 on last packet. Setting to 0 should
132+
# result in 706 more decoded samples. And the file duration reported by
133+
# the container should also increase.
134+
output_path = sandboxed("skip_samples_modified.mkv")
135+
136+
with av.open(fate_suite("mkv/codec_delay_opus.mkv")) as c:
137+
original_samples = sum(f.samples for f in c.decode(c.streams.audio[0]))
138+
139+
with av.open(fate_suite("mkv/codec_delay_opus.mkv")) as inp:
140+
original_duration = inp.duration
141+
audio_stream = inp.streams.audio[0]
142+
with av.open(output_path, "w") as out:
143+
out_stream = out.add_stream_from_template(audio_stream)
144+
for pkt in inp.demux(audio_stream):
145+
if pkt.dts is None:
146+
continue
147+
if pkt.has_sidedata("skip_samples"):
148+
sdata = pkt.get_sidedata("skip_samples")
149+
raw = bytes(sdata)
150+
skip_end = struct.unpack("<I", raw[4:8])[0]
151+
assert skip_end == 706
152+
sdata.update(raw[:4] + struct.pack("<I", 0) + raw[8:])
153+
pkt.set_sidedata(sdata)
154+
pkt.stream = out_stream
155+
out.mux(pkt)
156+
157+
with av.open(output_path) as c:
158+
modified_samples = sum(f.samples for f in c.decode(c.streams.audio[0]))
159+
modified_duration = c.duration
160+
161+
assert modified_samples - original_samples == 706
162+
163+
assert original_duration is not None
164+
assert modified_duration is not None
165+
assert modified_duration > original_duration

0 commit comments

Comments
 (0)