video-stream-data 0.1.3__tar.gz
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.
- video_stream_data-0.1.3/LICENSE +28 -0
- video_stream_data-0.1.3/PKG-INFO +105 -0
- video_stream_data-0.1.3/README.md +93 -0
- video_stream_data-0.1.3/setup.cfg +41 -0
- video_stream_data-0.1.3/setup.py +6 -0
- video_stream_data-0.1.3/test/test_utils.py +46 -0
- video_stream_data-0.1.3/video_stream_data/__init__.py +166 -0
- video_stream_data-0.1.3/video_stream_data/utils.py +21 -0
- video_stream_data-0.1.3/video_stream_data.egg-info/PKG-INFO +105 -0
- video_stream_data-0.1.3/video_stream_data.egg-info/SOURCES.txt +11 -0
- video_stream_data-0.1.3/video_stream_data.egg-info/dependency_links.txt +1 -0
- video_stream_data-0.1.3/video_stream_data.egg-info/top_level.txt +1 -0
|
@@ -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,105 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: video_stream_data
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Unified data format for video streams (devices, server, ...)
|
|
5
|
+
Home-page: https://github.com/brean/video_stream_data
|
|
6
|
+
Author: Andreas Bresser
|
|
7
|
+
Author-email: Andreas.Bresser@dfki.de
|
|
8
|
+
License: BSD-3
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Video Stream Data
|
|
14
|
+
Dependency-free data structures for video streaming.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
Use **pip** to install. Its recommended to install in a virtual environment
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python3 -m venv video-streaming-pipeline
|
|
21
|
+
# install using system-site-packages (like opencv and pyudev)
|
|
22
|
+
source video-streaming-pipeline/bin/activate --system-site-packages
|
|
23
|
+
pip3 install video_stream_data
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Although not recommended a user installation with `--break-system-packages` would also be okay as it does not have any dependencies.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
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).
|
|
30
|
+
|
|
31
|
+
You can use the data structure to manually create any kind of video stream.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
```mermaid
|
|
35
|
+
classDiagram
|
|
36
|
+
direction LR
|
|
37
|
+
|
|
38
|
+
class StreamControlType {
|
|
39
|
+
<<enumeration>>
|
|
40
|
+
+INT: str = 'int'
|
|
41
|
+
+BOOL: str = 'bool'
|
|
42
|
+
+MENU: str = 'menu'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class Encoding {
|
|
46
|
+
<<enumeration>>
|
|
47
|
+
+PASSTHROUGH: str = 'passthrough'
|
|
48
|
+
+BGR: str = 'bgr'
|
|
49
|
+
+GREY: str = 'grey'
|
|
50
|
+
+GREY16: str = 'grey16'
|
|
51
|
+
+GRB: str = 'rbg'
|
|
52
|
+
+BGRA: str = 'bgra'
|
|
53
|
+
+RGBA: str = 'rgba'
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class Codec {
|
|
58
|
+
<<enumeration>>
|
|
59
|
+
+MJPEG: str = 'MJPEG' # Motion-JPEG
|
|
60
|
+
+YUYV: str = 'YUYV' # e.g. YUYV 4:2:2
|
|
61
|
+
+XVID: str = 'XVID' # e.g. mp4 files
|
|
62
|
+
+UNKNOWN: str = 'unknown'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class StreamControl {
|
|
66
|
+
+type: StreamControlType
|
|
67
|
+
+name: str
|
|
68
|
+
+writable: bool = False
|
|
69
|
+
+min: float = 0
|
|
70
|
+
+max: float = 0
|
|
71
|
+
+step: int = 1
|
|
72
|
+
+value: float | bool | None = None
|
|
73
|
+
+options: list = []
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class StreamValue {
|
|
77
|
+
# desired video resolution
|
|
78
|
+
+resolution: list[int]
|
|
79
|
+
+fps: float # desired FPS by user
|
|
80
|
+
# desired configuration
|
|
81
|
+
+controls: dict[str, int | float | bool | None]
|
|
82
|
+
+encoding: Encoding = Encoding.PASSTHROUGH
|
|
83
|
+
+codec: Codec = Codec.UNKNOWN
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class StreamInfo {
|
|
87
|
+
+name: str
|
|
88
|
+
+stream_path: str
|
|
89
|
+
+driver: str
|
|
90
|
+
+codec: list[str]
|
|
91
|
+
+controls: list[StreamControl]
|
|
92
|
+
+resolutions: list[int, int]
|
|
93
|
+
+max_fps: float
|
|
94
|
+
+values: StreamValue
|
|
95
|
+
+backend: str = 'v4l2'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
StreamControlType <|-- StreamControl
|
|
99
|
+
Encoding <|-- StreamValue
|
|
100
|
+
Codec <|-- StreamValue
|
|
101
|
+
StreamControl --> StreamInfo : controls
|
|
102
|
+
StreamValue --> StreamInfo : values
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
*Fig. 1: Data Model for the StreamInfo generated from any device*
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Video Stream Data
|
|
2
|
+
Dependency-free data structures for video streaming.
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
Use **pip** to install. Its recommended to install in a virtual environment
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python3 -m venv video-streaming-pipeline
|
|
9
|
+
# install using system-site-packages (like opencv and pyudev)
|
|
10
|
+
source video-streaming-pipeline/bin/activate --system-site-packages
|
|
11
|
+
pip3 install video_stream_data
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Although not recommended a user installation with `--break-system-packages` would also be okay as it does not have any dependencies.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
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).
|
|
18
|
+
|
|
19
|
+
You can use the data structure to manually create any kind of video stream.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
```mermaid
|
|
23
|
+
classDiagram
|
|
24
|
+
direction LR
|
|
25
|
+
|
|
26
|
+
class StreamControlType {
|
|
27
|
+
<<enumeration>>
|
|
28
|
+
+INT: str = 'int'
|
|
29
|
+
+BOOL: str = 'bool'
|
|
30
|
+
+MENU: str = 'menu'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class Encoding {
|
|
34
|
+
<<enumeration>>
|
|
35
|
+
+PASSTHROUGH: str = 'passthrough'
|
|
36
|
+
+BGR: str = 'bgr'
|
|
37
|
+
+GREY: str = 'grey'
|
|
38
|
+
+GREY16: str = 'grey16'
|
|
39
|
+
+GRB: str = 'rbg'
|
|
40
|
+
+BGRA: str = 'bgra'
|
|
41
|
+
+RGBA: str = 'rgba'
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class Codec {
|
|
46
|
+
<<enumeration>>
|
|
47
|
+
+MJPEG: str = 'MJPEG' # Motion-JPEG
|
|
48
|
+
+YUYV: str = 'YUYV' # e.g. YUYV 4:2:2
|
|
49
|
+
+XVID: str = 'XVID' # e.g. mp4 files
|
|
50
|
+
+UNKNOWN: str = 'unknown'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class StreamControl {
|
|
54
|
+
+type: StreamControlType
|
|
55
|
+
+name: str
|
|
56
|
+
+writable: bool = False
|
|
57
|
+
+min: float = 0
|
|
58
|
+
+max: float = 0
|
|
59
|
+
+step: int = 1
|
|
60
|
+
+value: float | bool | None = None
|
|
61
|
+
+options: list = []
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class StreamValue {
|
|
65
|
+
# desired video resolution
|
|
66
|
+
+resolution: list[int]
|
|
67
|
+
+fps: float # desired FPS by user
|
|
68
|
+
# desired configuration
|
|
69
|
+
+controls: dict[str, int | float | bool | None]
|
|
70
|
+
+encoding: Encoding = Encoding.PASSTHROUGH
|
|
71
|
+
+codec: Codec = Codec.UNKNOWN
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class StreamInfo {
|
|
75
|
+
+name: str
|
|
76
|
+
+stream_path: str
|
|
77
|
+
+driver: str
|
|
78
|
+
+codec: list[str]
|
|
79
|
+
+controls: list[StreamControl]
|
|
80
|
+
+resolutions: list[int, int]
|
|
81
|
+
+max_fps: float
|
|
82
|
+
+values: StreamValue
|
|
83
|
+
+backend: str = 'v4l2'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
StreamControlType <|-- StreamControl
|
|
87
|
+
Encoding <|-- StreamValue
|
|
88
|
+
Codec <|-- StreamValue
|
|
89
|
+
StreamControl --> StreamInfo : controls
|
|
90
|
+
StreamValue --> StreamInfo : values
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
*Fig. 1: Data Model for the StreamInfo generated from any device*
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = video_stream_data
|
|
3
|
+
version = 0.1.3
|
|
4
|
+
author = Andreas Bresser
|
|
5
|
+
author_email = Andreas.Bresser@dfki.de
|
|
6
|
+
license = BSD-3
|
|
7
|
+
description = Unified data format for video streams (devices, server, ...)
|
|
8
|
+
url = https://github.com/brean/video_stream_data
|
|
9
|
+
long_description = file: README.md
|
|
10
|
+
long_description_content_type = text/markdown
|
|
11
|
+
|
|
12
|
+
[options]
|
|
13
|
+
packages = find:
|
|
14
|
+
install_requires =
|
|
15
|
+
|
|
16
|
+
[bumpversion]
|
|
17
|
+
current_version = 0.1.3
|
|
18
|
+
commit = True
|
|
19
|
+
tag = True
|
|
20
|
+
|
|
21
|
+
[bumpversion:file:setup.py]
|
|
22
|
+
search = version='{current_version}'
|
|
23
|
+
replace = version='{new_version}'
|
|
24
|
+
|
|
25
|
+
[bumpversion:file:video_stream_data/__init__.py]
|
|
26
|
+
search = __version__ = '{current_version}'
|
|
27
|
+
replace = __version__ = '{new_version}'
|
|
28
|
+
|
|
29
|
+
[bdist_wheel]
|
|
30
|
+
universal = 1
|
|
31
|
+
|
|
32
|
+
[flake8]
|
|
33
|
+
exclude = docs
|
|
34
|
+
|
|
35
|
+
[aliases]
|
|
36
|
+
test = pytest
|
|
37
|
+
|
|
38
|
+
[egg_info]
|
|
39
|
+
tag_build =
|
|
40
|
+
tag_date = 0
|
|
41
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Test loading/saving data."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import tempfile
|
|
4
|
+
|
|
5
|
+
from video_stream_data import Codec, Encoding, StreamInfo, StreamValue
|
|
6
|
+
from video_stream_data.utils import load, save
|
|
7
|
+
|
|
8
|
+
BASE_PATH = Path(__file__).absolute().parent
|
|
9
|
+
LOCAL_VIDEO_DEVICE = BASE_PATH / 'data' / 'local_video_device.yml'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_load_stream_info():
|
|
13
|
+
"""Test loading data from yaml."""
|
|
14
|
+
stream_info = load(LOCAL_VIDEO_DEVICE)
|
|
15
|
+
assert len(stream_info) == 1
|
|
16
|
+
stream = stream_info[0]
|
|
17
|
+
assert isinstance(stream, StreamInfo)
|
|
18
|
+
assert stream.name == 'Dummy Camera'
|
|
19
|
+
assert isinstance(stream.values, StreamValue)
|
|
20
|
+
assert stream.values.codec == Codec.MJPEG
|
|
21
|
+
assert stream.values.encoding == Encoding.PASSTHROUGH
|
|
22
|
+
assert stream.values.controls['Power Line Frequency'] == 2
|
|
23
|
+
assert stream.controls
|
|
24
|
+
power_line_freq = False
|
|
25
|
+
for ctrl in stream.controls:
|
|
26
|
+
if ctrl.name == 'Power Line Frequency':
|
|
27
|
+
power_line_freq = True
|
|
28
|
+
break
|
|
29
|
+
assert power_line_freq
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_save_stream_info():
|
|
33
|
+
"""Test saving data to temp file."""
|
|
34
|
+
stream_info = load(LOCAL_VIDEO_DEVICE)
|
|
35
|
+
tmp = tempfile.NamedTemporaryFile()
|
|
36
|
+
save(stream_info, tmp.name)
|
|
37
|
+
|
|
38
|
+
# check file by string compare
|
|
39
|
+
saved_data = ''
|
|
40
|
+
with open(tmp.name, 'r') as data:
|
|
41
|
+
saved_data = data.read()
|
|
42
|
+
assert 'Dummy Camera' in saved_data
|
|
43
|
+
|
|
44
|
+
# check full structure by reloading
|
|
45
|
+
reloaded_stream_info = load(tmp.name)
|
|
46
|
+
assert reloaded_stream_info == stream_info
|
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
|
|
17
|
+
class Encoding(str, Enum):
|
|
18
|
+
"""Defines how the video stream gets encodied.
|
|
19
|
+
|
|
20
|
+
Should default to 'passthrough'.
|
|
21
|
+
Normally BGR in ROS, RGB in h264.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
PASSTHROUGH = 'passthrough'
|
|
25
|
+
BGR = 'bgr'
|
|
26
|
+
GREY = 'grey'
|
|
27
|
+
GREY16 = 'grey16'
|
|
28
|
+
RGB = 'rgb'
|
|
29
|
+
BGRA = 'bgra'
|
|
30
|
+
RGBA = 'rgba'
|
|
31
|
+
|
|
32
|
+
def to_ros_bridge(self) -> str:
|
|
33
|
+
"""Create string to use with the ROS2 CvBridge."""
|
|
34
|
+
if self.value == Encoding.BGR:
|
|
35
|
+
return 'bgr8'
|
|
36
|
+
elif self.value == Encoding.RGB:
|
|
37
|
+
return 'rgb8'
|
|
38
|
+
elif self.value == Encoding.BGR:
|
|
39
|
+
return 'bgra8'
|
|
40
|
+
elif self.value == Encoding.RGB:
|
|
41
|
+
return 'rgba8'
|
|
42
|
+
elif self.value == Encoding.GREY:
|
|
43
|
+
return 'mono8'
|
|
44
|
+
elif self.value == Encoding.GREY16:
|
|
45
|
+
return 'mono16'
|
|
46
|
+
else:
|
|
47
|
+
return str(self.value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Codec(str, Enum):
|
|
51
|
+
"""Codec type/Pixel format as fourcc."""
|
|
52
|
+
|
|
53
|
+
# see linuxpy/video/raw.py
|
|
54
|
+
|
|
55
|
+
MJPEG = 'MJPEG' # Motion-JPEG
|
|
56
|
+
YUYV = 'YUYV'
|
|
57
|
+
XVID = 'XVID' # MP4 files
|
|
58
|
+
UNKNOWN = 'unknown'
|
|
59
|
+
|
|
60
|
+
def to_text(self) -> str:
|
|
61
|
+
"""Create human readable text."""
|
|
62
|
+
if self.value == Encoding.MJPEG:
|
|
63
|
+
return 'Motion-JPEG'
|
|
64
|
+
elif self.value == Encoding.YUYV:
|
|
65
|
+
return 'YUYV'
|
|
66
|
+
elif self.value == Encoding.XVID:
|
|
67
|
+
return 'XVID/MP4'
|
|
68
|
+
|
|
69
|
+
def to_fourcc(self) -> str:
|
|
70
|
+
"""Create FourCC-Text for configuration of OpenCV2 VideoWriter."""
|
|
71
|
+
if self.value == Encoding.MJPEG:
|
|
72
|
+
return 'MJPG'
|
|
73
|
+
return str(self)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class StreamControl:
|
|
78
|
+
"""Provide a value the user can configure."""
|
|
79
|
+
|
|
80
|
+
type: StreamControlType # noqa: A003
|
|
81
|
+
name: str
|
|
82
|
+
writable: bool = False
|
|
83
|
+
min: float = 0 # noqa: A003
|
|
84
|
+
max: float = 0 # noqa: A003
|
|
85
|
+
step: int = 1
|
|
86
|
+
value: int | float | bool | None = None
|
|
87
|
+
options: list[(str, str)] = field(default_factory=list)
|
|
88
|
+
|
|
89
|
+
def __post_init__(self):
|
|
90
|
+
if self.type:
|
|
91
|
+
self.type = StreamControlType(self.type)
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class StreamValue:
|
|
95
|
+
"""Values requested by the user."""
|
|
96
|
+
|
|
97
|
+
# selected resolution
|
|
98
|
+
resolution: list[int]
|
|
99
|
+
# current fps
|
|
100
|
+
fps: float
|
|
101
|
+
# selected control configurations
|
|
102
|
+
controls: dict[str, int | float | bool | None]
|
|
103
|
+
# selected encoding, e.g. bgr
|
|
104
|
+
encoding: Encoding = Encoding.PASSTHROUGH
|
|
105
|
+
# selected codec e.g. MJPEG for "Motion-JPEG" or YUYV for "YUYV 4:2:2"
|
|
106
|
+
codec: Codec = Codec.UNKNOWN
|
|
107
|
+
|
|
108
|
+
def __post_init__(self):
|
|
109
|
+
if self.encoding:
|
|
110
|
+
self.encoding = Encoding(self.encoding)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass
|
|
114
|
+
class StreamInfo:
|
|
115
|
+
"""Stream information provided by the device/host."""
|
|
116
|
+
|
|
117
|
+
name: str
|
|
118
|
+
# unique stream path
|
|
119
|
+
stream_path: str
|
|
120
|
+
# driver for example "uvcvideo" for a local OpenCV webcam or
|
|
121
|
+
# "webrtc" for a WebRTC Video Stream
|
|
122
|
+
driver: str
|
|
123
|
+
# available codec
|
|
124
|
+
codec: list[Codec]
|
|
125
|
+
# all available controls from linuxpy
|
|
126
|
+
controls: list[StreamControl]
|
|
127
|
+
# all available resolutions, starting with the default resolution
|
|
128
|
+
resolutions: list[int, int]
|
|
129
|
+
# max. possible fps (at default resolution)
|
|
130
|
+
max_fps: float
|
|
131
|
+
# current configuration as own data structure so it can easily be handed
|
|
132
|
+
# over as dict from any service
|
|
133
|
+
values: StreamValue
|
|
134
|
+
backend: str = 'v4l2' # (optional) OpenCV2 Backend (only if cv2 is used)
|
|
135
|
+
|
|
136
|
+
def __post_init__(self):
|
|
137
|
+
if self.codec:
|
|
138
|
+
self.codec = [Codec(c) for c in self.codec]
|
|
139
|
+
if self.values and isinstance(self.values, dict):
|
|
140
|
+
self.values = StreamValue(**self.values)
|
|
141
|
+
if self.controls:
|
|
142
|
+
self.controls = [
|
|
143
|
+
StreamControl(**c) if isinstance(c, dict) else c
|
|
144
|
+
for c in self.controls
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _dict_factory(data):
|
|
149
|
+
def convert_value(obj):
|
|
150
|
+
if isinstance(obj, Enum):
|
|
151
|
+
return obj.value
|
|
152
|
+
elif isinstance(obj, list):
|
|
153
|
+
return [convert_value(x) for x in obj]
|
|
154
|
+
elif isinstance(obj, set):
|
|
155
|
+
return {convert_value(x) for x in obj}
|
|
156
|
+
elif isinstance(obj, tuple):
|
|
157
|
+
return tuple(convert_value(x) for x in obj)
|
|
158
|
+
return obj
|
|
159
|
+
return {k: convert_value(v) for k, v in data}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def stream_as_dict(stream_obj):
|
|
163
|
+
"""Serialize stream information as dict."""
|
|
164
|
+
return asdict(
|
|
165
|
+
stream_obj,
|
|
166
|
+
dict_factory=_dict_factory)
|
|
@@ -0,0 +1,21 @@
|
|
|
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) -> None | list[StreamInfo]:
|
|
10
|
+
"""Load the yaml file and return it as list of StreamInfo."""
|
|
11
|
+
with open(filename, 'r') as data:
|
|
12
|
+
yaml_data = yaml.safe_load(data)
|
|
13
|
+
return [StreamInfo(**stream) for stream in yaml_data]
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def save(data: list[StreamInfo], filename: Path | str) -> None:
|
|
18
|
+
"""Save list of StreamInfo to yaml file."""
|
|
19
|
+
yaml_data = [stream_as_dict(s) for s in data]
|
|
20
|
+
with open(filename, 'w') as data:
|
|
21
|
+
data.write(yaml.safe_dump(yaml_data))
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: video_stream_data
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Unified data format for video streams (devices, server, ...)
|
|
5
|
+
Home-page: https://github.com/brean/video_stream_data
|
|
6
|
+
Author: Andreas Bresser
|
|
7
|
+
Author-email: Andreas.Bresser@dfki.de
|
|
8
|
+
License: BSD-3
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Video Stream Data
|
|
14
|
+
Dependency-free data structures for video streaming.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
Use **pip** to install. Its recommended to install in a virtual environment
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python3 -m venv video-streaming-pipeline
|
|
21
|
+
# install using system-site-packages (like opencv and pyudev)
|
|
22
|
+
source video-streaming-pipeline/bin/activate --system-site-packages
|
|
23
|
+
pip3 install video_stream_data
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Although not recommended a user installation with `--break-system-packages` would also be okay as it does not have any dependencies.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
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).
|
|
30
|
+
|
|
31
|
+
You can use the data structure to manually create any kind of video stream.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
```mermaid
|
|
35
|
+
classDiagram
|
|
36
|
+
direction LR
|
|
37
|
+
|
|
38
|
+
class StreamControlType {
|
|
39
|
+
<<enumeration>>
|
|
40
|
+
+INT: str = 'int'
|
|
41
|
+
+BOOL: str = 'bool'
|
|
42
|
+
+MENU: str = 'menu'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class Encoding {
|
|
46
|
+
<<enumeration>>
|
|
47
|
+
+PASSTHROUGH: str = 'passthrough'
|
|
48
|
+
+BGR: str = 'bgr'
|
|
49
|
+
+GREY: str = 'grey'
|
|
50
|
+
+GREY16: str = 'grey16'
|
|
51
|
+
+GRB: str = 'rbg'
|
|
52
|
+
+BGRA: str = 'bgra'
|
|
53
|
+
+RGBA: str = 'rgba'
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class Codec {
|
|
58
|
+
<<enumeration>>
|
|
59
|
+
+MJPEG: str = 'MJPEG' # Motion-JPEG
|
|
60
|
+
+YUYV: str = 'YUYV' # e.g. YUYV 4:2:2
|
|
61
|
+
+XVID: str = 'XVID' # e.g. mp4 files
|
|
62
|
+
+UNKNOWN: str = 'unknown'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class StreamControl {
|
|
66
|
+
+type: StreamControlType
|
|
67
|
+
+name: str
|
|
68
|
+
+writable: bool = False
|
|
69
|
+
+min: float = 0
|
|
70
|
+
+max: float = 0
|
|
71
|
+
+step: int = 1
|
|
72
|
+
+value: float | bool | None = None
|
|
73
|
+
+options: list = []
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
class StreamValue {
|
|
77
|
+
# desired video resolution
|
|
78
|
+
+resolution: list[int]
|
|
79
|
+
+fps: float # desired FPS by user
|
|
80
|
+
# desired configuration
|
|
81
|
+
+controls: dict[str, int | float | bool | None]
|
|
82
|
+
+encoding: Encoding = Encoding.PASSTHROUGH
|
|
83
|
+
+codec: Codec = Codec.UNKNOWN
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class StreamInfo {
|
|
87
|
+
+name: str
|
|
88
|
+
+stream_path: str
|
|
89
|
+
+driver: str
|
|
90
|
+
+codec: list[str]
|
|
91
|
+
+controls: list[StreamControl]
|
|
92
|
+
+resolutions: list[int, int]
|
|
93
|
+
+max_fps: float
|
|
94
|
+
+values: StreamValue
|
|
95
|
+
+backend: str = 'v4l2'
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
StreamControlType <|-- StreamControl
|
|
99
|
+
Encoding <|-- StreamValue
|
|
100
|
+
Codec <|-- StreamValue
|
|
101
|
+
StreamControl --> StreamInfo : controls
|
|
102
|
+
StreamValue --> StreamInfo : values
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
*Fig. 1: Data Model for the StreamInfo generated from any device*
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.cfg
|
|
4
|
+
setup.py
|
|
5
|
+
test/test_utils.py
|
|
6
|
+
video_stream_data/__init__.py
|
|
7
|
+
video_stream_data/utils.py
|
|
8
|
+
video_stream_data.egg-info/PKG-INFO
|
|
9
|
+
video_stream_data.egg-info/SOURCES.txt
|
|
10
|
+
video_stream_data.egg-info/dependency_links.txt
|
|
11
|
+
video_stream_data.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
video_stream_data
|