mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-28 04:36:33 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2077d94edc | ||
|
|
defe5eb6fe | ||
|
|
dee5e18134 | ||
|
|
6ef5a7e440 | ||
|
|
462735147d | ||
|
|
51ca0e4474 | ||
|
|
4ee3b18533 | ||
|
|
cdc9ed54c9 | ||
|
|
d9e7c07851 | ||
|
|
f2debc5d16 | ||
|
|
b3dbaf40cf |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 0.65
|
current_version = 0.68
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
@@ -14,3 +14,7 @@ replace = VERSION "{new_version}"
|
|||||||
search = pkgver={current_version}
|
search = pkgver={current_version}
|
||||||
replace = pkgver={new_version}
|
replace = pkgver={new_version}
|
||||||
|
|
||||||
|
[bumpversion:file:pkg/openwrt/Makefile]
|
||||||
|
search = PKG_VERSION:={current_version}
|
||||||
|
replace = PKG_VERSION:={new_version}
|
||||||
|
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -50,7 +50,7 @@ release:
|
|||||||
|
|
||||||
|
|
||||||
bump:
|
bump:
|
||||||
bumpversion minor
|
bumpversion $(if $(V), $(V), minor)
|
||||||
|
|
||||||
|
|
||||||
push:
|
push:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
| Multithreaded JPEG encoding |  Yes |  No |
|
| Multithreaded JPEG encoding |  Yes |  No |
|
||||||
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi |  Yes |  No |
|
| [OpenMAX IL](https://www.khronos.org/openmaxil) hardware acceleration<br>on Raspberry Pi |  Yes |  No |
|
||||||
| Behavior when the device<br>is disconnected while streaming |  Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected |  Stops the broadcast <sup>1</sup> |
|
| Behavior when the device<br>is disconnected while streaming |  Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected |  Stops the broadcast <sup>1</sup> |
|
||||||
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal |  Yes |  Partially yes <sup>2</sup> |
|
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal |  Yes |  Partially yes <sup>1</sup> |
|
||||||
| Option to skip frames when streaming<br>static images by HTTP to save traffic |  Yes <sup>2</sup> |  No |
|
| Option to skip frames when streaming<br>static images by HTTP to save traffic |  Yes <sup>2</sup> |  No |
|
||||||
| Streaming via UNIX domain socket |  Yes |  No |
|
| Streaming via UNIX domain socket |  Yes |  No |
|
||||||
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters |  Yes |  No |
|
| Debug logs without recompiling,<br>performance statistics log,<br>access to HTTP broadcast parameters |  Yes |  No |
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
|----------|---------------|-------------------|
|
|----------|---------------|-------------------|
|
||||||
| Многопоточное кодирование JPEG |  Есть |  Нет |
|
| Многопоточное кодирование JPEG |  Есть |  Нет |
|
||||||
| Аппаратное кодирование с помощью [OpenMAX IL](https://www.khronos.org/openmaxil) на Raspberry Pi |  Есть |  Нет |
|
| Аппаратное кодирование с помощью [OpenMAX IL](https://www.khronos.org/openmaxil) на Raspberry Pi |  Есть |  Нет |
|
||||||
| Поведение при физическом отключении устройства<br>от сервера во время работы |  Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова |  Прерывает трансляцию <sup>1</sup> |
|
| Поведение при физическом отключении<br>устройства от сервера во время работы |  Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова |  Прерывает трансляцию <sup>1</sup> |
|
||||||
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) - возможности изменения <br>параметров разрешения трансляции на лету<br>по сигналу источника (устройства видеозахвата) |  Есть |  Условно есть <sup>2</sup> |
|
| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/dv-timings.html) - возможности<br>изменения параметров разрешения<br>трансляции на лету по сигналу<br>источника (устройства видеозахвата) |  Есть |  Условно есть <sup>1</sup> |
|
||||||
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика |  Есть <sup>2</sup> |  Нет |
|
| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика |  Есть <sup>2</sup> |  Нет |
|
||||||
| Стрим через UNIX domain socket |  Есть |  Нет |
|
| Стрим через UNIX domain socket |  Есть |  Нет |
|
||||||
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP |  Есть |  Нет |
|
| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP |  Есть |  Нет |
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=0.65
|
pkgver=0.68
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
|
||||||
url="https://github.com/pi-kvm/ustreamer"
|
url="https://github.com/pi-kvm/ustreamer"
|
||||||
|
|||||||
45
pkg/openwrt/Makefile
Normal file
45
pkg/openwrt/Makefile
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#
|
||||||
|
# This is free software, licensed under the GNU General Public License v2.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=ustreamer
|
||||||
|
PKG_VERSION:=0.68
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_URL:=https://github.com/pi-kvm/ustreamer.git
|
||||||
|
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
|
||||||
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
|
||||||
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
|
|
||||||
|
PKG_LICENSE:=GPL-3.0
|
||||||
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/ustreamer
|
||||||
|
SECTION:=multimedia
|
||||||
|
CATEGORY:=Multimedia
|
||||||
|
TITLE:=uStreamer
|
||||||
|
DEPENDS:=+libpthread +libjpeg +libv4l +libuuid +libevent2 +libevent2-core +libevent2-extra +libevent2-pthreads
|
||||||
|
URL:=https://github.com/pi-kvm/ustreamer
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/ustreamer/description
|
||||||
|
µStreamer - Lightweight and fast MJPG-HTTP streamer
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/ustreamer/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ustreamer $(1)/usr/bin/
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(CP) ./files/ustreamer.config $(1)/etc/config/ustreamer
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/ustreamer.init $(1)/etc/init.d/ustreamer
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,ustreamer))
|
||||||
20
pkg/openwrt/files/ustreamer.config
Normal file
20
pkg/openwrt/files/ustreamer.config
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
config ustreamer
|
||||||
|
option enabled '0'
|
||||||
|
|
||||||
|
option device '/dev/video0'
|
||||||
|
option device_timeout '5'
|
||||||
|
option input '0'
|
||||||
|
|
||||||
|
option width '640'
|
||||||
|
option height '480'
|
||||||
|
option format 'YUYV'
|
||||||
|
option quality '80'
|
||||||
|
option desired_fps '0'
|
||||||
|
option encoder 'CPU'
|
||||||
|
|
||||||
|
option host '::'
|
||||||
|
option port '8080'
|
||||||
|
option static ''
|
||||||
|
option user ''
|
||||||
|
option password ''
|
||||||
55
pkg/openwrt/files/ustreamer.init
Normal file
55
pkg/openwrt/files/ustreamer.init
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# Copyright (C) 2009-2019 OpenWrt.org
|
||||||
|
|
||||||
|
START=90
|
||||||
|
STOP=10
|
||||||
|
|
||||||
|
USE_PROCD=1
|
||||||
|
PROG=/usr/bin/ustreamer
|
||||||
|
|
||||||
|
getcfg() {
|
||||||
|
config_get value ustreamer $1 $2
|
||||||
|
return "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_instance() {
|
||||||
|
config_get_bool enabled ustreamer enabled 0
|
||||||
|
[ "$enabled" -eq 0 ] && return
|
||||||
|
|
||||||
|
local options=""
|
||||||
|
|
||||||
|
options="$options --device='`getcfg device /dev/video0`'"
|
||||||
|
options="$options --device-timeout='`getcfg device_timeout 5`'"
|
||||||
|
options="$options --input='`getcfg input 0`'"
|
||||||
|
|
||||||
|
options="$options --width='`getcfg width 640`'"
|
||||||
|
options="$options --height='`getcfg height 480`'"
|
||||||
|
options="$options --format='`getcfg format YUYV`'"
|
||||||
|
options="$options --quality='`getcfg quality 80`'"
|
||||||
|
options="$options --desired-fps='`getcfg desired_fps 0`'"
|
||||||
|
options="$options --encoder='`getcfg encoder CPU`'"
|
||||||
|
|
||||||
|
options="$options --host='`getcfg host '::'`'"
|
||||||
|
local port=`getcfg port 8080`
|
||||||
|
options="$options --port='$port'"
|
||||||
|
options="$options --static='`getcfg static ''`'"
|
||||||
|
options="$options --user='`getcfg user ''`'"
|
||||||
|
options="$options --passwd='`getcfg password ''`'"
|
||||||
|
|
||||||
|
config-get-bool opt_slowdown ustreamer slowdown 1
|
||||||
|
[ "$slowdown" -eq 1 ] && options="$options --slowdown"
|
||||||
|
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param command "$PROG" $options
|
||||||
|
procd_add_mdns http tcp "$port" daemon=ustreamer
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
config_load ustreamer
|
||||||
|
config_foreach start_instance ustreamer
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers() {
|
||||||
|
procd_add_reload_trigger ustreamer
|
||||||
|
}
|
||||||
@@ -22,4 +22,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION "0.65"
|
#define VERSION "0.68"
|
||||||
|
|||||||
@@ -376,12 +376,12 @@ static int _stream_init_loop(struct stream_t *stream, struct workers_pool_t *poo
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int _stream_init(struct stream_t *stream, struct workers_pool_t *pool) {
|
static int _stream_init(struct stream_t *stream, struct workers_pool_t *pool) {
|
||||||
SEP_INFO('=');
|
|
||||||
|
|
||||||
_stream_destroy_workers(stream, pool);
|
_stream_destroy_workers(stream, pool);
|
||||||
device_switch_capturing(stream->dev, false);
|
device_switch_capturing(stream->dev, false);
|
||||||
device_close(stream->dev);
|
device_close(stream->dev);
|
||||||
|
|
||||||
|
SEP_INFO('=');
|
||||||
|
|
||||||
if (device_open(stream->dev) < 0) {
|
if (device_open(stream->dev) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|||||||
53
tools/common.py
Normal file
53
tools/common.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#============================================================================#
|
||||||
|
# #
|
||||||
|
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This program is free software: you can redistribute it and/or modify #
|
||||||
|
# it under the terms of the GNU General Public License as published by #
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
#============================================================================#
|
||||||
|
|
||||||
|
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
|
||||||
|
# =====
|
||||||
|
def get_prepend() -> str:
|
||||||
|
return textwrap.dedent("""
|
||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# This program is free software: you can redistribute it and/or modify #
|
||||||
|
# it under the terms of the GNU General Public License as published by #
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, #
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||||
|
# GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License #
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
""").strip() + "\n"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env -S python3 -B
|
||||||
#============================================================================#
|
#============================================================================#
|
||||||
# #
|
# #
|
||||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||||
@@ -22,12 +22,13 @@
|
|||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
|
import common
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
def main():
|
def main() -> None:
|
||||||
assert len(sys.argv) == 4, "%s <file.html> <file.h> <name>" % (sys.argv[0])
|
assert len(sys.argv) == 4, "%s <file.html> <file.h> <name>" % (sys.argv[0])
|
||||||
html_path = sys.argv[1]
|
html_path = sys.argv[1]
|
||||||
header_path = sys.argv[2]
|
header_path = sys.argv[2]
|
||||||
@@ -36,9 +37,6 @@ def main():
|
|||||||
with open(html_path, "r") as html_file:
|
with open(html_path, "r") as html_file:
|
||||||
text = html_file.read()
|
text = html_file.read()
|
||||||
|
|
||||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "prepend.h")) as prepend_file:
|
|
||||||
prepend = prepend_file.read()
|
|
||||||
|
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
text = text.replace("\"", "\\\"")
|
text = text.replace("\"", "\\\"")
|
||||||
text = text.replace("%VERSION%", "\" VERSION \"")
|
text = text.replace("%VERSION%", "\" VERSION \"")
|
||||||
@@ -48,7 +46,7 @@ def main():
|
|||||||
for line in text.split("\n")
|
for line in text.split("\n")
|
||||||
)
|
)
|
||||||
text = "const char HTML_%s_PAGE[] = \" \\\n%s\n\";\n" % (name, text)
|
text = "const char HTML_%s_PAGE[] = \" \\\n%s\n\";\n" % (name, text)
|
||||||
text = prepend + "\n#include \"../../config.h\"\n\n\n" + text
|
text = common.get_prepend() + "\n#include \"../../config.h\"\n\n\n" + text
|
||||||
|
|
||||||
with open(header_path, "w") as header_file:
|
with open(header_path, "w") as header_file:
|
||||||
header_file.write(text)
|
header_file.write(text)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env -S python3 -B
|
||||||
#============================================================================#
|
#============================================================================#
|
||||||
# #
|
# #
|
||||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||||
@@ -22,13 +22,17 @@
|
|||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
import io
|
import io
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
from typing import Tuple
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import common
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
def _get_jpeg_size(data):
|
def _get_jpeg_size(data: bytes) -> Tuple[int, int]:
|
||||||
# https://sheep.horse/2013/9/finding_the_dimensions_of_a_jpeg_file_in_python.html
|
# https://sheep.horse/2013/9/finding_the_dimensions_of_a_jpeg_file_in_python.html
|
||||||
|
|
||||||
stream = io.BytesIO(data)
|
stream = io.BytesIO(data)
|
||||||
@@ -55,7 +59,7 @@ def _get_jpeg_size(data):
|
|||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
def main():
|
def main() -> None:
|
||||||
assert len(sys.argv) == 4, "%s <file.jpeg> <file.h> <name>" % (sys.argv[0])
|
assert len(sys.argv) == 4, "%s <file.jpeg> <file.h> <name>" % (sys.argv[0])
|
||||||
jpeg_path = sys.argv[1]
|
jpeg_path = sys.argv[1]
|
||||||
header_path = sys.argv[2]
|
header_path = sys.argv[2]
|
||||||
@@ -66,10 +70,7 @@ def main():
|
|||||||
|
|
||||||
(width, height) = _get_jpeg_size(jpeg_data)
|
(width, height) = _get_jpeg_size(jpeg_data)
|
||||||
|
|
||||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "prepend.h")) as prepend_file:
|
rows: List[List[str]] = [[]]
|
||||||
prepend = prepend_file.read()
|
|
||||||
|
|
||||||
rows = [[]]
|
|
||||||
for ch in jpeg_data:
|
for ch in jpeg_data:
|
||||||
if len(rows[-1]) > 20:
|
if len(rows[-1]) > 20:
|
||||||
rows.append([])
|
rows.append([])
|
||||||
@@ -79,7 +80,7 @@ def main():
|
|||||||
text = "const unsigned char %s_JPEG_DATA[] = {\n\t%s\n};\n" % (name, text)
|
text = "const unsigned char %s_JPEG_DATA[] = {\n\t%s\n};\n" % (name, text)
|
||||||
text = "const unsigned %s_JPEG_HEIGHT = %d;\n\n" % (name, height) + text
|
text = "const unsigned %s_JPEG_HEIGHT = %d;\n\n" % (name, height) + text
|
||||||
text = "const unsigned %s_JPEG_WIDTH = %d;\n" % (name, width) + text
|
text = "const unsigned %s_JPEG_WIDTH = %d;\n" % (name, width) + text
|
||||||
text = prepend + "\n\n" + text
|
text = common.get_prepend() + "\n\n" + text
|
||||||
|
|
||||||
with open(header_path, "w") as header_file:
|
with open(header_path, "w") as header_file:
|
||||||
header_file.write(text)
|
header_file.write(text)
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
# #
|
|
||||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
|
||||||
# #
|
|
||||||
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
|
|
||||||
# #
|
|
||||||
# This program is free software: you can redistribute it and/or modify #
|
|
||||||
# it under the terms of the GNU General Public License as published by #
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or #
|
|
||||||
# (at your option) any later version. #
|
|
||||||
# #
|
|
||||||
# This program is distributed in the hope that it will be useful, #
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
||||||
# GNU General Public License for more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License #
|
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
|
||||||
# #
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
Reference in New Issue
Block a user