Skip to content

Commit

Permalink
Deprecate async variants (#307)
Browse files Browse the repository at this point in the history
  • Loading branch information
mthrok authored Jan 8, 2025
1 parent 83a6c17 commit 521c88b
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 58 deletions.
39 changes: 27 additions & 12 deletions src/spdl/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,43 @@ def __dir__():
return __all__


_deprecated = {
"streaming_demux_audio",
"streaming_demux_video",
"async_streaming_demux_audio",
"async_streaming_demux_video",
"streaming_load_audio",
"streaming_load_video",
"async_streaming_load_audio",
"async_streaming_load_video",
_deprecated_core = {
"async_demux_audio",
"async_demux_video",
"async_demux_image",
"async_decode_packets",
"async_decode_packets_nvdec",
"async_streaming_decode_packets",
"async_decode_image_nvjpeg",
"async_convert_array",
"async_convert_frames",
"async_transfer_buffer",
"async_transfer_buffer_cpu",
"async_encode_image",
"run_async",
}
_deprecated_composite = {
"async_load_audio",
"async_load_video",
"async_load_image",
"async_load_image_batch",
"async_load_image_batch_nvdec",
"async_load_image_batch_nvjpeg",
"async_sample_decode_video",
}


def __getattr__(name: str) -> Any:
if name in _deprecated:
if name in _deprecated_core or name in _deprecated_composite:
import warnings

warnings.warn(
f"`{name}` has been deprecated. Please use `spdl.io.Demuxer`.",
f"`{name}` has been deprecated. Please use synchronous variant.",
category=FutureWarning,
stacklevel=2,
)
if "demux" in name:

if name in _deprecated_core:
return getattr(_core, name)
return getattr(_composite, name)

Expand Down
98 changes: 88 additions & 10 deletions src/spdl/io/_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,7 @@
"load_audio",
"load_video",
"load_image",
"async_load_audio",
"async_load_video",
"async_load_image",
"async_load_image_batch",
"async_load_image_batch_nvdec",
"load_image_batch_nvjpeg",
"async_load_image_batch_nvjpeg",
"async_sample_decode_video",
"sample_decode_video",
]

Expand Down Expand Up @@ -967,10 +960,95 @@ def sample_decode_video(
decode_config: DecodeConfig | None = None,
filter_desc: str | None = _FILTER_DESC_DEFAULT,
) -> list[ImagePackets]:
"""Synchronous version of :py:func:`~async_sample_decode_video`.
"""Decode specified frames from the packets.
This function decodes the input video packets and returns the frames
specified by ``indices``. Internally, it splits the packets into
smaller set of packets and decode the minimum number of frames to
retrieve the specified frames.
.. mermaid::
block-beta
columns 15
A["Input Packets"]:15
space:15
block:B1:3
columns 3
P1["1"] P2["2"] P3["3"]
end
block:B2:3
columns 3
P4["4"] P5["5"] P6["6"]
end
block:B3:3
columns 3
P7["7"] P8["8"] P9["9"]
end
block:B4:3
columns 3
P10["10"] P11["11"] P12["12"]
end
block:B5:3
columns 3
P13["13"] P14["14"] P15["15"]
end
space:15
block:d1:3
columns 3
F1["Frame 1"] space:2
end
space:3
block:d2:3
columns 3
F7["Frame 7"] F8["Frame 8"] space
end
space:3
block:d3:3
columns 3
F13["Frame 13"] F14["Frame 14"] F15["Frame 15"]
end
space:15
space:6
block:out:3
columns 3
O1["Frame 1"] O8["Frame 8"] O15["Frame 15"]
end
space:6
A -- "Split 1" --> B1
A -- "Split 2" --> B2
A -- "Split 3" --> B3
A -- "Split 4" --> B4
A -- "Split 5" --> B5
This function performs the same operation as :py:func:`~async_sample_decode_video`,
but the operations are performed sequentially.
B1 -- "Decode 1" --> d1
B3 -- "Decode 3" --> d2
B5 -- "Decode 5" --> d3
F1 --> O1
F8 --> O8
F15 --> O15
Args:
packets: The input video packets.
indices: The list of frame indices.
decode_config:
*Optional:* Decode config.
See :py:func:`~spdl.io.decode_config`.
filter_desc: *Optional:* Filter description.
See :py:func:`~spdl.io.decode_packets` for detail.
strict: *Optional:* If True, raise an error
if any of the frames failed to decode.
Returns:
Decoded frames.
"""
if not indices:
raise ValueError("Frame indices must be non-empty.")
Expand Down
25 changes: 5 additions & 20 deletions src/spdl/io/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,33 +55,19 @@
"demux_audio",
"demux_video",
"demux_image",
"async_demux_audio",
"async_demux_video",
"async_demux_image",
# DECODING
"decode_packets",
"async_decode_packets",
"decode_packets_nvdec",
"async_decode_packets_nvdec",
"streaming_decode_packets",
"async_streaming_decode_packets",
"decode_image_nvjpeg",
"async_decode_image_nvjpeg",
# FRAME CONVERSION
"convert_array",
"convert_frames",
"async_convert_array",
"async_convert_frames",
# DATA TRANSFER
"transfer_buffer",
"async_transfer_buffer",
"transfer_buffer_cpu",
"async_transfer_buffer_cpu",
# ENCODING
"encode_image",
"async_encode_image",
# MISC
"run_async",
]

_LG = logging.getLogger(__name__)
Expand Down Expand Up @@ -699,7 +685,7 @@ def encode_image(path: str, data: Array, pix_fmt: str = "rgb24", **kwargs):
>>> import spdl.io
>>>
>>> data = np.random.randint(255, size=(32, 16, 3), dtype=np.uint8)
>>> coro = spdl.io.async_encode_image(
>>> img = spdl.io.encode_image(
... "foo.png",
... data,
... pix_fmt="rgb24",
Expand All @@ -709,7 +695,6 @@ def encode_image(path: str, data: Array, pix_fmt: str = "rgb24", **kwargs):
... scale_algo="neighbor",
... ),
... )
>>> asyncio.run(coro)
>>>
Example - Save CUDA tensor as image
Expand All @@ -718,15 +703,15 @@ def encode_image(path: str, data: Array, pix_fmt: str = "rgb24", **kwargs):
>>>
>>> data = torch.randint(255, size=(32, 16, 3), dtype=torch.uint8, device="cuda")
>>>
>>> async def encode(data):
>>> buffer = await spdl.io.async_transfer_buffer_cpu(data)
>>> return await spdl.io.async_encode_image(
>>> def encode(data):
... buffer = spdl.io.transfer_buffer_cpu(data)
... return spdl.io.encode_image(
... "foo.png",
... buffer,
... pix_fmt="rgb24",
... )
...
>>> asyncio.run(encode(data))
>>> encode(data)
>>>
"""
return _libspdl.encode_image(path, data, pix_fmt=pix_fmt, **kwargs)
Expand Down
21 changes: 5 additions & 16 deletions src/spdl/io/_type_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ class Packets:
Decode functions receive Packets objects and generate audio samples and
visual frames.
The ``Packets`` objects are exposed to public API to allow composing
demux/decoding functions in ways that they are executed concurrently.
For example, when decoding multiple clips from a single audio and video file,
by emitting ``Packets`` objects in between, decoding can be started while the
demuxer is demuxing the subsequent windows.
The following code will kick-off the decoding job as soon as the streaming
demux function yields a ``VideoPackets`` object.
.. admonition:: Example
Expand All @@ -57,14 +48,12 @@ class Packets:
... (13, 15),
... ]
>>>
>>> tasks = []
>>> async for packets in spdl.io.async_streaming_demux_video(src, windows):
>>> # Kick off decoding job while demux function is demuxing the next window
>>> task = asyncio.create_task(async_decode_packets(packets))
>>> task.append(task)
>>> demuxer = spdl.io.Demuxer(src)
>>> for window in windows:
... packets = demuxer.demux_video(window)
... frames = decode_packets(packets)
...
>>>
>>> # Wait for all the decoding to be complete
>>> asyncio.wait(tasks)
.. important::
Expand Down

0 comments on commit 521c88b

Please sign in to comment.