Kano001 commited on
Commit
6b6d66f
1 Parent(s): 56d4caf

Upload 29 files

Browse files
Files changed (30) hide show
  1. .gitattributes +3 -0
  2. MLPY/Lib/site-packages/torio/__init__.py +8 -0
  3. MLPY/Lib/site-packages/torio/__pycache__/__init__.cpython-39.pyc +0 -0
  4. MLPY/Lib/site-packages/torio/_extension/__init__.py +13 -0
  5. MLPY/Lib/site-packages/torio/_extension/__pycache__/__init__.cpython-39.pyc +0 -0
  6. MLPY/Lib/site-packages/torio/_extension/__pycache__/utils.cpython-39.pyc +0 -0
  7. MLPY/Lib/site-packages/torio/_extension/utils.py +147 -0
  8. MLPY/Lib/site-packages/torio/io/__init__.py +9 -0
  9. MLPY/Lib/site-packages/torio/io/__pycache__/__init__.cpython-39.pyc +0 -0
  10. MLPY/Lib/site-packages/torio/io/__pycache__/_streaming_media_decoder.cpython-39.pyc +0 -0
  11. MLPY/Lib/site-packages/torio/io/__pycache__/_streaming_media_encoder.cpython-39.pyc +0 -0
  12. MLPY/Lib/site-packages/torio/io/_streaming_media_decoder.py +978 -0
  13. MLPY/Lib/site-packages/torio/io/_streaming_media_encoder.py +502 -0
  14. MLPY/Lib/site-packages/torio/lib/__init__.py +0 -0
  15. MLPY/Lib/site-packages/torio/lib/__pycache__/__init__.cpython-39.pyc +0 -0
  16. MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg4.pyd +3 -0
  17. MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg5.pyd +3 -0
  18. MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg6.pyd +3 -0
  19. MLPY/Lib/site-packages/torio/lib/libtorio_ffmpeg4.pyd +0 -0
  20. MLPY/Lib/site-packages/torio/lib/libtorio_ffmpeg5.pyd +0 -0
  21. MLPY/Lib/site-packages/torio/lib/libtorio_ffmpeg6.pyd +0 -0
  22. MLPY/Lib/site-packages/torio/utils/__init__.py +4 -0
  23. MLPY/Lib/site-packages/torio/utils/__pycache__/__init__.cpython-39.pyc +0 -0
  24. MLPY/Lib/site-packages/torio/utils/__pycache__/ffmpeg_utils.cpython-39.pyc +0 -0
  25. MLPY/Lib/site-packages/torio/utils/ffmpeg_utils.py +247 -0
  26. MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/INSTALLER +1 -0
  27. MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/LICENSE +279 -0
  28. MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/METADATA +67 -0
  29. MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/RECORD +7 -0
  30. MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/WHEEL +4 -0
.gitattributes CHANGED
@@ -102,3 +102,6 @@ MLPY/Lib/site-packages/torch/lib/torch_cpu.lib filter=lfs diff=lfs merge=lfs -te
102
  MLPY/Lib/site-packages/torch/lib/torch_python.dll filter=lfs diff=lfs merge=lfs -text
103
  MLPY/Lib/site-packages/torch/lib/XNNPACK.lib filter=lfs diff=lfs merge=lfs -text
104
  MLPY/Lib/site-packages/torchaudio/lib/libtorchaudio.pyd filter=lfs diff=lfs merge=lfs -text
 
 
 
 
102
  MLPY/Lib/site-packages/torch/lib/torch_python.dll filter=lfs diff=lfs merge=lfs -text
103
  MLPY/Lib/site-packages/torch/lib/XNNPACK.lib filter=lfs diff=lfs merge=lfs -text
104
  MLPY/Lib/site-packages/torchaudio/lib/libtorchaudio.pyd filter=lfs diff=lfs merge=lfs -text
105
+ MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg4.pyd filter=lfs diff=lfs merge=lfs -text
106
+ MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg5.pyd filter=lfs diff=lfs merge=lfs -text
107
+ MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg6.pyd filter=lfs diff=lfs merge=lfs -text
MLPY/Lib/site-packages/torio/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from . import _extension # noqa # usort: skip
2
+ from . import io, utils
3
+
4
+
5
+ __all__ = [
6
+ "io",
7
+ "utils",
8
+ ]
MLPY/Lib/site-packages/torio/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (251 Bytes). View file
 
MLPY/Lib/site-packages/torio/_extension/__init__.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .utils import _init_ffmpeg, _LazyImporter
2
+
3
+
4
+ _FFMPEG_EXT = None
5
+
6
+
7
+ def lazy_import_ffmpeg_ext():
8
+ """Load FFmpeg integration based on availability in lazy manner"""
9
+
10
+ global _FFMPEG_EXT
11
+ if _FFMPEG_EXT is None:
12
+ _FFMPEG_EXT = _LazyImporter("_torio_ffmpeg", _init_ffmpeg)
13
+ return _FFMPEG_EXT
MLPY/Lib/site-packages/torio/_extension/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (466 Bytes). View file
 
MLPY/Lib/site-packages/torio/_extension/__pycache__/utils.cpython-39.pyc ADDED
Binary file (5.2 kB). View file
 
MLPY/Lib/site-packages/torio/_extension/utils.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib
2
+ import logging
3
+ import os
4
+ import types
5
+ from pathlib import Path
6
+
7
+ import torch
8
+
9
+ _LG = logging.getLogger(__name__)
10
+ _LIB_DIR = Path(__file__).parent.parent / "lib"
11
+
12
+
13
+ class _LazyImporter(types.ModuleType):
14
+ """Lazily import module/extension."""
15
+
16
+ def __init__(self, name, import_func):
17
+ super().__init__(name)
18
+ self.import_func = import_func
19
+ self.module = None
20
+
21
+ # Note:
22
+ # Python caches what was retrieved with `__getattr__`, so this method will not be
23
+ # called again for the same item.
24
+ def __getattr__(self, item):
25
+ self._import_once()
26
+ return getattr(self.module, item)
27
+
28
+ def __repr__(self):
29
+ if self.module is None:
30
+ return f"<module '{self.__module__}.{self.__class__.__name__}(\"{self.name}\")'>"
31
+ return repr(self.module)
32
+
33
+ def __dir__(self):
34
+ self._import_once()
35
+ return dir(self.module)
36
+
37
+ def _import_once(self):
38
+ if self.module is None:
39
+ self.module = self.import_func()
40
+ # Note:
41
+ # By attaching the module attributes to self,
42
+ # module attributes are directly accessible.
43
+ # This allows to avoid calling __getattr__ for every attribute access.
44
+ self.__dict__.update(self.module.__dict__)
45
+
46
+ def is_available(self):
47
+ try:
48
+ self._import_once()
49
+ except Exception:
50
+ return False
51
+ return True
52
+
53
+
54
+ def _get_lib_path(lib: str):
55
+ suffix = "pyd" if os.name == "nt" else "so"
56
+ path = _LIB_DIR / f"{lib}.{suffix}"
57
+ return path
58
+
59
+
60
+ def _load_lib(lib: str) -> bool:
61
+ """Load extension module
62
+
63
+ Note:
64
+ In case `torio` is deployed with `pex` format, the library file
65
+ is not in a standard location.
66
+ In this case, we expect that `libtorio` is available somewhere
67
+ in the search path of dynamic loading mechanism, so that importing
68
+ `_torio` will have library loader find and load `libtorio`.
69
+ This is the reason why the function should not raising an error when the library
70
+ file is not found.
71
+
72
+ Returns:
73
+ bool:
74
+ True if the library file is found AND the library loaded without failure.
75
+ False if the library file is not found (like in the case where torio
76
+ is deployed with pex format, thus the shared library file is
77
+ in a non-standard location.).
78
+ If the library file is found but there is an issue loading the library,
79
+ (such as missing dependency) then this function raises the exception as-is.
80
+
81
+ Raises:
82
+ Exception:
83
+ If the library file is found, but there is an issue loading the library file,
84
+ (when underlying `ctype.DLL` throws an exception), this function will pass
85
+ the exception as-is, instead of catching it and returning bool.
86
+ The expected case is `OSError` thrown by `ctype.DLL` when a dynamic dependency
87
+ is not found.
88
+ This behavior was chosen because the expected failure case is not recoverable.
89
+ If a dependency is missing, then users have to install it.
90
+ """
91
+ path = _get_lib_path(lib)
92
+ if not path.exists():
93
+ return False
94
+ torch.ops.load_library(path)
95
+ return True
96
+
97
+
98
+ _FFMPEG_VERS = ["6", "5", "4", ""]
99
+
100
+
101
+ def _find_versionsed_ffmpeg_extension(version: str):
102
+ ext = f"torio.lib._torio_ffmpeg{version}"
103
+ lib = f"libtorio_ffmpeg{version}"
104
+
105
+ if not importlib.util.find_spec(ext):
106
+ raise RuntimeError(f"FFmpeg{version} extension is not available.")
107
+
108
+ _load_lib(lib)
109
+ return importlib.import_module(ext)
110
+
111
+
112
+ def _find_ffmpeg_extension(ffmpeg_vers):
113
+ for ffmpeg_ver in ffmpeg_vers:
114
+ _LG.debug("Loading FFmpeg%s", ffmpeg_ver)
115
+ try:
116
+ ext = _find_versionsed_ffmpeg_extension(ffmpeg_ver)
117
+ _LG.debug("Successfully loaded FFmpeg%s", ffmpeg_ver)
118
+ return ext
119
+ except Exception:
120
+ _LG.debug("Failed to load FFmpeg%s extension.", ffmpeg_ver, exc_info=True)
121
+ continue
122
+ raise ImportError(
123
+ f"Failed to intialize FFmpeg extension. Tried versions: {ffmpeg_vers}. "
124
+ "Enable DEBUG logging to see more details about the error."
125
+ )
126
+
127
+
128
+ def _get_ffmpeg_versions():
129
+ ffmpeg_vers = _FFMPEG_VERS
130
+ # User override
131
+ if (ffmpeg_ver := os.environ.get("TORIO_USE_FFMPEG_VERSION")) is not None:
132
+ if ffmpeg_ver not in ffmpeg_vers:
133
+ raise ValueError(
134
+ f"The FFmpeg version '{ffmpeg_ver}' (read from TORIO_USE_FFMPEG_VERSION) "
135
+ f"is not one of supported values. Possible values are {ffmpeg_vers}"
136
+ )
137
+ ffmpeg_vers = [ffmpeg_ver]
138
+ return ffmpeg_vers
139
+
140
+
141
+ def _init_ffmpeg():
142
+ ffmpeg_vers = _get_ffmpeg_versions()
143
+ ext = _find_ffmpeg_extension(ffmpeg_vers)
144
+ ext.init()
145
+ if ext.get_log_level() > 8:
146
+ ext.set_log_level(8)
147
+ return ext
MLPY/Lib/site-packages/torio/io/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from ._streaming_media_decoder import StreamingMediaDecoder
2
+ from ._streaming_media_encoder import CodecConfig, StreamingMediaEncoder
3
+
4
+
5
+ __all__ = [
6
+ "StreamingMediaDecoder",
7
+ "CodecConfig",
8
+ "StreamingMediaEncoder",
9
+ ]
MLPY/Lib/site-packages/torio/io/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (343 Bytes). View file
 
MLPY/Lib/site-packages/torio/io/__pycache__/_streaming_media_decoder.cpython-39.pyc ADDED
Binary file (31.2 kB). View file
 
MLPY/Lib/site-packages/torio/io/__pycache__/_streaming_media_encoder.cpython-39.pyc ADDED
Binary file (19.6 kB). View file
 
MLPY/Lib/site-packages/torio/io/_streaming_media_decoder.py ADDED
@@ -0,0 +1,978 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import BinaryIO, Dict, Iterator, Optional, Tuple, TypeVar, Union
7
+
8
+ import torch
9
+ import torio
10
+ from torch.utils._pytree import tree_map
11
+
12
+ ffmpeg_ext = torio._extension.lazy_import_ffmpeg_ext()
13
+
14
+ __all__ = [
15
+ "StreamingMediaDecoder",
16
+ ]
17
+
18
+
19
+ @dataclass
20
+ class SourceStream:
21
+ """The metadata of a source stream, returned by :meth:`~torio.io.StreamingMediaDecoder.get_src_stream_info`.
22
+
23
+ This class is used when representing streams of media type other than `audio` or `video`.
24
+
25
+ When source stream is `audio` or `video` type, :class:`SourceAudioStream` and
26
+ :class:`SourceVideoStream`, which reports additional media-specific attributes,
27
+ are used respectively.
28
+ """
29
+
30
+ media_type: str
31
+ """The type of the stream.
32
+ One of ``"audio"``, ``"video"``, ``"data"``, ``"subtitle"``, ``"attachment"`` and empty string.
33
+
34
+ .. note::
35
+ Only audio and video streams are supported for output.
36
+ .. note::
37
+ Still images, such as PNG and JPEG formats are reported as video.
38
+ """
39
+ codec: str
40
+ """Short name of the codec. Such as ``"pcm_s16le"`` and ``"h264"``."""
41
+ codec_long_name: str
42
+ """Detailed name of the codec.
43
+
44
+ Such as "`PCM signed 16-bit little-endian`" and "`H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10`".
45
+ """
46
+ format: Optional[str]
47
+ """Media format. Such as ``"s16"`` and ``"yuv420p"``.
48
+
49
+ Commonly found audio values are;
50
+
51
+ - ``"u8"``, ``"u8p"``: Unsigned 8-bit unsigned interger.
52
+ - ``"s16"``, ``"s16p"``: 16-bit signed integer.
53
+ - ``"s32"``, ``"s32p"``: 32-bit signed integer.
54
+ - ``"flt"``, ``"fltp"``: 32-bit floating-point.
55
+
56
+ .. note::
57
+
58
+ `p` at the end indicates the format is `planar`.
59
+ Channels are grouped together instead of interspersed in memory.
60
+ """
61
+ bit_rate: Optional[int]
62
+ """Bit rate of the stream in bits-per-second.
63
+ This is an estimated values based on the initial few frames of the stream.
64
+ For container formats and variable bit rate, it can be 0.
65
+ """
66
+ num_frames: Optional[int]
67
+ """The number of frames in the stream"""
68
+ bits_per_sample: Optional[int]
69
+ """This is the number of valid bits in each output sample.
70
+ For compressed format, it can be 0.
71
+ """
72
+ metadata: Dict[str, str]
73
+ """Metadata attached to the source stream."""
74
+
75
+
76
+ @dataclass
77
+ class SourceAudioStream(SourceStream):
78
+ """The metadata of an audio source stream, returned by :meth:`~torio.io.StreamingMediaDecoder.get_src_stream_info`.
79
+
80
+ This class is used when representing audio stream.
81
+
82
+ In addition to the attributes reported by :class:`SourceStream`,
83
+ the following attributes are reported.
84
+ """
85
+
86
+ sample_rate: float
87
+ """Sample rate of the audio."""
88
+ num_channels: int
89
+ """Number of channels."""
90
+
91
+
92
+ @dataclass
93
+ class SourceVideoStream(SourceStream):
94
+ """The metadata of a video source stream, returned by :meth:`~torio.io.StreamingMediaDecoder.get_src_stream_info`.
95
+
96
+ This class is used when representing video stream.
97
+
98
+ In addition to the attributes reported by :class:`SourceStream`,
99
+ the following attributes are reported.
100
+ """
101
+
102
+ width: int
103
+ """Width of the video frame in pixel."""
104
+ height: int
105
+ """Height of the video frame in pixel."""
106
+ frame_rate: float
107
+ """Frame rate."""
108
+
109
+
110
+ def _parse_si(i):
111
+ media_type = i.media_type
112
+ if media_type == "audio":
113
+ return SourceAudioStream(
114
+ media_type=i.media_type,
115
+ codec=i.codec_name,
116
+ codec_long_name=i.codec_long_name,
117
+ format=i.format,
118
+ bit_rate=i.bit_rate,
119
+ num_frames=i.num_frames,
120
+ bits_per_sample=i.bits_per_sample,
121
+ metadata=i.metadata,
122
+ sample_rate=i.sample_rate,
123
+ num_channels=i.num_channels,
124
+ )
125
+ if media_type == "video":
126
+ return SourceVideoStream(
127
+ media_type=i.media_type,
128
+ codec=i.codec_name,
129
+ codec_long_name=i.codec_long_name,
130
+ format=i.format,
131
+ bit_rate=i.bit_rate,
132
+ num_frames=i.num_frames,
133
+ bits_per_sample=i.bits_per_sample,
134
+ metadata=i.metadata,
135
+ width=i.width,
136
+ height=i.height,
137
+ frame_rate=i.frame_rate,
138
+ )
139
+ return SourceStream(
140
+ media_type=i.media_type,
141
+ codec=i.codec_name,
142
+ codec_long_name=i.codec_long_name,
143
+ format=None,
144
+ bit_rate=None,
145
+ num_frames=None,
146
+ bits_per_sample=None,
147
+ metadata=i.metadata,
148
+ )
149
+
150
+
151
+ @dataclass
152
+ class OutputStream:
153
+ """Output stream configured on :class:`StreamingMediaDecoder`,
154
+ returned by :meth:`~torio.io.StreamingMediaDecoder.get_out_stream_info`.
155
+ """
156
+
157
+ source_index: int
158
+ """Index of the source stream that this output stream is connected."""
159
+ filter_description: str
160
+ """Description of filter graph applied to the source stream."""
161
+ media_type: str
162
+ """The type of the stream. ``"audio"`` or ``"video"``."""
163
+ format: str
164
+ """Media format. Such as ``"s16"`` and ``"yuv420p"``.
165
+
166
+ Commonly found audio values are;
167
+
168
+ - ``"u8"``, ``"u8p"``: Unsigned 8-bit unsigned interger.
169
+ - ``"s16"``, ``"s16p"``: 16-bit signed integer.
170
+ - ``"s32"``, ``"s32p"``: 32-bit signed integer.
171
+ - ``"flt"``, ``"fltp"``: 32-bit floating-point.
172
+
173
+ .. note::
174
+
175
+ `p` at the end indicates the format is `planar`.
176
+ Channels are grouped together instead of interspersed in memory."""
177
+
178
+
179
+ @dataclass
180
+ class OutputAudioStream(OutputStream):
181
+ """Information about an audio output stream configured with
182
+ :meth:`~torio.io.StreamingMediaDecoder.add_audio_stream` or
183
+ :meth:`~torio.io.StreamingMediaDecoder.add_basic_audio_stream`.
184
+
185
+ In addition to the attributes reported by :class:`OutputStream`,
186
+ the following attributes are reported.
187
+ """
188
+
189
+ sample_rate: float
190
+ """Sample rate of the audio."""
191
+ num_channels: int
192
+ """Number of channels."""
193
+
194
+
195
+ @dataclass
196
+ class OutputVideoStream(OutputStream):
197
+ """Information about a video output stream configured with
198
+ :meth:`~torio.io.StreamingMediaDecoder.add_video_stream` or
199
+ :meth:`~torio.io.StreamingMediaDecoder.add_basic_video_stream`.
200
+
201
+ In addition to the attributes reported by :class:`OutputStream`,
202
+ the following attributes are reported.
203
+ """
204
+
205
+ width: int
206
+ """Width of the video frame in pixel."""
207
+ height: int
208
+ """Height of the video frame in pixel."""
209
+ frame_rate: float
210
+ """Frame rate."""
211
+
212
+
213
+ def _parse_oi(i):
214
+ media_type = i.media_type
215
+ if media_type == "audio":
216
+ return OutputAudioStream(
217
+ source_index=i.source_index,
218
+ filter_description=i.filter_description,
219
+ media_type=i.media_type,
220
+ format=i.format,
221
+ sample_rate=i.sample_rate,
222
+ num_channels=i.num_channels,
223
+ )
224
+ if media_type == "video":
225
+ return OutputVideoStream(
226
+ source_index=i.source_index,
227
+ filter_description=i.filter_description,
228
+ media_type=i.media_type,
229
+ format=i.format,
230
+ width=i.width,
231
+ height=i.height,
232
+ frame_rate=i.frame_rate,
233
+ )
234
+ raise ValueError(f"Unexpected media_type: {i.media_type}({i})")
235
+
236
+
237
+ def _get_afilter_desc(sample_rate: Optional[int], fmt: Optional[str], num_channels: Optional[int]):
238
+ descs = []
239
+ if sample_rate is not None:
240
+ descs.append(f"aresample={sample_rate}")
241
+ if fmt is not None or num_channels is not None:
242
+ parts = []
243
+ if fmt is not None:
244
+ parts.append(f"sample_fmts={fmt}")
245
+ if num_channels is not None:
246
+ parts.append(f"channel_layouts={num_channels}c")
247
+ descs.append(f"aformat={':'.join(parts)}")
248
+ return ",".join(descs) if descs else None
249
+
250
+
251
+ def _get_vfilter_desc(frame_rate: Optional[float], width: Optional[int], height: Optional[int], fmt: Optional[str]):
252
+ descs = []
253
+ if frame_rate is not None:
254
+ descs.append(f"fps={frame_rate}")
255
+ scales = []
256
+ if width is not None:
257
+ scales.append(f"width={width}")
258
+ if height is not None:
259
+ scales.append(f"height={height}")
260
+ if scales:
261
+ descs.append(f"scale={':'.join(scales)}")
262
+ if fmt is not None:
263
+ descs.append(f"format=pix_fmts={fmt}")
264
+ return ",".join(descs) if descs else None
265
+
266
+
267
+ # Base class for ChunkTensor
268
+ # Based off of TrivialTensorViaComposition
269
+ # https://github.com/albanD/subclass_zoo/blob/0eeb1d68fb59879029c610bc407f2997ae43ba0a/trivial_tensors.py#L83
270
+ class ChunkTensorBase(torch.Tensor):
271
+ __torch_function__ = torch._C._disabled_torch_function_impl
272
+
273
+ @staticmethod
274
+ def __new__(cls, _elem, *_):
275
+ return super().__new__(cls, _elem)
276
+
277
+ @classmethod
278
+ def __torch_dispatch__(cls, func, _, args=(), kwargs=None):
279
+ def unwrap(t):
280
+ return t._elem if isinstance(t, cls) else t
281
+
282
+ return func(*tree_map(unwrap, args), **tree_map(unwrap, kwargs))
283
+
284
+
285
+ @dataclass
286
+ class ChunkTensor(ChunkTensorBase):
287
+ """Decoded media frames with metadata.
288
+
289
+ The instance of this class represents the decoded video/audio frames with
290
+ metadata, and the instance itself behave like :py:class:`~torch.Tensor`.
291
+
292
+ Client codes can pass instance of this class as-if it's
293
+ :py:class:`~torch.Tensor` class, or call the methods defined on
294
+ :py:class:`~torch.Tensor` class.
295
+
296
+ Example:
297
+ >>> # Define input streams
298
+ >>> reader = StreamingMediaDecoder(...)
299
+ >>> reader.add_audio_stream(frames_per_chunk=4000, sample_rate=8000)
300
+ >>> reader.add_video_stream(frames_per_chunk=7, frame_rate=28)
301
+ >>> # Decode the streams and fetch frames
302
+ >>> reader.fill_buffer()
303
+ >>> audio_chunk, video_chunk = reader.pop_chunks()
304
+
305
+ >>> # Access metadata
306
+ >>> (audio_chunk.pts, video_chunks.pts)
307
+ (0.0, 0.0)
308
+ >>>
309
+ >>> # The second time the PTS is different
310
+ >>> reader.fill_buffer()
311
+ >>> audio_chunk, video_chunk = reader.pop_chunks()
312
+ >>> (audio_chunk.pts, video_chunks.pts)
313
+ (0.5, 0.25)
314
+
315
+ >>> # Call PyTorch ops on chunk
316
+ >>> audio_chunk.shape
317
+ torch.Size([4000, 2]
318
+ >>> power = torch.pow(video_chunk, 2)
319
+ >>>
320
+ >>> # the result is a plain torch.Tensor class
321
+ >>> type(power)
322
+ <class 'torch.Tensor'>
323
+ >>>
324
+ >>> # Metadata is not available on the result
325
+ >>> power.pts
326
+ AttributeError: 'Tensor' object has no attribute 'pts'
327
+ """
328
+
329
+ # Keep it private for now
330
+ _elem: torch.Tensor
331
+
332
+ pts: float
333
+ """Presentation time stamp of the first frame in the chunk.
334
+
335
+ Unit: second.
336
+ """
337
+
338
+
339
+ def _format_doc(**kwargs):
340
+ def decorator(obj):
341
+ obj.__doc__ = obj.__doc__.format(**kwargs)
342
+ return obj
343
+
344
+ return decorator
345
+
346
+
347
+ _frames_per_chunk = """Number of frames returned as one chunk.
348
+ If the source stream is exhausted before enough frames are buffered,
349
+ then the chunk is returned as-is.
350
+
351
+ Providing ``-1`` disables chunking and :py:func:`pop_chunks` method
352
+ will concatenate all the buffered frames and return it."""
353
+
354
+ _buffer_chunk_size = """Internal buffer size.
355
+ When the number of chunks buffered exceeds this number, old frames are
356
+ dropped. For example, if ``frames_per_chunk`` is 5 and ``buffer_chunk_size`` is
357
+ 3, then frames older than ``15`` are dropped.
358
+ Providing ``-1`` disables this behavior.
359
+
360
+ Default: ``3``."""
361
+
362
+ _audio_stream_index = """The source audio stream index.
363
+ If omitted, :py:attr:`default_audio_stream` is used."""
364
+
365
+
366
+ _video_stream_index = """The source video stream index.
367
+ If omitted, :py:attr:`default_video_stream` is used."""
368
+
369
+ _decoder = """The name of the decoder to be used.
370
+ When provided, use the specified decoder instead of the default one.
371
+
372
+ To list the available decoders, please use
373
+ :py:func:`~torio.utils.ffmpeg_utils.get_audio_decoders` for audio, and
374
+ :py:func:`~torio.utils.ffmpeg_utils.get_video_decoders` for video.
375
+
376
+ Default: ``None``."""
377
+
378
+ _decoder_option = """Options passed to decoder.
379
+ Mapping from str to str. (Default: ``None``)
380
+
381
+ To list decoder options for a decoder, you can use
382
+ ``ffmpeg -h decoder=<DECODER>`` command.
383
+
384
+ |
385
+
386
+ In addition to decoder-specific options, you can also pass options related
387
+ to multithreading. They are effective only if the decoder support them.
388
+ If neither of them are provided, StreamingMediaDecoder defaults to single thread.
389
+
390
+ ``"threads"``: The number of threads (in str).
391
+ Providing the value ``"0"`` will let FFmpeg decides based on its heuristics.
392
+
393
+ ``"thread_type"``: Which multithreading method to use.
394
+ The valid values are ``"frame"`` or ``"slice"``.
395
+ Note that each decoder supports different set of methods.
396
+ If not provided, a default value is used.
397
+
398
+ - ``"frame"``: Decode more than one frame at once.
399
+ Each thread handles one frame.
400
+ This will increase decoding delay by one frame per thread
401
+ - ``"slice"``: Decode more than one part of a single frame at once.
402
+
403
+ |
404
+ """
405
+
406
+
407
+ _hw_accel = """Enable hardware acceleration.
408
+
409
+ When video is decoded on CUDA hardware, for example
410
+ `decoder="h264_cuvid"`, passing CUDA device indicator to `hw_accel`
411
+ (i.e. `hw_accel="cuda:0"`) will make StreamingMediaDecoder place the resulting
412
+ frames directly on the specified CUDA device as CUDA tensor.
413
+
414
+ If `None`, the frame will be moved to CPU memory.
415
+ Default: ``None``."""
416
+
417
+
418
+ _format_audio_args = _format_doc(
419
+ frames_per_chunk=_frames_per_chunk,
420
+ buffer_chunk_size=_buffer_chunk_size,
421
+ stream_index=_audio_stream_index,
422
+ decoder=_decoder,
423
+ decoder_option=_decoder_option,
424
+ )
425
+
426
+
427
+ _format_video_args = _format_doc(
428
+ frames_per_chunk=_frames_per_chunk,
429
+ buffer_chunk_size=_buffer_chunk_size,
430
+ stream_index=_video_stream_index,
431
+ decoder=_decoder,
432
+ decoder_option=_decoder_option,
433
+ hw_accel=_hw_accel,
434
+ )
435
+
436
+
437
+ InputStreamTypes = TypeVar("InputStream", bound=SourceStream)
438
+ OutputStreamTypes = TypeVar("OutputStream", bound=OutputStream)
439
+
440
+
441
+ class StreamingMediaDecoder:
442
+ """Fetch and decode audio/video streams chunk by chunk.
443
+
444
+ For the detailed usage of this class, please refer to the tutorial.
445
+
446
+ Args:
447
+ src (str, path-like, bytes or file-like object): The media source.
448
+ If string-type, it must be a resource indicator that FFmpeg can
449
+ handle. This includes a file path, URL, device identifier or
450
+ filter expression. The supported value depends on the FFmpeg found
451
+ in the system.
452
+
453
+ If bytes, it must be an encoded media data in contiguous memory.
454
+
455
+ If file-like object, it must support `read` method with the signature
456
+ `read(size: int) -> bytes`.
457
+ Additionally, if the file-like object has `seek` method, it uses
458
+ the method when parsing media metadata. This improves the reliability
459
+ of codec detection. The signagure of `seek` method must be
460
+ `seek(offset: int, whence: int) -> int`.
461
+
462
+ Please refer to the following for the expected signature and behavior
463
+ of `read` and `seek` method.
464
+
465
+ - https://docs.python.org/3/library/io.html#io.BufferedIOBase.read
466
+ - https://docs.python.org/3/library/io.html#io.IOBase.seek
467
+
468
+ format (str or None, optional):
469
+ Override the input format, or specify the source sound device.
470
+ Default: ``None`` (no override nor device input).
471
+
472
+ This argument serves two different usecases.
473
+
474
+ 1) Override the source format.
475
+ This is useful when the input data do not contain a header.
476
+
477
+ 2) Specify the input source device.
478
+ This allows to load media stream from hardware devices,
479
+ such as microphone, camera and screen, or a virtual device.
480
+
481
+
482
+ .. note::
483
+
484
+ This option roughly corresponds to ``-f`` option of ``ffmpeg`` command.
485
+ Please refer to the ffmpeg documentations for the possible values.
486
+
487
+ https://ffmpeg.org/ffmpeg-formats.html#Demuxers
488
+
489
+ Please use :py:func:`~torio.utils.ffmpeg_utils.get_demuxers` to list the
490
+ demultiplexers available in the current environment.
491
+
492
+ For device access, the available values vary based on hardware (AV device) and
493
+ software configuration (ffmpeg build).
494
+
495
+ https://ffmpeg.org/ffmpeg-devices.html#Input-Devices
496
+
497
+ Please use :py:func:`~torio.utils.ffmpeg_utils.get_input_devices` to list
498
+ the input devices available in the current environment.
499
+
500
+ option (dict of str to str, optional):
501
+ Custom option passed when initializing format context (opening source).
502
+
503
+ You can use this argument to change the input source before it is passed to decoder.
504
+
505
+ Default: ``None``.
506
+
507
+ buffer_size (int):
508
+ The internal buffer size in byte. Used only when `src` is file-like object.
509
+
510
+ Default: `4096`.
511
+ """
512
+
513
+ def __init__(
514
+ self,
515
+ src: Union[str, Path, BinaryIO],
516
+ format: Optional[str] = None,
517
+ option: Optional[Dict[str, str]] = None,
518
+ buffer_size: int = 4096,
519
+ ):
520
+ self.src = src
521
+ if isinstance(src, bytes):
522
+ self._be = ffmpeg_ext.StreamingMediaDecoderBytes(src, format, option, buffer_size)
523
+ elif hasattr(src, "read"):
524
+ self._be = ffmpeg_ext.StreamingMediaDecoderFileObj(src, format, option, buffer_size)
525
+ else:
526
+ self._be = ffmpeg_ext.StreamingMediaDecoder(os.path.normpath(src), format, option)
527
+
528
+ i = self._be.find_best_audio_stream()
529
+ self._default_audio_stream = None if i < 0 else i
530
+ i = self._be.find_best_video_stream()
531
+ self._default_video_stream = None if i < 0 else i
532
+
533
+ @property
534
+ def num_src_streams(self):
535
+ """Number of streams found in the provided media source.
536
+
537
+ :type: int
538
+ """
539
+ return self._be.num_src_streams()
540
+
541
+ @property
542
+ def num_out_streams(self):
543
+ """Number of output streams configured by client code.
544
+
545
+ :type: int
546
+ """
547
+ return self._be.num_out_streams()
548
+
549
+ @property
550
+ def default_audio_stream(self):
551
+ """The index of default audio stream. ``None`` if there is no audio stream
552
+
553
+ :type: Optional[int]
554
+ """
555
+ return self._default_audio_stream
556
+
557
+ @property
558
+ def default_video_stream(self):
559
+ """The index of default video stream. ``None`` if there is no video stream
560
+
561
+ :type: Optional[int]
562
+ """
563
+ return self._default_video_stream
564
+
565
+ def get_metadata(self) -> Dict[str, str]:
566
+ """Get the metadata of the source media.
567
+
568
+ Returns:
569
+ dict
570
+ """
571
+ return self._be.get_metadata()
572
+
573
+ def get_src_stream_info(self, i: int) -> InputStreamTypes:
574
+ """Get the metadata of source stream
575
+
576
+ Args:
577
+ i (int): Stream index.
578
+ Returns:
579
+ InputStreamTypes:
580
+ Information about the source stream.
581
+ If the source stream is audio type, then
582
+ :class:`~torio.io._stream_reader.SourceAudioStream` is returned.
583
+ If it is video type, then
584
+ :class:`~torio.io._stream_reader.SourceVideoStream` is returned.
585
+ Otherwise :class:`~torio.io._stream_reader.SourceStream` class is returned.
586
+ """
587
+ return _parse_si(self._be.get_src_stream_info(i))
588
+
589
+ def get_out_stream_info(self, i: int) -> OutputStreamTypes:
590
+ """Get the metadata of output stream
591
+
592
+ Args:
593
+ i (int): Stream index.
594
+ Returns:
595
+ OutputStreamTypes
596
+ Information about the output stream.
597
+ If the output stream is audio type, then
598
+ :class:`~torio.io._stream_reader.OutputAudioStream` is returned.
599
+ If it is video type, then
600
+ :class:`~torio.io._stream_reader.OutputVideoStream` is returned.
601
+ """
602
+ info = self._be.get_out_stream_info(i)
603
+ return _parse_oi(info)
604
+
605
+ def seek(self, timestamp: float, mode: str = "precise"):
606
+ """Seek the stream to the given timestamp [second]
607
+
608
+ Args:
609
+ timestamp (float): Target time in second.
610
+ mode (str): Controls how seek is done.
611
+ Valid choices are;
612
+
613
+ * "key": Seek into the nearest key frame before the given timestamp.
614
+ * "any": Seek into any frame (including non-key frames) before the given timestamp.
615
+ * "precise": First seek into the nearest key frame before the given timestamp, then
616
+ decode frames until it reaches the closes frame to the given timestamp.
617
+
618
+ Note:
619
+ All the modes invalidate and reset the internal state of decoder.
620
+ When using "any" mode and if it ends up seeking into non-key frame,
621
+ the image decoded may be invalid due to lack of key frame.
622
+ Using "precise" will workaround this issue by decoding frames from previous
623
+ key frame, but will be slower.
624
+ """
625
+ modes = {
626
+ "key": 0,
627
+ "any": 1,
628
+ "precise": 2,
629
+ }
630
+ if mode not in modes:
631
+ raise ValueError(f"The value of mode must be one of {list(modes.keys())}. Found: {mode}")
632
+ self._be.seek(timestamp, modes[mode])
633
+
634
+ @_format_audio_args
635
+ def add_basic_audio_stream(
636
+ self,
637
+ frames_per_chunk: int,
638
+ buffer_chunk_size: int = 3,
639
+ *,
640
+ stream_index: Optional[int] = None,
641
+ decoder: Optional[str] = None,
642
+ decoder_option: Optional[Dict[str, str]] = None,
643
+ format: Optional[str] = "fltp",
644
+ sample_rate: Optional[int] = None,
645
+ num_channels: Optional[int] = None,
646
+ ):
647
+ """Add output audio stream
648
+
649
+ Args:
650
+ frames_per_chunk (int): {frames_per_chunk}
651
+
652
+ buffer_chunk_size (int, optional): {buffer_chunk_size}
653
+
654
+ stream_index (int or None, optional): {stream_index}
655
+
656
+ decoder (str or None, optional): {decoder}
657
+
658
+ decoder_option (dict or None, optional): {decoder_option}
659
+
660
+ format (str, optional): Output sample format (precision).
661
+
662
+ If ``None``, the output chunk has dtype corresponding to
663
+ the precision of the source audio.
664
+
665
+ Otherwise, the sample is converted and the output dtype is changed
666
+ as following.
667
+
668
+ - ``"u8p"``: The output is ``torch.uint8`` type.
669
+ - ``"s16p"``: The output is ``torch.int16`` type.
670
+ - ``"s32p"``: The output is ``torch.int32`` type.
671
+ - ``"s64p"``: The output is ``torch.int64`` type.
672
+ - ``"fltp"``: The output is ``torch.float32`` type.
673
+ - ``"dblp"``: The output is ``torch.float64`` type.
674
+
675
+ Default: ``"fltp"``.
676
+
677
+ sample_rate (int or None, optional): If provided, resample the audio.
678
+
679
+ num_channels (int, or None, optional): If provided, change the number of channels.
680
+ """
681
+ self.add_audio_stream(
682
+ frames_per_chunk,
683
+ buffer_chunk_size,
684
+ stream_index=stream_index,
685
+ decoder=decoder,
686
+ decoder_option=decoder_option,
687
+ filter_desc=_get_afilter_desc(sample_rate, format, num_channels),
688
+ )
689
+
690
+ @_format_video_args
691
+ def add_basic_video_stream(
692
+ self,
693
+ frames_per_chunk: int,
694
+ buffer_chunk_size: int = 3,
695
+ *,
696
+ stream_index: Optional[int] = None,
697
+ decoder: Optional[str] = None,
698
+ decoder_option: Optional[Dict[str, str]] = None,
699
+ format: Optional[str] = "rgb24",
700
+ frame_rate: Optional[int] = None,
701
+ width: Optional[int] = None,
702
+ height: Optional[int] = None,
703
+ hw_accel: Optional[str] = None,
704
+ ):
705
+ """Add output video stream
706
+
707
+ Args:
708
+ frames_per_chunk (int): {frames_per_chunk}
709
+
710
+ buffer_chunk_size (int, optional): {buffer_chunk_size}
711
+
712
+ stream_index (int or None, optional): {stream_index}
713
+
714
+ decoder (str or None, optional): {decoder}
715
+
716
+ decoder_option (dict or None, optional): {decoder_option}
717
+
718
+ format (str, optional): Change the format of image channels. Valid values are,
719
+
720
+ - ``"rgb24"``: 8 bits * 3 channels (R, G, B)
721
+ - ``"bgr24"``: 8 bits * 3 channels (B, G, R)
722
+ - ``"yuv420p"``: 8 bits * 3 channels (Y, U, V)
723
+ - ``"gray"``: 8 bits * 1 channels
724
+
725
+ Default: ``"rgb24"``.
726
+
727
+ frame_rate (int or None, optional): If provided, change the frame rate.
728
+
729
+ width (int or None, optional): If provided, change the image width. Unit: Pixel.
730
+
731
+ height (int or None, optional): If provided, change the image height. Unit: Pixel.
732
+
733
+ hw_accel (str or None, optional): {hw_accel}
734
+ """
735
+ self.add_video_stream(
736
+ frames_per_chunk,
737
+ buffer_chunk_size,
738
+ stream_index=stream_index,
739
+ decoder=decoder,
740
+ decoder_option=decoder_option,
741
+ filter_desc=_get_vfilter_desc(frame_rate, width, height, format),
742
+ hw_accel=hw_accel,
743
+ )
744
+
745
+ @_format_audio_args
746
+ def add_audio_stream(
747
+ self,
748
+ frames_per_chunk: int,
749
+ buffer_chunk_size: int = 3,
750
+ *,
751
+ stream_index: Optional[int] = None,
752
+ decoder: Optional[str] = None,
753
+ decoder_option: Optional[Dict[str, str]] = None,
754
+ filter_desc: Optional[str] = None,
755
+ ):
756
+ """Add output audio stream
757
+
758
+ Args:
759
+ frames_per_chunk (int): {frames_per_chunk}
760
+
761
+ buffer_chunk_size (int, optional): {buffer_chunk_size}
762
+
763
+ stream_index (int or None, optional): {stream_index}
764
+
765
+ decoder (str or None, optional): {decoder}
766
+
767
+ decoder_option (dict or None, optional): {decoder_option}
768
+
769
+ filter_desc (str or None, optional): Filter description.
770
+ The list of available filters can be found at
771
+ https://ffmpeg.org/ffmpeg-filters.html
772
+ Note that complex filters are not supported.
773
+
774
+ """
775
+ i = self.default_audio_stream if stream_index is None else stream_index
776
+ if i is None:
777
+ raise RuntimeError("There is no audio stream.")
778
+ self._be.add_audio_stream(
779
+ i,
780
+ frames_per_chunk,
781
+ buffer_chunk_size,
782
+ filter_desc,
783
+ decoder,
784
+ decoder_option or {},
785
+ )
786
+
787
+ @_format_video_args
788
+ def add_video_stream(
789
+ self,
790
+ frames_per_chunk: int,
791
+ buffer_chunk_size: int = 3,
792
+ *,
793
+ stream_index: Optional[int] = None,
794
+ decoder: Optional[str] = None,
795
+ decoder_option: Optional[Dict[str, str]] = None,
796
+ filter_desc: Optional[str] = None,
797
+ hw_accel: Optional[str] = None,
798
+ ):
799
+ """Add output video stream
800
+
801
+ Args:
802
+ frames_per_chunk (int): {frames_per_chunk}
803
+
804
+ buffer_chunk_size (int, optional): {buffer_chunk_size}
805
+
806
+ stream_index (int or None, optional): {stream_index}
807
+
808
+ decoder (str or None, optional): {decoder}
809
+
810
+ decoder_option (dict or None, optional): {decoder_option}
811
+
812
+ hw_accel (str or None, optional): {hw_accel}
813
+
814
+ filter_desc (str or None, optional): Filter description.
815
+ The list of available filters can be found at
816
+ https://ffmpeg.org/ffmpeg-filters.html
817
+ Note that complex filters are not supported.
818
+ """
819
+ i = self.default_video_stream if stream_index is None else stream_index
820
+ if i is None:
821
+ raise RuntimeError("There is no video stream.")
822
+ self._be.add_video_stream(
823
+ i,
824
+ frames_per_chunk,
825
+ buffer_chunk_size,
826
+ filter_desc,
827
+ decoder,
828
+ decoder_option or {},
829
+ hw_accel,
830
+ )
831
+
832
+ def remove_stream(self, i: int):
833
+ """Remove an output stream.
834
+
835
+ Args:
836
+ i (int): Index of the output stream to be removed.
837
+ """
838
+ self._be.remove_stream(i)
839
+
840
+ def process_packet(self, timeout: Optional[float] = None, backoff: float = 10.0) -> int:
841
+ """Read the source media and process one packet.
842
+
843
+ If a packet is read successfully, then the data in the packet will
844
+ be decoded and passed to corresponding output stream processors.
845
+
846
+ If the packet belongs to a source stream that is not connected to
847
+ an output stream, then the data are discarded.
848
+
849
+ When the source reaches EOF, then it triggers all the output stream
850
+ processors to enter drain mode. All the output stream processors
851
+ flush the pending frames.
852
+
853
+ Args:
854
+ timeout (float or None, optional): Timeout in milli seconds.
855
+
856
+ This argument changes the retry behavior when it failed to
857
+ process a packet due to the underlying media resource being
858
+ temporarily unavailable.
859
+
860
+ When using a media device such as a microphone, there are cases
861
+ where the underlying buffer is not ready.
862
+ Calling this function in such case would cause the system to report
863
+ `EAGAIN (resource temporarily unavailable)`.
864
+
865
+ * ``>=0``: Keep retrying until the given time passes.
866
+
867
+ * ``0<``: Keep retrying forever.
868
+
869
+ * ``None`` : No retrying and raise an exception immediately.
870
+
871
+ Default: ``None``.
872
+
873
+ Note:
874
+
875
+ The retry behavior is applicable only when the reason is the
876
+ unavailable resource. It is not invoked if the reason of failure is
877
+ other.
878
+
879
+ backoff (float, optional): Time to wait before retrying in milli seconds.
880
+
881
+ This option is effective only when `timeout` is effective. (not ``None``)
882
+
883
+ When `timeout` is effective, this `backoff` controls how long the function
884
+ should wait before retrying. Default: ``10.0``.
885
+
886
+ Returns:
887
+ int:
888
+ ``0``
889
+ A packet was processed properly. The caller can keep
890
+ calling this function to buffer more frames.
891
+
892
+ ``1``
893
+ The streamer reached EOF. All the output stream processors
894
+ flushed the pending frames. The caller should stop calling
895
+ this method.
896
+ """
897
+ return self._be.process_packet(timeout, backoff)
898
+
899
+ def process_all_packets(self):
900
+ """Process packets until it reaches EOF."""
901
+ self._be.process_all_packets()
902
+
903
+ def is_buffer_ready(self) -> bool:
904
+ """Returns true if all the output streams have at least one chunk filled."""
905
+ return self._be.is_buffer_ready()
906
+
907
+ def pop_chunks(self) -> Tuple[Optional[ChunkTensor]]:
908
+ """Pop one chunk from all the output stream buffers.
909
+
910
+ Returns:
911
+ Tuple[Optional[ChunkTensor]]:
912
+ Buffer contents.
913
+ If a buffer does not contain any frame, then `None` is returned instead.
914
+ """
915
+ ret = []
916
+ for chunk in self._be.pop_chunks():
917
+ if chunk is None:
918
+ ret.append(None)
919
+ else:
920
+ ret.append(ChunkTensor(chunk.frames, chunk.pts))
921
+ return ret
922
+
923
+ def fill_buffer(self, timeout: Optional[float] = None, backoff: float = 10.0) -> int:
924
+ """Keep processing packets until all buffers have at least one chunk
925
+
926
+ Arguments:
927
+ timeout (float or None, optional): See
928
+ :py:func:`~StreamingMediaDecoder.process_packet`. (Default: ``None``)
929
+
930
+ backoff (float, optional): See
931
+ :py:func:`~StreamingMediaDecoder.process_packet`. (Default: ``10.0``)
932
+
933
+ Returns:
934
+ int:
935
+ ``0``
936
+ Packets are processed properly and buffers are
937
+ ready to be popped once.
938
+
939
+ ``1``
940
+ The streamer reached EOF. All the output stream processors
941
+ flushed the pending frames. The caller should stop calling
942
+ this method.
943
+ """
944
+ return self._be.fill_buffer(timeout, backoff)
945
+
946
+ def stream(
947
+ self, timeout: Optional[float] = None, backoff: float = 10.0
948
+ ) -> Iterator[Tuple[Optional[ChunkTensor], ...]]:
949
+ """Return an iterator that generates output tensors
950
+
951
+ Arguments:
952
+ timeout (float or None, optional): See
953
+ :py:func:`~StreamingMediaDecoder.process_packet`. (Default: ``None``)
954
+
955
+ backoff (float, optional): See
956
+ :py:func:`~StreamingMediaDecoder.process_packet`. (Default: ``10.0``)
957
+
958
+ Returns:
959
+ Iterator[Tuple[Optional[ChunkTensor], ...]]:
960
+ Iterator that yields a tuple of chunks that correspond to the output
961
+ streams defined by client code.
962
+ If an output stream is exhausted, then the chunk Tensor is substituted
963
+ with ``None``.
964
+ The iterator stops if all the output streams are exhausted.
965
+ """
966
+ if self.num_out_streams == 0:
967
+ raise RuntimeError("No output stream is configured.")
968
+
969
+ while True:
970
+ if self.fill_buffer(timeout, backoff):
971
+ break
972
+ yield self.pop_chunks()
973
+
974
+ while True:
975
+ chunks = self.pop_chunks()
976
+ if all(c is None for c in chunks):
977
+ return
978
+ yield chunks
MLPY/Lib/site-packages/torio/io/_streaming_media_encoder.py ADDED
@@ -0,0 +1,502 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+ from pathlib import Path
3
+ from typing import BinaryIO, Dict, Optional, Union
4
+
5
+ import torch
6
+ import torio
7
+
8
+ ffmpeg_ext = torio._extension.lazy_import_ffmpeg_ext()
9
+
10
+
11
+ @dataclass
12
+ class CodecConfig:
13
+ """Codec configuration."""
14
+
15
+ bit_rate: int = -1
16
+ """Bit rate"""
17
+
18
+ compression_level: int = -1
19
+ """Compression level"""
20
+
21
+ qscale: Optional[int] = None
22
+ """Global quality factor. Enables variable bit rate. Valid values depend on encoder.
23
+
24
+ For example: MP3 takes ``0`` - ``9`` (https://trac.ffmpeg.org/wiki/Encode/MP3) while
25
+ libvorbis takes ``-1`` - ``10``.
26
+ """
27
+
28
+ gop_size: int = -1
29
+ """The number of pictures in a group of pictures, or 0 for intra_only"""
30
+
31
+ max_b_frames: int = -1
32
+ """maximum number of B-frames between non-B-frames."""
33
+
34
+
35
+ def _convert_config(cfg: CodecConfig):
36
+ if cfg is None:
37
+ return None
38
+ # Convert the codecconfig to C++ compatible type.
39
+ # omitting the return type annotation so as not to access ffmpeg_ext here.
40
+ return ffmpeg_ext.CodecConfig(
41
+ cfg.bit_rate,
42
+ cfg.compression_level,
43
+ cfg.qscale,
44
+ cfg.gop_size,
45
+ cfg.max_b_frames,
46
+ )
47
+
48
+
49
+ def _format_doc(**kwargs):
50
+ def decorator(obj):
51
+ obj.__doc__ = obj.__doc__.format(**kwargs)
52
+ return obj
53
+
54
+ return decorator
55
+
56
+
57
+ _encoder = """The name of the encoder to be used.
58
+ When provided, use the specified encoder instead of the default one.
59
+
60
+ To list the available encoders, please use
61
+ :py:func:`~torio.utils.ffmpeg_utils.get_audio_encoders` for audio, and
62
+ :py:func:`~torio.utils.ffmpeg_utils.get_video_encoders` for video.
63
+
64
+ Default: ``None``."""
65
+
66
+
67
+ _encoder_option = """Options passed to encoder.
68
+ Mapping from str to str.
69
+
70
+ To list encoder options for a encoder, you can use
71
+ ``ffmpeg -h encoder=<ENCODER>`` command.
72
+
73
+ Default: ``None``.
74
+
75
+ |
76
+
77
+ In addition to encoder-specific options, you can also pass options related
78
+ to multithreading. They are effective only if the encoder support them.
79
+ If neither of them are provided, StreamReader defaults to single thread.
80
+
81
+ ``"threads"``: The number of threads (in str).
82
+ Providing the value ``"0"`` will let FFmpeg decides based on its heuristics.
83
+
84
+ ``"thread_type"``: Which multithreading method to use.
85
+ The valid values are ``"frame"`` or ``"slice"``.
86
+ Note that each encoder supports different set of methods.
87
+ If not provided, a default value is used.
88
+
89
+ - ``"frame"``: Encode more than one frame at once.
90
+ Each thread handles one frame.
91
+ This will increase decoding delay by one frame per thread
92
+ - ``"slice"``: Encode more than one part of a single frame at once.
93
+
94
+ |
95
+ """
96
+
97
+
98
+ _encoder_format = """Format used to encode media.
99
+ When encoder supports multiple formats, passing this argument will override
100
+ the format used for encoding.
101
+
102
+ To list supported formats for the encoder, you can use
103
+ ``ffmpeg -h encoder=<ENCODER>`` command.
104
+
105
+ Default: ``None``.
106
+
107
+ Note:
108
+ When ``encoder_format`` option is not provided, encoder uses its default format.
109
+
110
+ For example, when encoding audio into wav format, 16-bit signed integer is used,
111
+ and when encoding video into mp4 format (h264 encoder), one of YUV format is used.
112
+
113
+ This is because typically, 32-bit or 16-bit floating point is used in audio models but
114
+ they are not commonly used in audio formats. Similarly, RGB24 is commonly used in vision
115
+ models, but video formats usually (and better) support YUV formats.
116
+ """
117
+
118
+ _codec_config = """Codec configuration. Please refer to :py:class:`CodecConfig` for
119
+ configuration options.
120
+
121
+ Default: ``None``."""
122
+
123
+
124
+ _filter_desc = """Additional processing to apply before encoding the input media.
125
+ """
126
+
127
+ _format_common_args = _format_doc(
128
+ encoder=_encoder,
129
+ encoder_option=_encoder_option,
130
+ encoder_format=_encoder_format,
131
+ codec_config=_codec_config,
132
+ filter_desc=_filter_desc,
133
+ )
134
+
135
+
136
+ class StreamingMediaEncoder:
137
+ """Encode and write audio/video streams chunk by chunk
138
+
139
+ Args:
140
+ dst (str, path-like or file-like object): The destination where the encoded data are written.
141
+ If string-type, it must be a resource indicator that FFmpeg can
142
+ handle. The supported value depends on the FFmpeg found in the system.
143
+
144
+ If file-like object, it must support `write` method with the signature
145
+ `write(data: bytes) -> int`.
146
+
147
+ Please refer to the following for the expected signature and behavior of
148
+ `write` method.
149
+
150
+ - https://docs.python.org/3/library/io.html#io.BufferedIOBase.write
151
+
152
+ format (str or None, optional):
153
+ Override the output format, or specify the output media device.
154
+ Default: ``None`` (no override nor device output).
155
+
156
+ This argument serves two different use cases.
157
+
158
+ 1) Override the output format.
159
+ This is useful when writing raw data or in a format different from the extension.
160
+
161
+ 2) Specify the output device.
162
+ This allows to output media streams to hardware devices,
163
+ such as speaker and video screen.
164
+
165
+ .. note::
166
+
167
+ This option roughly corresponds to ``-f`` option of ``ffmpeg`` command.
168
+ Please refer to the ffmpeg documentations for possible values.
169
+
170
+ https://ffmpeg.org/ffmpeg-formats.html#Muxers
171
+
172
+ Please use :py:func:`~torio.utils.ffmpeg_utils.get_muxers` to list the
173
+ multiplexers available in the current environment.
174
+
175
+ For device access, the available values vary based on hardware (AV device) and
176
+ software configuration (ffmpeg build).
177
+ Please refer to the ffmpeg documentations for possible values.
178
+
179
+ https://ffmpeg.org/ffmpeg-devices.html#Output-Devices
180
+
181
+ Please use :py:func:`~torio.utils.ffmpeg_utils.get_output_devices` to list
182
+ the output devices available in the current environment.
183
+
184
+ buffer_size (int):
185
+ The internal buffer size in byte. Used only when `dst` is a file-like object.
186
+
187
+ Default: `4096`.
188
+ """
189
+
190
+ def __init__(
191
+ self,
192
+ dst: Union[str, Path, BinaryIO],
193
+ format: Optional[str] = None,
194
+ buffer_size: int = 4096,
195
+ ):
196
+ if hasattr(dst, "write"):
197
+ self._s = ffmpeg_ext.StreamingMediaEncoderFileObj(dst, format, buffer_size)
198
+ else:
199
+ self._s = ffmpeg_ext.StreamingMediaEncoder(str(dst), format)
200
+ self._is_open = False
201
+
202
+ @_format_common_args
203
+ def add_audio_stream(
204
+ self,
205
+ sample_rate: int,
206
+ num_channels: int,
207
+ format: str = "flt",
208
+ *,
209
+ encoder: Optional[str] = None,
210
+ encoder_option: Optional[Dict[str, str]] = None,
211
+ encoder_sample_rate: Optional[int] = None,
212
+ encoder_num_channels: Optional[int] = None,
213
+ encoder_format: Optional[str] = None,
214
+ codec_config: Optional[CodecConfig] = None,
215
+ filter_desc: Optional[str] = None,
216
+ ):
217
+ """Add an output audio stream.
218
+
219
+ Args:
220
+ sample_rate (int): The sample rate.
221
+
222
+ num_channels (int): The number of channels.
223
+
224
+ format (str, optional): Input sample format, which determines the dtype
225
+ of the input tensor.
226
+
227
+ - ``"u8"``: The input tensor must be ``torch.uint8`` type.
228
+ - ``"s16"``: The input tensor must be ``torch.int16`` type.
229
+ - ``"s32"``: The input tensor must be ``torch.int32`` type.
230
+ - ``"s64"``: The input tensor must be ``torch.int64`` type.
231
+ - ``"flt"``: The input tensor must be ``torch.float32`` type.
232
+ - ``"dbl"``: The input tensor must be ``torch.float64`` type.
233
+
234
+ Default: ``"flt"``.
235
+
236
+ encoder (str or None, optional): {encoder}
237
+
238
+ encoder_option (dict or None, optional): {encoder_option}
239
+
240
+ encoder_sample_rate (int or None, optional): Override the sample rate used for encoding time.
241
+ Some encoders pose restriction on the sample rate used for encoding.
242
+ If the source sample rate is not supported by the encoder, the source sample rate is used,
243
+ otherwise a default one is picked.
244
+
245
+ For example, ``"opus"`` encoder only supports 48k Hz, so, when encoding a
246
+ waveform with ``"opus"`` encoder, it is always encoded as 48k Hz.
247
+ Meanwhile ``"mp3"`` (``"libmp3lame"``) supports 44.1k, 48k, 32k, 22.05k,
248
+ 24k, 16k, 11.025k, 12k and 8k Hz.
249
+ If the original sample rate is one of these, then the original sample rate
250
+ is used, otherwise it will be resampled to a default one (44.1k).
251
+ When encoding into WAV format, there is no restriction on sample rate,
252
+ so the original sample rate will be used.
253
+
254
+ Providing ``encoder_sample_rate`` will override this behavior and
255
+ make encoder attempt to use the provided sample rate.
256
+ The provided value must be one support by the encoder.
257
+
258
+ encoder_num_channels (int or None, optional): Override the number of channels used for encoding.
259
+
260
+ Similar to sample rate, some encoders (such as ``"opus"``,
261
+ ``"vorbis"`` and ``"g722"``) pose restriction on
262
+ the numbe of channels that can be used for encoding.
263
+
264
+ If the original number of channels is supported by encoder,
265
+ then it will be used, otherwise, the encoder attempts to
266
+ remix the channel to one of the supported ones.
267
+
268
+ Providing ``encoder_num_channels`` will override this behavior and
269
+ make encoder attempt to use the provided number of channels.
270
+ The provided value must be one support by the encoder.
271
+
272
+ encoder_format (str or None, optional): {encoder_format}
273
+
274
+ codec_config (CodecConfig or None, optional): {codec_config}
275
+
276
+ filter_desc (str or None, optional): {filter_desc}
277
+ """
278
+ self._s.add_audio_stream(
279
+ sample_rate,
280
+ num_channels,
281
+ format,
282
+ encoder,
283
+ encoder_option,
284
+ encoder_format,
285
+ encoder_sample_rate,
286
+ encoder_num_channels,
287
+ _convert_config(codec_config),
288
+ filter_desc,
289
+ )
290
+
291
+ @_format_common_args
292
+ def add_video_stream(
293
+ self,
294
+ frame_rate: float,
295
+ width: int,
296
+ height: int,
297
+ format: str = "rgb24",
298
+ *,
299
+ encoder: Optional[str] = None,
300
+ encoder_option: Optional[Dict[str, str]] = None,
301
+ encoder_frame_rate: Optional[float] = None,
302
+ encoder_width: Optional[int] = None,
303
+ encoder_height: Optional[int] = None,
304
+ encoder_format: Optional[str] = None,
305
+ codec_config: Optional[CodecConfig] = None,
306
+ filter_desc: Optional[str] = None,
307
+ hw_accel: Optional[str] = None,
308
+ ):
309
+ """Add an output video stream.
310
+
311
+ This method has to be called before `open` is called.
312
+
313
+ Args:
314
+ frame_rate (float): Frame rate of the video.
315
+
316
+ width (int): Width of the video frame.
317
+
318
+ height (int): Height of the video frame.
319
+
320
+ format (str, optional): Input pixel format, which determines the
321
+ color channel order of the input tensor.
322
+
323
+ - ``"gray8"``: One channel, grayscale.
324
+ - ``"rgb24"``: Three channels in the order of RGB.
325
+ - ``"bgr24"``: Three channels in the order of BGR.
326
+ - ``"yuv444p"``: Three channels in the order of YUV.
327
+
328
+ Default: ``"rgb24"``.
329
+
330
+ In either case, the input tensor has to be ``torch.uint8`` type and
331
+ the shape must be (frame, channel, height, width).
332
+
333
+ encoder (str or None, optional): {encoder}
334
+
335
+ encoder_option (dict or None, optional): {encoder_option}
336
+
337
+ encoder_frame_rate (float or None, optional): Override the frame rate used for encoding.
338
+
339
+ Some encoders, (such as ``"mpeg1"`` and ``"mpeg2"``) pose restriction on the
340
+ frame rate that can be used for encoding.
341
+ If such case, if the source frame rate (provided as ``frame_rate``) is not
342
+ one of the supported frame rate, then a default one is picked, and the frame rate
343
+ is changed on-the-fly. Otherwise the source frame rate is used.
344
+
345
+ Providing ``encoder_frame_rate`` will override this behavior and
346
+ make encoder attempts to use the provided sample rate.
347
+ The provided value must be one support by the encoder.
348
+
349
+ encoder_width (int or None, optional): Width of the image used for encoding.
350
+ This allows to change the image size during encoding.
351
+
352
+ encoder_height (int or None, optional): Height of the image used for encoding.
353
+ This allows to change the image size during encoding.
354
+
355
+ encoder_format (str or None, optional): {encoder_format}
356
+
357
+ codec_config (CodecConfig or None, optional): {codec_config}
358
+
359
+ filter_desc (str or None, optional): {filter_desc}
360
+
361
+ hw_accel (str or None, optional): Enable hardware acceleration.
362
+
363
+ When video is encoded on CUDA hardware, for example
364
+ `encoder="h264_nvenc"`, passing CUDA device indicator to `hw_accel`
365
+ (i.e. `hw_accel="cuda:0"`) will make StreamingMediaEncoder expect video
366
+ chunk to be CUDA Tensor. Passing CPU Tensor will result in an error.
367
+
368
+ If `None`, the video chunk Tensor has to be CPU Tensor.
369
+ Default: ``None``.
370
+ """
371
+ self._s.add_video_stream(
372
+ frame_rate,
373
+ width,
374
+ height,
375
+ format,
376
+ encoder,
377
+ encoder_option,
378
+ encoder_format,
379
+ encoder_frame_rate,
380
+ encoder_width,
381
+ encoder_height,
382
+ hw_accel,
383
+ _convert_config(codec_config),
384
+ filter_desc,
385
+ )
386
+
387
+ def set_metadata(self, metadata: Dict[str, str]):
388
+ """Set file-level metadata
389
+
390
+ Args:
391
+ metadata (dict or None, optional): File-level metadata.
392
+ """
393
+ self._s.set_metadata(metadata)
394
+
395
+ def _print_output_stream(self, i: int):
396
+ """[debug] Print the registered stream information to stdout."""
397
+ self._s.dump_format(i)
398
+
399
+ def open(self, option: Optional[Dict[str, str]] = None) -> "StreamingMediaEncoder":
400
+ """Open the output file / device and write the header.
401
+
402
+ :py:class:`StreamingMediaEncoder` is also a context manager and therefore supports the
403
+ ``with`` statement.
404
+ This method returns the instance on which the method is called (i.e. `self`),
405
+ so that it can be used in `with` statement.
406
+ It is recommended to use context manager, as the file is closed automatically
407
+ when exiting from ``with`` clause.
408
+
409
+ Args:
410
+ option (dict or None, optional): Private options for protocol, device and muxer. See example.
411
+
412
+ Example - Protocol option
413
+ >>> s = StreamingMediaEncoder(dst="rtmp://localhost:1234/live/app", format="flv")
414
+ >>> s.add_video_stream(...)
415
+ >>> # Passing protocol option `listen=1` makes StreamingMediaEncoder act as RTMP server.
416
+ >>> with s.open(option={"listen": "1"}) as f:
417
+ >>> f.write_video_chunk(...)
418
+
419
+ Example - Device option
420
+ >>> s = StreamingMediaEncoder("-", format="sdl")
421
+ >>> s.add_video_stream(..., encoder_format="rgb24")
422
+ >>> # Open SDL video player with fullscreen
423
+ >>> with s.open(option={"window_fullscreen": "1"}):
424
+ >>> f.write_video_chunk(...)
425
+
426
+ Example - Muxer option
427
+ >>> s = StreamingMediaEncoder("foo.flac")
428
+ >>> s.add_audio_stream(...)
429
+ >>> s.set_metadata({"artist": "torio contributors"})
430
+ >>> # FLAC muxer has a private option to not write the header.
431
+ >>> # The resulting file does not contain the above metadata.
432
+ >>> with s.open(option={"write_header": "false"}) as f:
433
+ >>> f.write_audio_chunk(...)
434
+ """
435
+ if not self._is_open:
436
+ self._s.open(option)
437
+ self._is_open = True
438
+ return self
439
+
440
+ def close(self):
441
+ """Close the output
442
+
443
+ :py:class:`StreamingMediaEncoder` is also a context manager and therefore supports the
444
+ ``with`` statement.
445
+ It is recommended to use context manager, as the file is closed automatically
446
+ when exiting from ``with`` clause.
447
+
448
+ See :py:meth:`StreamingMediaEncoder.open` for more detail.
449
+ """
450
+ if self._is_open:
451
+ self._s.close()
452
+ self._is_open = False
453
+
454
+ def write_audio_chunk(self, i: int, chunk: torch.Tensor, pts: Optional[float] = None):
455
+ """Write audio data
456
+
457
+ Args:
458
+ i (int): Stream index.
459
+ chunk (Tensor): Waveform tensor. Shape: `(frame, channel)`.
460
+ The ``dtype`` must match what was passed to :py:meth:`add_audio_stream` method.
461
+ pts (float, optional, or None): If provided, overwrite the presentation timestamp.
462
+
463
+ .. note::
464
+
465
+ The provided value is converted to integer value expressed in basis of
466
+ sample rate. Therefore, it is truncated to the nearest value of
467
+ ``n / sample_rate``.
468
+ """
469
+ self._s.write_audio_chunk(i, chunk, pts)
470
+
471
+ def write_video_chunk(self, i: int, chunk: torch.Tensor, pts: Optional[float] = None):
472
+ """Write video/image data
473
+
474
+ Args:
475
+ i (int): Stream index.
476
+ chunk (Tensor): Video/image tensor.
477
+ Shape: `(time, channel, height, width)`.
478
+ The ``dtype`` must be ``torch.uint8``.
479
+ The shape (height, width and the number of channels) must match
480
+ what was configured when calling :py:meth:`add_video_stream`
481
+ pts (float, optional or None): If provided, overwrite the presentation timestamp.
482
+
483
+ .. note::
484
+
485
+ The provided value is converted to integer value expressed in basis of
486
+ frame rate. Therefore, it is truncated to the nearest value of
487
+ ``n / frame_rate``.
488
+ """
489
+ self._s.write_video_chunk(i, chunk, pts)
490
+
491
+ def flush(self):
492
+ """Flush the frames from encoders and write the frames to the destination."""
493
+ self._s.flush()
494
+
495
+ def __enter__(self):
496
+ """Context manager so that the destination is closed and data are flushed automatically."""
497
+ return self
498
+
499
+ def __exit__(self, exception_type, exception_value, traceback):
500
+ """Context manager so that the destination is closed and data are flushed automatically."""
501
+ self.flush()
502
+ self.close()
MLPY/Lib/site-packages/torio/lib/__init__.py ADDED
File without changes
MLPY/Lib/site-packages/torio/lib/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (146 Bytes). View file
 
MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg4.pyd ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:144cdcf7ee3bf60d2589dccd6a17a127218ae38a4c99b2ab25519e9c46448090
3
+ size 1710080
MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg5.pyd ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c8a912dc0691b65075b20ebbdd517946b52da49600fb47ccd31fe7d6a2ea464c
3
+ size 1710080
MLPY/Lib/site-packages/torio/lib/_torio_ffmpeg6.pyd ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a54d9a42baf649f5d759862f499e3c3863b634e2b44d957a31fae39092f03387
3
+ size 1710080
MLPY/Lib/site-packages/torio/lib/libtorio_ffmpeg4.pyd ADDED
Binary file (964 kB). View file
 
MLPY/Lib/site-packages/torio/lib/libtorio_ffmpeg5.pyd ADDED
Binary file (964 kB). View file
 
MLPY/Lib/site-packages/torio/lib/libtorio_ffmpeg6.pyd ADDED
Binary file (964 kB). View file
 
MLPY/Lib/site-packages/torio/utils/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from . import ffmpeg_utils
2
+
3
+
4
+ __all__ = ["ffmpeg_utils"]
MLPY/Lib/site-packages/torio/utils/__pycache__/__init__.cpython-39.pyc ADDED
Binary file (207 Bytes). View file
 
MLPY/Lib/site-packages/torio/utils/__pycache__/ffmpeg_utils.cpython-39.pyc ADDED
Binary file (8.93 kB). View file
 
MLPY/Lib/site-packages/torio/utils/ffmpeg_utils.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Module to change the configuration of FFmpeg libraries (such as libavformat).
2
+
3
+ It affects functionalities in :py:mod:`torio.io`.
4
+ """
5
+ from typing import Dict, List, Tuple
6
+
7
+ import torio
8
+
9
+ ffmpeg_ext = torio._extension.lazy_import_ffmpeg_ext()
10
+
11
+
12
+ def get_versions() -> Dict[str, Tuple[int]]:
13
+ """Get the versions of FFmpeg libraries
14
+
15
+ Returns:
16
+ dict: mapping from library names to version string,
17
+ i.e. `"libavutil": (56, 22, 100)`.
18
+ """
19
+ return ffmpeg_ext.get_versions()
20
+
21
+
22
+ def get_log_level() -> int:
23
+ """Get the log level of FFmpeg.
24
+
25
+ See :py:func:`set_log_level` for the detail.
26
+ """
27
+ return ffmpeg_ext.get_log_level()
28
+
29
+
30
+ def set_log_level(level: int):
31
+ """Set the log level of FFmpeg (libavformat etc)
32
+
33
+ Arguments:
34
+ level (int): Log level. The larger, the more verbose.
35
+
36
+ The following values are common values, the corresponding ``ffmpeg``'s
37
+ ``-loglevel`` option value and desription.
38
+
39
+ * ``-8`` (``quiet``):
40
+ Print no output.
41
+ * ``0`` (``panic``):
42
+ Something went really wrong and we will crash now.
43
+ * ``8`` (``fatal``):
44
+ Something went wrong and recovery is not possible.
45
+ For example, no header was found for a format which depends
46
+ on headers or an illegal combination of parameters is used.
47
+ * ``16`` (``error``):
48
+ Something went wrong and cannot losslessly be recovered.
49
+ However, not all future data is affected.
50
+ * ``24`` (``warning``):
51
+ Something somehow does not look correct.
52
+ This may or may not lead to problems.
53
+ * ``32`` (``info``):
54
+ Standard information.
55
+ * ``40`` (``verbose``):
56
+ Detailed information.
57
+ * ``48`` (``debug``):
58
+ Stuff which is only useful for libav* developers.
59
+ * ``56`` (``trace``):
60
+ Extremely verbose debugging, useful for libav* development.
61
+
62
+ """
63
+ ffmpeg_ext.set_log_level(level)
64
+
65
+
66
+ def get_demuxers() -> Dict[str, str]:
67
+ """Get the available demuxers.
68
+
69
+ Returns:
70
+ Dict[str, str]: Mapping from demuxer (format) short name to long name.
71
+
72
+ Example
73
+ >>> for k, v in get_demuxers().items():
74
+ >>> print(f"{k}: {v}")
75
+ ... aa: Audible AA format files
76
+ ... aac: raw ADTS AAC (Advanced Audio Coding)
77
+ ... aax: CRI AAX
78
+ ... ac3: raw AC-3
79
+ """
80
+ return ffmpeg_ext.get_demuxers()
81
+
82
+
83
+ def get_muxers() -> Dict[str, str]:
84
+ """Get the available muxers.
85
+
86
+ Returns:
87
+ Dict[str, str]: Mapping from muxer (format) short name to long name.
88
+
89
+ Example
90
+ >>> for k, v in get_muxers().items():
91
+ >>> print(f"{k}: {v}")
92
+ ... a64: a64 - video for Commodore 64
93
+ ... ac3: raw AC-3
94
+ ... adts: ADTS AAC (Advanced Audio Coding)
95
+ ... adx: CRI ADX
96
+ ... aiff: Audio IFF
97
+ """
98
+ return ffmpeg_ext.get_muxers()
99
+
100
+
101
+ def get_audio_decoders() -> Dict[str, str]:
102
+ """Get the available audio decoders.
103
+
104
+ Returns:
105
+ Dict[str, str]: Mapping from decoder short name to long name.
106
+
107
+ Example
108
+ >>> for k, v in get_audio_decoders().items():
109
+ >>> print(f"{k}: {v}")
110
+ ... a64: a64 - video for Commodore 64
111
+ ... ac3: raw AC-3
112
+ ... adts: ADTS AAC (Advanced Audio Coding)
113
+ ... adx: CRI ADX
114
+ ... aiff: Audio IFF
115
+ """
116
+ return ffmpeg_ext.get_audio_decoders()
117
+
118
+
119
+ def get_audio_encoders() -> Dict[str, str]:
120
+ """Get the available audio encoders.
121
+
122
+ Returns:
123
+ Dict[str, str]: Mapping from encoder short name to long name.
124
+
125
+ Example
126
+ >>> for k, v in get_audio_encoders().items():
127
+ >>> print(f"{k}: {v}")
128
+ ... comfortnoise: RFC 3389 comfort noise generator
129
+ ... s302m: SMPTE 302M
130
+ ... aac: AAC (Advanced Audio Coding)
131
+ ... ac3: ATSC A/52A (AC-3)
132
+ ... ac3_fixed: ATSC A/52A (AC-3)
133
+ ... alac: ALAC (Apple Lossless Audio Codec)
134
+ """
135
+ return ffmpeg_ext.get_audio_encoders()
136
+
137
+
138
+ def get_video_decoders() -> Dict[str, str]:
139
+ """Get the available video decoders.
140
+
141
+ Returns:
142
+ Dict[str, str]: Mapping from decoder short name to long name.
143
+
144
+ Example
145
+ >>> for k, v in get_video_decoders().items():
146
+ >>> print(f"{k}: {v}")
147
+ ... aasc: Autodesk RLE
148
+ ... aic: Apple Intermediate Codec
149
+ ... alias_pix: Alias/Wavefront PIX image
150
+ ... agm: Amuse Graphics Movie
151
+ ... amv: AMV Video
152
+ ... anm: Deluxe Paint Animation
153
+ """
154
+ return ffmpeg_ext.get_video_decoders()
155
+
156
+
157
+ def get_video_encoders() -> Dict[str, str]:
158
+ """Get the available video encoders.
159
+
160
+ Returns:
161
+ Dict[str, str]: Mapping from encoder short name to long name.
162
+
163
+ Example
164
+ >>> for k, v in get_audio_encoders().items():
165
+ >>> print(f"{k}: {v}")
166
+ ... a64multi: Multicolor charset for Commodore 64
167
+ ... a64multi5: Multicolor charset for Commodore 64, extended with 5th color (colram)
168
+ ... alias_pix: Alias/Wavefront PIX image
169
+ ... amv: AMV Video
170
+ ... apng: APNG (Animated Portable Network Graphics) image
171
+ ... asv1: ASUS V1
172
+ ... asv2: ASUS V2
173
+ """
174
+ return ffmpeg_ext.get_video_encoders()
175
+
176
+
177
+ def get_input_devices() -> Dict[str, str]:
178
+ """Get the available input devices.
179
+
180
+ Returns:
181
+ Dict[str, str]: Mapping from device short name to long name.
182
+
183
+ Example
184
+ >>> for k, v in get_input_devices().items():
185
+ >>> print(f"{k}: {v}")
186
+ ... avfoundation: AVFoundation input device
187
+ ... lavfi: Libavfilter virtual input device
188
+ """
189
+ return ffmpeg_ext.get_input_devices()
190
+
191
+
192
+ def get_output_devices() -> Dict[str, str]:
193
+ """Get the available output devices.
194
+
195
+ Returns:
196
+ Dict[str, str]: Mapping from device short name to long name.
197
+
198
+ Example
199
+ >>> for k, v in get_output_devices().items():
200
+ >>> print(f"{k}: {v}")
201
+ ... audiotoolbox: AudioToolbox output device
202
+ """
203
+ return ffmpeg_ext.get_output_devices()
204
+
205
+
206
+ def get_input_protocols() -> List[str]:
207
+ """Get the supported input protocols.
208
+
209
+ Returns:
210
+ List[str]: The names of supported input protocols
211
+
212
+ Example
213
+ >>> print(get_input_protocols())
214
+ ... ['file', 'ftp', 'hls', 'http','https', 'pipe', 'rtmp', 'tcp', 'tls', 'udp', 'unix']
215
+ """
216
+ return ffmpeg_ext.get_input_protocols()
217
+
218
+
219
+ def get_output_protocols() -> List[str]:
220
+ """Get the supported output protocols.
221
+
222
+ Returns:
223
+ list of str: The names of supported output protocols
224
+
225
+ Example
226
+ >>> print(get_output_protocols())
227
+ ... ['file', 'ftp', 'http', 'https', 'md5', 'pipe', 'prompeg', 'rtmp', 'tee', 'tcp', 'tls', 'udp', 'unix']
228
+ """
229
+ return ffmpeg_ext.get_output_protocols()
230
+
231
+
232
+ def get_build_config() -> str:
233
+ """Get the FFmpeg build configuration
234
+
235
+ Returns:
236
+ str: Build configuration string.
237
+
238
+ Example
239
+ >>> print(get_build_config())
240
+ --prefix=/Users/runner/miniforge3 --cc=arm64-apple-darwin20.0.0-clang --enable-gpl --enable-hardcoded-tables --enable-libfreetype --enable-libopenh264 --enable-neon --enable-libx264 --enable-libx265 --enable-libaom --enable-libsvtav1 --enable-libxml2 --enable-libvpx --enable-pic --enable-pthreads --enable-shared --disable-static --enable-version3 --enable-zlib --enable-libmp3lame --pkg-config=/Users/runner/miniforge3/conda-bld/ffmpeg_1646229390493/_build_env/bin/pkg-config --enable-cross-compile --arch=arm64 --target-os=darwin --cross-prefix=arm64-apple-darwin20.0.0- --host-cc=/Users/runner/miniforge3/conda-bld/ffmpeg_1646229390493/_build_env/bin/x86_64-apple-darwin13.4.0-clang # noqa
241
+ """
242
+ return ffmpeg_ext.get_build_config()
243
+
244
+
245
+ def clear_cuda_context_cache():
246
+ """Clear the CUDA context used by CUDA Hardware accelerated video decoding"""
247
+ ffmpeg_ext.clear_cuda_context_cache()
MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/LICENSE ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ A. HISTORY OF THE SOFTWARE
2
+ ==========================
3
+
4
+ Python was created in the early 1990s by Guido van Rossum at Stichting
5
+ Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
6
+ as a successor of a language called ABC. Guido remains Python's
7
+ principal author, although it includes many contributions from others.
8
+
9
+ In 1995, Guido continued his work on Python at the Corporation for
10
+ National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
11
+ in Reston, Virginia where he released several versions of the
12
+ software.
13
+
14
+ In May 2000, Guido and the Python core development team moved to
15
+ BeOpen.com to form the BeOpen PythonLabs team. In October of the same
16
+ year, the PythonLabs team moved to Digital Creations, which became
17
+ Zope Corporation. In 2001, the Python Software Foundation (PSF, see
18
+ https://www.python.org/psf/) was formed, a non-profit organization
19
+ created specifically to own Python-related Intellectual Property.
20
+ Zope Corporation was a sponsoring member of the PSF.
21
+
22
+ All Python releases are Open Source (see https://opensource.org for
23
+ the Open Source Definition). Historically, most, but not all, Python
24
+ releases have also been GPL-compatible; the table below summarizes
25
+ the various releases.
26
+
27
+ Release Derived Year Owner GPL-
28
+ from compatible? (1)
29
+
30
+ 0.9.0 thru 1.2 1991-1995 CWI yes
31
+ 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
32
+ 1.6 1.5.2 2000 CNRI no
33
+ 2.0 1.6 2000 BeOpen.com no
34
+ 1.6.1 1.6 2001 CNRI yes (2)
35
+ 2.1 2.0+1.6.1 2001 PSF no
36
+ 2.0.1 2.0+1.6.1 2001 PSF yes
37
+ 2.1.1 2.1+2.0.1 2001 PSF yes
38
+ 2.1.2 2.1.1 2002 PSF yes
39
+ 2.1.3 2.1.2 2002 PSF yes
40
+ 2.2 and above 2.1.1 2001-now PSF yes
41
+
42
+ Footnotes:
43
+
44
+ (1) GPL-compatible doesn't mean that we're distributing Python under
45
+ the GPL. All Python licenses, unlike the GPL, let you distribute
46
+ a modified version without making your changes open source. The
47
+ GPL-compatible licenses make it possible to combine Python with
48
+ other software that is released under the GPL; the others don't.
49
+
50
+ (2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
51
+ because its license has a choice of law clause. According to
52
+ CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
53
+ is "not incompatible" with the GPL.
54
+
55
+ Thanks to the many outside volunteers who have worked under Guido's
56
+ direction to make these releases possible.
57
+
58
+
59
+ B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
60
+ ===============================================================
61
+
62
+ Python software and documentation are licensed under the
63
+ Python Software Foundation License Version 2.
64
+
65
+ Starting with Python 3.8.6, examples, recipes, and other code in
66
+ the documentation are dual licensed under the PSF License Version 2
67
+ and the Zero-Clause BSD license.
68
+
69
+ Some software incorporated into Python is under different licenses.
70
+ The licenses are listed with code falling under that license.
71
+
72
+
73
+ PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
74
+ --------------------------------------------
75
+
76
+ 1. This LICENSE AGREEMENT is between the Python Software Foundation
77
+ ("PSF"), and the Individual or Organization ("Licensee") accessing and
78
+ otherwise using this software ("Python") in source or binary form and
79
+ its associated documentation.
80
+
81
+ 2. Subject to the terms and conditions of this License Agreement, PSF hereby
82
+ grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
83
+ analyze, test, perform and/or display publicly, prepare derivative works,
84
+ distribute, and otherwise use Python alone or in any derivative version,
85
+ provided, however, that PSF's License Agreement and PSF's notice of copyright,
86
+ i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
87
+ 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
88
+ All Rights Reserved" are retained in Python alone or in any derivative version
89
+ prepared by Licensee.
90
+
91
+ 3. In the event Licensee prepares a derivative work that is based on
92
+ or incorporates Python or any part thereof, and wants to make
93
+ the derivative work available to others as provided herein, then
94
+ Licensee hereby agrees to include in any such work a brief summary of
95
+ the changes made to Python.
96
+
97
+ 4. PSF is making Python available to Licensee on an "AS IS"
98
+ basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
99
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
100
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
101
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
102
+ INFRINGE ANY THIRD PARTY RIGHTS.
103
+
104
+ 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
105
+ FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
106
+ A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
107
+ OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
108
+
109
+ 6. This License Agreement will automatically terminate upon a material
110
+ breach of its terms and conditions.
111
+
112
+ 7. Nothing in this License Agreement shall be deemed to create any
113
+ relationship of agency, partnership, or joint venture between PSF and
114
+ Licensee. This License Agreement does not grant permission to use PSF
115
+ trademarks or trade name in a trademark sense to endorse or promote
116
+ products or services of Licensee, or any third party.
117
+
118
+ 8. By copying, installing or otherwise using Python, Licensee
119
+ agrees to be bound by the terms and conditions of this License
120
+ Agreement.
121
+
122
+
123
+ BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
124
+ -------------------------------------------
125
+
126
+ BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
127
+
128
+ 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
129
+ office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
130
+ Individual or Organization ("Licensee") accessing and otherwise using
131
+ this software in source or binary form and its associated
132
+ documentation ("the Software").
133
+
134
+ 2. Subject to the terms and conditions of this BeOpen Python License
135
+ Agreement, BeOpen hereby grants Licensee a non-exclusive,
136
+ royalty-free, world-wide license to reproduce, analyze, test, perform
137
+ and/or display publicly, prepare derivative works, distribute, and
138
+ otherwise use the Software alone or in any derivative version,
139
+ provided, however, that the BeOpen Python License is retained in the
140
+ Software, alone or in any derivative version prepared by Licensee.
141
+
142
+ 3. BeOpen is making the Software available to Licensee on an "AS IS"
143
+ basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
144
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
145
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
146
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
147
+ INFRINGE ANY THIRD PARTY RIGHTS.
148
+
149
+ 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
150
+ SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
151
+ AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
152
+ DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
153
+
154
+ 5. This License Agreement will automatically terminate upon a material
155
+ breach of its terms and conditions.
156
+
157
+ 6. This License Agreement shall be governed by and interpreted in all
158
+ respects by the law of the State of California, excluding conflict of
159
+ law provisions. Nothing in this License Agreement shall be deemed to
160
+ create any relationship of agency, partnership, or joint venture
161
+ between BeOpen and Licensee. This License Agreement does not grant
162
+ permission to use BeOpen trademarks or trade names in a trademark
163
+ sense to endorse or promote products or services of Licensee, or any
164
+ third party. As an exception, the "BeOpen Python" logos available at
165
+ http://www.pythonlabs.com/logos.html may be used according to the
166
+ permissions granted on that web page.
167
+
168
+ 7. By copying, installing or otherwise using the software, Licensee
169
+ agrees to be bound by the terms and conditions of this License
170
+ Agreement.
171
+
172
+
173
+ CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
174
+ ---------------------------------------
175
+
176
+ 1. This LICENSE AGREEMENT is between the Corporation for National
177
+ Research Initiatives, having an office at 1895 Preston White Drive,
178
+ Reston, VA 20191 ("CNRI"), and the Individual or Organization
179
+ ("Licensee") accessing and otherwise using Python 1.6.1 software in
180
+ source or binary form and its associated documentation.
181
+
182
+ 2. Subject to the terms and conditions of this License Agreement, CNRI
183
+ hereby grants Licensee a nonexclusive, royalty-free, world-wide
184
+ license to reproduce, analyze, test, perform and/or display publicly,
185
+ prepare derivative works, distribute, and otherwise use Python 1.6.1
186
+ alone or in any derivative version, provided, however, that CNRI's
187
+ License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
188
+ 1995-2001 Corporation for National Research Initiatives; All Rights
189
+ Reserved" are retained in Python 1.6.1 alone or in any derivative
190
+ version prepared by Licensee. Alternately, in lieu of CNRI's License
191
+ Agreement, Licensee may substitute the following text (omitting the
192
+ quotes): "Python 1.6.1 is made available subject to the terms and
193
+ conditions in CNRI's License Agreement. This Agreement together with
194
+ Python 1.6.1 may be located on the internet using the following
195
+ unique, persistent identifier (known as a handle): 1895.22/1013. This
196
+ Agreement may also be obtained from a proxy server on the internet
197
+ using the following URL: http://hdl.handle.net/1895.22/1013".
198
+
199
+ 3. In the event Licensee prepares a derivative work that is based on
200
+ or incorporates Python 1.6.1 or any part thereof, and wants to make
201
+ the derivative work available to others as provided herein, then
202
+ Licensee hereby agrees to include in any such work a brief summary of
203
+ the changes made to Python 1.6.1.
204
+
205
+ 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
206
+ basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
207
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
208
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
209
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
210
+ INFRINGE ANY THIRD PARTY RIGHTS.
211
+
212
+ 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
213
+ 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
214
+ A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
215
+ OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
216
+
217
+ 6. This License Agreement will automatically terminate upon a material
218
+ breach of its terms and conditions.
219
+
220
+ 7. This License Agreement shall be governed by the federal
221
+ intellectual property law of the United States, including without
222
+ limitation the federal copyright law, and, to the extent such
223
+ U.S. federal law does not apply, by the law of the Commonwealth of
224
+ Virginia, excluding Virginia's conflict of law provisions.
225
+ Notwithstanding the foregoing, with regard to derivative works based
226
+ on Python 1.6.1 that incorporate non-separable material that was
227
+ previously distributed under the GNU General Public License (GPL), the
228
+ law of the Commonwealth of Virginia shall govern this License
229
+ Agreement only as to issues arising under or with respect to
230
+ Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
231
+ License Agreement shall be deemed to create any relationship of
232
+ agency, partnership, or joint venture between CNRI and Licensee. This
233
+ License Agreement does not grant permission to use CNRI trademarks or
234
+ trade name in a trademark sense to endorse or promote products or
235
+ services of Licensee, or any third party.
236
+
237
+ 8. By clicking on the "ACCEPT" button where indicated, or by copying,
238
+ installing or otherwise using Python 1.6.1, Licensee agrees to be
239
+ bound by the terms and conditions of this License Agreement.
240
+
241
+ ACCEPT
242
+
243
+
244
+ CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
245
+ --------------------------------------------------
246
+
247
+ Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
248
+ The Netherlands. All rights reserved.
249
+
250
+ Permission to use, copy, modify, and distribute this software and its
251
+ documentation for any purpose and without fee is hereby granted,
252
+ provided that the above copyright notice appear in all copies and that
253
+ both that copyright notice and this permission notice appear in
254
+ supporting documentation, and that the name of Stichting Mathematisch
255
+ Centrum or CWI not be used in advertising or publicity pertaining to
256
+ distribution of the software without specific, written prior
257
+ permission.
258
+
259
+ STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
260
+ THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
261
+ FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
262
+ FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
263
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
264
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
265
+ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
266
+
267
+ ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
268
+ ----------------------------------------------------------------------
269
+
270
+ Permission to use, copy, modify, and/or distribute this software for any
271
+ purpose with or without fee is hereby granted.
272
+
273
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
274
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
275
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
276
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
277
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
278
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
279
+ PERFORMANCE OF THIS SOFTWARE.
MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/METADATA ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: typing_extensions
3
+ Version: 4.12.2
4
+ Summary: Backported and Experimental Type Hints for Python 3.8+
5
+ Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing
6
+ Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" <[email protected]>
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Python Software Foundation License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development
23
+ Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues
24
+ Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md
25
+ Project-URL: Documentation, https://typing-extensions.readthedocs.io/
26
+ Project-URL: Home, https://github.com/python/typing_extensions
27
+ Project-URL: Q & A, https://github.com/python/typing/discussions
28
+ Project-URL: Repository, https://github.com/python/typing_extensions
29
+
30
+ # Typing Extensions
31
+
32
+ [![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)
33
+
34
+ [Documentation](https://typing-extensions.readthedocs.io/en/latest/#) –
35
+ [PyPI](https://pypi.org/project/typing-extensions/)
36
+
37
+ ## Overview
38
+
39
+ The `typing_extensions` module serves two related purposes:
40
+
41
+ - Enable use of new type system features on older Python versions. For example,
42
+ `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows
43
+ users on previous Python versions to use it too.
44
+ - Enable experimentation with new type system PEPs before they are accepted and
45
+ added to the `typing` module.
46
+
47
+ `typing_extensions` is treated specially by static type checkers such as
48
+ mypy and pyright. Objects defined in `typing_extensions` are treated the same
49
+ way as equivalent forms in `typing`.
50
+
51
+ `typing_extensions` uses
52
+ [Semantic Versioning](https://semver.org/). The
53
+ major version will be incremented only for backwards-incompatible changes.
54
+ Therefore, it's safe to depend
55
+ on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,
56
+ where `x.y` is the first version that includes all features you need.
57
+
58
+ ## Included items
59
+
60
+ See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a
61
+ complete listing of module contents.
62
+
63
+ ## Contributing
64
+
65
+ See [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)
66
+ for how to contribute to `typing_extensions`.
67
+
MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/RECORD ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ __pycache__/typing_extensions.cpython-39.pyc,,
2
+ typing_extensions-4.12.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
3
+ typing_extensions-4.12.2.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
4
+ typing_extensions-4.12.2.dist-info/METADATA,sha256=BeUQIa8cnYbrjWx-N8TOznM9UGW5Gm2DicVpDtRA8W0,3018
5
+ typing_extensions-4.12.2.dist-info/RECORD,,
6
+ typing_extensions-4.12.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
7
+ typing_extensions.py,sha256=gwekpyG9DVG3lxWKX4ni8u7nk3We5slG98mA9F3DJQw,134451
MLPY/Lib/site-packages/typing_extensions-4.12.2.dist-info/WHEEL ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: flit 3.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any