video-stream-data 0.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,177 @@
1
+ """Provide unified meta data definition for video streaming."""
2
+ from dataclasses import asdict, dataclass, field
3
+ from enum import Enum
4
+
5
+
6
+ class StreamControlType(str, Enum):
7
+ """Different configuration types for the stream.
8
+
9
+ Taken from opencv2 device controls.
10
+ """
11
+
12
+ INT = 'int'
13
+ BOOL = 'bool'
14
+ MENU = 'menu'
15
+
16
+ def __str__(self) -> str:
17
+ return self.value
18
+
19
+
20
+ class Encoding(str, Enum):
21
+ """Defines how the video stream gets encodied.
22
+
23
+ Should default to 'passthrough'.
24
+ Normally BGR in ROS, RGB in h264.
25
+ """
26
+
27
+ PASSTHROUGH = 'passthrough'
28
+ BGR = 'bgr'
29
+ GREY = 'grey'
30
+ GREY16 = 'grey16'
31
+ RGB = 'rgb'
32
+ BGRA = 'bgra'
33
+ RGBA = 'rgba'
34
+
35
+ def __str__(self) -> str:
36
+ return self.value
37
+
38
+ def to_ros_bridge(self) -> str:
39
+ """Create string to use with the ROS2 CvBridge."""
40
+ return _ROS_ENCODINGS.get(self, self.value)
41
+
42
+
43
+ _ROS_ENCODINGS = {
44
+ Encoding.BGR: 'bgr8',
45
+ Encoding.RGB: 'rgb8',
46
+ Encoding.BGRA: 'bgra8',
47
+ Encoding.RGBA: 'rgba8',
48
+ Encoding.GREY: 'mono8',
49
+ Encoding.GREY16: 'mono16',
50
+ }
51
+
52
+
53
+ class Codec(str, Enum):
54
+ """Codec type/Pixel format as fourcc."""
55
+
56
+ # see linuxpy/video/raw.py
57
+
58
+ MJPEG = 'MJPEG' # Motion-JPEG
59
+ YUYV = 'YUYV'
60
+ XVID = 'XVID' # MP4 files
61
+ H264 = 'H264'
62
+ GREY = 'GREY'
63
+ UNKNOWN = 'unknown'
64
+
65
+ def __str__(self) -> str:
66
+ return self.value
67
+
68
+ def to_text(self) -> str:
69
+ """Create human readable text."""
70
+ return _CODEC_TEXT.get(self, self.value)
71
+
72
+ def to_fourcc(self) -> str:
73
+ """Create FourCC-Text for configuration of OpenCV2 VideoWriter."""
74
+ if self is Codec.MJPEG:
75
+ return 'MJPG'
76
+ return self.value
77
+
78
+
79
+ _CODEC_TEXT = {
80
+ Codec.MJPEG: 'Motion-JPEG',
81
+ Codec.YUYV: 'YUYV',
82
+ Codec.XVID: 'XVID/MP4',
83
+ }
84
+
85
+
86
+ @dataclass
87
+ class StreamControl:
88
+ """Provide a value the user can configure."""
89
+
90
+ type: StreamControlType # noqa: A003
91
+ name: str
92
+ writable: bool = False
93
+ min: float = 0 # noqa: A003
94
+ max: float = 0 # noqa: A003
95
+ step: int = 1
96
+ value: int | float | bool | None = None
97
+ # (key, label) pairs of a MENU control
98
+ options: list[tuple[str, str]] = field(default_factory=list)
99
+
100
+ def __post_init__(self):
101
+ self.type = StreamControlType(self.type)
102
+ # JSON and YAML have no tuples
103
+ self.options = [tuple(option) for option in self.options]
104
+
105
+
106
+ @dataclass
107
+ class StreamValue:
108
+ """Values requested by the user."""
109
+
110
+ # selected resolution [width, height]
111
+ resolution: list[int]
112
+ # current fps
113
+ fps: float
114
+ # selected control configurations
115
+ controls: dict[str, int | float | bool | None]
116
+ # selected encoding, e.g. bgr
117
+ encoding: Encoding = Encoding.PASSTHROUGH
118
+ # selected codec e.g. MJPEG for "Motion-JPEG" or YUYV for "YUYV 4:2:2"
119
+ codec: Codec = Codec.UNKNOWN
120
+
121
+ def __post_init__(self):
122
+ self.resolution = list(self.resolution)
123
+ self.encoding = Encoding(self.encoding)
124
+ self.codec = Codec(self.codec)
125
+
126
+
127
+ @dataclass
128
+ class StreamInfo:
129
+ """Stream information provided by the device/host."""
130
+
131
+ name: str
132
+ # unique stream path
133
+ stream_path: str
134
+ # current configuration as own data structure so it can easily be handed
135
+ # over as dict from any service
136
+ values: StreamValue
137
+ # driver for example "uvcvideo" for a local OpenCV webcam or
138
+ # "webrtc" for a WebRTC Video Stream
139
+ driver: str = ''
140
+ # available codec
141
+ codec: list[Codec] = field(default_factory=list)
142
+ # all available controls from linuxpy
143
+ controls: list[StreamControl] = field(default_factory=list)
144
+ # all available resolutions [width, height], starting with the default
145
+ resolutions: list[list[int]] = field(default_factory=list)
146
+ # max. possible fps (at default resolution)
147
+ max_fps: float = 30.0
148
+ backend: str = 'v4l2' # (optional) OpenCV2 Backend (only if cv2 is used)
149
+
150
+ def __post_init__(self):
151
+ self.codec = [Codec(c) for c in self.codec]
152
+ if isinstance(self.values, dict):
153
+ self.values = StreamValue(**self.values)
154
+ self.controls = [
155
+ StreamControl(**c) if isinstance(c, dict) else c
156
+ for c in self.controls
157
+ ]
158
+ self.resolutions = [list(r) for r in self.resolutions]
159
+
160
+
161
+ def _dict_factory(data):
162
+ def convert_value(obj):
163
+ if isinstance(obj, Enum):
164
+ return obj.value
165
+ elif isinstance(obj, (list, tuple)):
166
+ return [convert_value(x) for x in obj]
167
+ elif isinstance(obj, set):
168
+ return {convert_value(x) for x in obj}
169
+ return obj
170
+ return {k: convert_value(v) for k, v in data}
171
+
172
+
173
+ def stream_as_dict(stream_obj):
174
+ """Serialize stream information as dict."""
175
+ return asdict(
176
+ stream_obj,
177
+ dict_factory=_dict_factory)
@@ -0,0 +1,20 @@
1
+ """Utility functions like saving and loading YAML files."""
2
+ from pathlib import Path
3
+
4
+ import yaml
5
+
6
+ from . import stream_as_dict, StreamInfo
7
+
8
+
9
+ def load(filename: Path | str) -> list[StreamInfo]:
10
+ """Load the yaml file and return it as list of StreamInfo."""
11
+ with open(filename, 'r', encoding='utf-8') as data:
12
+ yaml_data = yaml.safe_load(data)
13
+ return [StreamInfo(**stream) for stream in yaml_data]
14
+
15
+
16
+ def save(data: list[StreamInfo], filename: Path | str) -> None:
17
+ """Save list of StreamInfo to yaml file."""
18
+ yaml_data = [stream_as_dict(s) for s in data]
19
+ with open(filename, 'w', encoding='utf-8') as data:
20
+ data.write(yaml.safe_dump(yaml_data))
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: video_stream_data
3
+ Version: 0.1.6
4
+ Summary: Unified data format for video streams (devices, server, ...)
5
+ Author-email: Andreas Bresser <Andreas.Bresser@dfki.de>
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://codeberg.org/brean/video_stream_data
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Provides-Extra: yaml
12
+ Requires-Dist: pyyaml; extra == "yaml"
13
+ Provides-Extra: test
14
+ Requires-Dist: pytest; extra == "test"
15
+ Requires-Dist: pyyaml; extra == "test"
16
+ Dynamic: license-file
17
+
18
+ # Video Stream Data
19
+ Dependency-free data structures for video streaming.
20
+
21
+ ## Installation
22
+ Use **pip** to install. Its recommended to install in a virtual environment
23
+
24
+ ```bash
25
+ python3 -m venv video-streaming-pipeline
26
+ # install using system-site-packages (like opencv and pyudev)
27
+ source video-streaming-pipeline/bin/activate --system-site-packages
28
+ pip3 install video_stream_data
29
+ ```
30
+
31
+ Although not recommended a user installation with `--break-system-packages` would also be okay as it does not have any dependencies.
32
+
33
+ ## Usage
34
+ Simply import the data classes you want to have, you can use the parser to load the video stream settings from a file (see `test/`-folder).
35
+
36
+ You can use the data structure to manually create any kind of video stream.
37
+
38
+
39
+ ```mermaid
40
+ classDiagram
41
+ direction LR
42
+
43
+ class StreamControlType {
44
+ <<enumeration>>
45
+ +INT: str = 'int'
46
+ +BOOL: str = 'bool'
47
+ +MENU: str = 'menu'
48
+ }
49
+
50
+ class Encoding {
51
+ <<enumeration>>
52
+ +PASSTHROUGH: str = 'passthrough'
53
+ +BGR: str = 'bgr'
54
+ +GREY: str = 'grey'
55
+ +GREY16: str = 'grey16'
56
+ +GRB: str = 'rbg'
57
+ +BGRA: str = 'bgra'
58
+ +RGBA: str = 'rgba'
59
+
60
+ }
61
+
62
+ class Codec {
63
+ <<enumeration>>
64
+ +MJPEG: str = 'MJPEG' # Motion-JPEG
65
+ +YUYV: str = 'YUYV' # e.g. YUYV 4:2:2
66
+ +XVID: str = 'XVID' # e.g. mp4 files
67
+ +H264: str = 'H264'
68
+ +UNKNOWN: str = 'unknown'
69
+ }
70
+
71
+ class StreamControl {
72
+ +type: StreamControlType
73
+ +name: str
74
+ +writable: bool = False
75
+ +min: float = 0
76
+ +max: float = 0
77
+ +step: int = 1
78
+ +value: float | bool | None = None
79
+ +options: list = []
80
+ }
81
+
82
+ class StreamValue {
83
+ # desired video resolution
84
+ +resolution: list[int]
85
+ +fps: float # desired FPS by user
86
+ # desired configuration
87
+ +controls: dict[str, int | float | bool | None]
88
+ +encoding: Encoding = Encoding.PASSTHROUGH
89
+ +codec: Codec = Codec.UNKNOWN
90
+ }
91
+
92
+ class StreamInfo {
93
+ +name: str
94
+ +stream_path: str
95
+ +driver: str
96
+ +codec: list[str]
97
+ +controls: list[StreamControl]
98
+ +resolutions: list[int, int]
99
+ +max_fps: float
100
+ +values: StreamValue
101
+ +backend: str = 'v4l2'
102
+ }
103
+
104
+ StreamControlType <|-- StreamControl
105
+ Encoding <|-- StreamValue
106
+ Codec <|-- StreamValue
107
+ StreamControl --> StreamInfo : controls
108
+ StreamValue --> StreamInfo : values
109
+
110
+ ```
111
+ *Fig. 1: Data Model for the StreamInfo generated from any device*
@@ -0,0 +1,7 @@
1
+ video_stream_data/__init__.py,sha256=RtVgtl5dbF-aLrOFPHOgLGZcYbVZez02R-gYjBUoWWU,4795
2
+ video_stream_data/utils.py,sha256=4jC2OxKuFFN6mbEHIUv70xBXPdS-DmLW_66KlB_e5Tk,681
3
+ video_stream_data-0.1.6.dist-info/licenses/LICENSE,sha256=9eS7gmlDqQI_plfhhfriVsVbRBzCzIjH1QH0qSyNB40,1502
4
+ video_stream_data-0.1.6.dist-info/METADATA,sha256=14AybSQ-7V9JtYicEeTd65_DTe9L-rDzstXrRqA5f2o,3097
5
+ video_stream_data-0.1.6.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
6
+ video_stream_data-0.1.6.dist-info/top_level.txt,sha256=iH0zMRQNzh7YijSv9kSwoX3kXQzj90T99BYZYnDywu4,18
7
+ video_stream_data-0.1.6.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Andreas Bresser
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ video_stream_data